Split GTP-C and GTP-U functionality into seperate classes in the SPGW. Added GTP-U specific logging in the EPC. Added handle_s11_pdu function to both the MME GTP-C entity and the SPGW GTP-C entity.

master
Pedro Alvarez 6 years ago committed by Andre Puschmann
parent 3405a1bff6
commit 5f7b6ba79e

@ -142,6 +142,9 @@ const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_STOP_RESPONSE = 236;
// Other
// 240 - 255 For future use
// Helper function to pretty print GTP-C message type
const char* gtpc_msg_type_to_str(uint8_t type);
/****************************************************************************
*
* GTP-C v2 Create Session Request

@ -20,7 +20,9 @@
*/
#ifndef SRSLTE_EPC_INTERFACES_H
#define SRSLTE_EPC_INTERFACES_H
#include "srslte/asn1/gtpc_ies.h"
#include "srslte/common/common.h"
#include <netinet/sctp.h>
namespace srsepc {
@ -72,5 +74,17 @@ public:
virtual bool resync_sqn(uint64_t imsi, uint8_t* auts) = 0;
};
/*******************
* SPGW Interfaces *
*******************/
class gtpu_interface_gtpc //GTP-C -> GTP-U
{
public:
virtual in_addr_t get_s1u_addr() = 0;
virtual bool modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtpc_f_teid_ie dw_user_fteid, uint32_t up_ctrl_teid) = 0;
virtual bool delete_gtpu_tunnel(in_addr_t ue_ipv4) = 0;
};
} // namespace srsepc
#endif // SRSLTE_EPC_INTERFACES_H

@ -28,6 +28,169 @@
namespace srslte {
const char* gtpc_msg_type_to_str(uint8_t type)
{
switch (type) {
case GTPC_MSG_TYPE_RESERVED:
return "GTPC_MSG_TYPE_RESERVED";
case GTPC_MSG_TYPE_ECHO_REQUEST:
return "GTPC_MSG_TYPE_ECHO_REQUEST";
case GTPC_MSG_TYPE_ECHO_RESPONSE:
return "GTPC_MSG_TYPE_ECHO_RESPONSE";
case GTPC_MSG_TYPE_VERSION_SUPPORT:
return "GTPC_MSG_TYPE_VERSION_SUPPORT";
case GTPC_MSG_TYPE_CREATE_SESSION_REQUEST:
return "GTPC_MSG_TYPE_CREATE_SESSION_REQUEST";
case GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE:
return "GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE";
case GTPC_MSG_TYPE_DELETE_SESSION_REQUEST:
return "GTPC_MSG_TYPE_DELETE_SESSION_REQUEST";
case GTPC_MSG_TYPE_DELETE_SESSION_RESPONSE:
return "GTPC_MSG_TYPE_DELETE_SESSION_RESPONSE";
case GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST:
return "GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST";
case GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE:
return "GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE";
case GTPC_MSG_TYPE_CHANGE_NOTIFICATION_REQUEST:
return "GTPC_MSG_TYPE_CHANGE_NOTIFICATION_REQUEST";
case GTPC_MSG_TYPE_CHANGE_NOTIFICATION_RESPONSE:
return "GTPC_MSG_TYPE_CHANGE_NOTIFICATION_RESPONSE";
case GTPC_MSG_TYPE_RESUME_NOTIFICATION:
return "GTPC_MSG_TYPE_RESUME_NOTIFICATION";
case GTPC_MSG_TYPE_RESUME_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_RESUME_ACKNOWLEDGE";
case GTPC_MSG_TYPE_MODIFY_BEARER_COMMAND:
return "GTPC_MSG_TYPE_MODIFY_BEARER_COMMAND";
case GTPC_MSG_TYPE_MODIFY_BEARER_FAILURE_INDICATION:
return "GTPC_MSG_TYPE_MODIFY_BEARER_FAILURE_INDICATION";
case GTPC_MSG_TYPE_DELETE_BEARER_COMMAND:
return "GTPC_MSG_TYPE_DELETE_BEARER_COMMAND";
case GTPC_MSG_TYPE_DELETE_BEARER_FAILURE_INDICATION:
return "GTPC_MSG_TYPE_DELETE_BEARER_FAILURE_INDICATION";
case GTPC_MSG_TYPE_BEARER_RESOURCE_COMMAND:
return "GTPC_MSG_TYPE_BEARER_RESOURCE_COMMAND";
case GTPC_MSG_TYPE_BEARER_RESOURCE_FAILURE_INDICATION:
return "GTPC_MSG_TYPE_BEARER_RESOURCE_FAILURE_INDICATION";
case GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_FAILURE_INDICATION:
return "GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_FAILURE_INDICATION";
case GTPC_MSG_TYPE_TRACE_SESSION_ACTIVATION:
return "GTPC_MSG_TYPE_TRACE_SESSION_ACTIVATION";
case GTPC_MSG_TYPE_TRACE_SESSION_DEACTIVATION:
return "GTPC_MSG_TYPE_TRACE_SESSION_DEACTIVATION";
case GTPC_MSG_TYPE_STOP_PAGING_INDICATION:
return "GTPC_MSG_TYPE_STOP_PAGING_INDICATION";
case GTPC_MSG_TYPE_CREATE_BEARER_REQUEST:
return "GTPC_MSG_TYPE_CREATE_BEARER_REQUEST";
case GTPC_MSG_TYPE_CREATE_BEARER_RESPONSE:
return "GTPC_MSG_TYPE_CREATE_BEARER_RESPONSE";
case GTPC_MSG_TYPE_UPDATE_BEARER_REQUEST:
return "GTPC_MSG_TYPE_UPDATE_BEARER_REQUEST";
case GTPC_MSG_TYPE_UPDATE_BEARER_RESPONSE:
return "GTPC_MSG_TYPE_UPDATE_BEARER_RESPONSE";
case GTPC_MSG_TYPE_DELETE_BEARER_REQUEST:
return "GTPC_MSG_TYPE_DELETE_BEARER_REQUEST";
case GTPC_MSG_TYPE_DELETE_BEARER_RESPONSE:
return "GTPC_MSG_TYPE_DELETE_BEARER_RESPONSE";
case GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_REQUEST:
return "GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_REQUEST";
case GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_RESPONSE:
return "GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_RESPONSE";
case GTPC_MSG_TYPE_IDENTIFICATION_REQUEST:
return "GTPC_MSG_TYPE_IDENTIFICATION_REQUEST";
case GTPC_MSG_TYPE_IDENTIFICATION_RESPONSE:
return "GTPC_MSG_TYPE_IDENTIFICATION_RESPONSE";
case GTPC_MSG_TYPE_CONTEXT_REQUEST:
return "GTPC_MSG_TYPE_CONTEXT_REQUEST";
case GTPC_MSG_TYPE_CONTEXT_RESPONSE:
return "GTPC_MSG_TYPE_CONTEXT_RESPONSE";
case GTPC_MSG_TYPE_CONTEXT_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_CONTEXT_ACKNOWLEDGE";
case GTPC_MSG_TYPE_FORWARD_RELOCATION_REQUEST:
return "GTPC_MSG_TYPE_FORWARD_RELOCATION_REQUEST";
case GTPC_MSG_TYPE_FORWARD_RELOCATION_RESPONSE:
return "GTPC_MSG_TYPE_FORWARD_RELOCATION_RESPONSE";
case GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_NOTIFICATION:
return "GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_NOTIFICATION";
case GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_ACKNOWLEDGE";
case GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_NOTIFICATION:
return "GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_NOTIFICATION";
case GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_ACKNOWLEDGE";
case GTPC_MSG_TYPE_RELOCATION_CANCEL_REQUEST:
return "GTPC_MSG_TYPE_RELOCATION_CANCEL_REQUEST";
case GTPC_MSG_TYPE_RELOCATION_CANCEL_RESPONSE:
return "GTPC_MSG_TYPE_RELOCATION_CANCEL_RESPONSE";
case GTPC_MSG_TYPE_CONFIGURATION_TRANSFER_TUNNEL:
return "GTPC_MSG_TYPE_CONFIGURATION_TRANSFER_TUNNEL";
case GTPC_MSG_TYPE_RAN_INFORMATION_RELAY:
return "GTPC_MSG_TYPE_RAN_INFORMATION_RELAY";
case GTPC_MSG_TYPE_DETACH_NOTIFICATION:
return "GTPC_MSG_TYPE_DETACH_NOTIFICATION";
case GTPC_MSG_TYPE_DETACH_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_DETACH_ACKNOWLEDGE";
case GTPC_MSG_TYPE_CS_PAGING_INDICATION:
return "GTPC_MSG_TYPE_CS_PAGING_INDICATION";
case GTPC_MSG_TYPE_ALERT_MME_NOTIFICATION:
return "GTPC_MSG_TYPE_ALERT_MME_NOTIFICATION";
case GTPC_MSG_TYPE_ALERT_MME_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_ALERT_MME_ACKNOWLEDGE";
case GTPC_MSG_TYPE_UE_ACTIVITY_NOTIFICATION:
return "GTPC_MSG_TYPE_UE_ACTIVITY_NOTIFICATION";
case GTPC_MSG_TYPE_UE_ACTIVITY_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_UE_ACTIVITY_ACKNOWLEDGE";
case GTPC_MSG_TYPE_SUSPEND_NOTIFICATION:
return "GTPC_MSG_TYPE_SUSPEND_NOTIFICATION";
case GTPC_MSG_TYPE_SUSPEND_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_SUSPEND_ACKNOWLEDGE";
case GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_REQUEST:
return "GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_REQUEST";
case GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_RESPONSE:
return "GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_RESPONSE";
case GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST:
return "GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST";
case GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE:
return "GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE";
case GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST:
return "GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST";
case GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE:
return "GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE";
case GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST:
return "GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST";
case GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_RESPONSE:
return "GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_RESPONSE";
case GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION:
return "GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION";
case GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE";
case GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION:
return "GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION";
case GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION_ACKNOWLEDGE:
return "GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION_ACKNOWLEDGE";
case GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_REQUEST:
return "GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_REQUEST";
case GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_RESPONSE:
return "GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_RESPONSE";
case GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_REQUEST:
return "GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_REQUEST";
case GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_RESPONSE:
return "GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_RESPONSE";
case GTPC_MSG_TYPE_MBMS_SESSION_START_REQUEST:
return "GTPC_MSG_TYPE_MBMS_SESSION_START_REQUEST";
case GTPC_MSG_TYPE_MBMS_SESSION_START_RESPONSE:
return "GTPC_MSG_TYPE_MBMS_SESSION_START_RESPONSE";
case GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_REQUEST:
return "GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_REQUEST";
case GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_RESPONSE:
return "GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_RESPONSE";
case GTPC_MSG_TYPE_MBMS_SESSION_STOP_REQUEST:
return "GTPC_MSG_TYPE_MBMS_SESSION_STOP_REQUEST";
case GTPC_MSG_TYPE_MBMS_SESSION_STOP_RESPONSE:
return "GTPC_MSG_TYPE_MBMS_SESSION_STOP_RESPONSE";
}
return "GTPC_MSG_TYPE_INVALID";
}
int gtpc_pack_create_session_request(struct gtpc_create_session_request* cs_req, srslte::byte_buffer_t)
{
// TODO

@ -85,18 +85,21 @@ filename = /tmp/epc.pcap
# configured.
# Format: e.g. s1ap_hex_limit = 32
#
# Logging layers: s1ap, gtpc, spgw, hss, all
# Logging layers: nas, s1ap, mme_gtpc, spgw_gtpc, gtpu, spgw, hss, all
# Logging levels: debug, info, warning, error, none
#
# filename: File path to use for log output. Can be set to stdout
# to print logs to standard output
#####################################################################
[log]
all_level = debug
all_level = info
all_hex_limit = 32
filename = /tmp/epc.log
#nas_level = debug
#s1ap_level = debug
#gtpc_level = debug
#mme_gtpc_level = debug
#spgw_gtpc_level = debug
#gtpu_level = debug
#spgw_level = debug
#hss_level = debug

@ -48,6 +48,7 @@ public:
static void cleanup(void);
bool init(srslte::log_filter* mme_gtpc_log);
void handle_s11_pdu(srslte::gtpc_pdu* msg);
uint32_t get_new_ctrl_teid();
virtual bool send_create_session_request(uint64_t imsi);
@ -74,5 +75,10 @@ private:
std::map<uint64_t, struct gtpc_ctx> m_imsi_to_gtpc_ctx;
};
inline uint32_t mme_gtpc::get_new_ctrl_teid()
{
return m_next_ctrl_teid++;
}
} // namespace srsepc
#endif // SRSEPC_MME_GTPC_H

@ -0,0 +1,103 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2017 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSEPC_GTPC_H
#define SRSEPC_GTPC_H
#include "srsepc/hdr/spgw/spgw.h"
#include "srslte/asn1/gtpc.h"
#include "srslte/interfaces/epc_interfaces.h"
namespace srsepc {
class spgw::gtpc
{
public:
gtpc();
virtual ~gtpc();
int init(spgw_args_t* args, spgw* spgw, gtpu_interface_gtpc* gtpu, srslte::log_filter* gtpc_log);
void stop();
srslte::error_t init_ue_ip(spgw_args_t* args);
uint64_t get_new_ctrl_teid();
uint64_t get_new_user_teid();
in_addr_t get_new_ue_ipv4();
void handle_s11_pdu(srslte::gtpc_pdu* msg, srslte::gtpc_pdu* reply_msg);
void handle_create_session_request(const srslte::gtpc_create_session_request& cs_req, srslte::gtpc_pdu* gtpc_pdu);
void handle_modify_bearer_request(const srslte::gtpc_header& mb_req_hdr,
const srslte::gtpc_modify_bearer_request& mb_req,
srslte::gtpc_pdu* mb_resp_pdu);
void handle_delete_session_request(const srslte::gtpc_header& header,
const srslte::gtpc_delete_session_request& del_req,
srslte::gtpc_pdu* del_resp_pdu);
void handle_release_access_bearers_request(const srslte::gtpc_header& header,
const srslte::gtpc_release_access_bearers_request& rel_req,
srslte::gtpc_pdu* rel_resp_pdu);
spgw_tunnel_ctx_t* create_gtpc_ctx(const srslte::gtpc_create_session_request& cs_req);
bool delete_gtpc_ctx(uint32_t ctrl_teid);
spgw* m_spgw;
gtpu_interface_gtpc* m_gtpu;
uint32_t m_h_next_ue_ip;
uint64_t m_next_ctrl_teid;
uint64_t m_next_user_teid;
std::map<uint64_t, uint32_t> m_imsi_to_ctr_teid; // IMSI to control TEID map. Important to check if UE
// is previously connected
std::map<uint32_t, spgw_tunnel_ctx*> m_teid_to_tunnel_ctx; // Map control TEID to tunnel ctx. Usefull to get
// reply ctrl TEID, UE IP, etc.
srslte::log_filter* m_gtpc_log;
srslte::byte_buffer_pool* m_pool;
};
inline uint64_t spgw::gtpc::get_new_ctrl_teid()
{
return m_next_ctrl_teid++;
}
inline uint64_t spgw::gtpc::get_new_user_teid()
{
return m_next_user_teid++;
}
inline in_addr_t spgw::gtpc::get_new_ue_ipv4()
{
m_h_next_ue_ip++;
return ntohl(m_h_next_ue_ip);
}
inline srslte::error_t spgw::gtpc::init_ue_ip(spgw_args_t* args)
{
m_h_next_ue_ip = ntohl(inet_addr(args->sgi_if_addr.c_str()));
return srslte::ERROR_NONE;
}
} // namespace srsepc
#endif // SRSEPC_SPGW_H

@ -0,0 +1,105 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2017 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSEPC_GTPU_H
#define SRSEPC_GTPU_H
#include "srsepc/hdr/spgw/spgw.h"
#include "srslte/asn1/gtpc.h"
#include "srslte/common/buffer_pool.h"
#include "srslte/interfaces/epc_interfaces.h"
#include <cstddef>
namespace srsepc {
class spgw::gtpu : public gtpu_interface_gtpc {
public:
gtpu();
virtual ~gtpu();
int init(spgw_args_t *args, spgw *spgw, srslte::log_filter *gtpu_log);
void stop();
srslte::error_t init_sgi(spgw_args_t *args);
srslte::error_t init_s1u(spgw_args_t *args);
int get_sgi();
int get_s1u();
void handle_sgi_pdu(srslte::byte_buffer_t *msg);
void handle_s1u_pdu(srslte::byte_buffer_t *msg);
void send_s1u_pdu(srslte::gtp_fteid_t enb_fteid, srslte::byte_buffer_t *msg);
virtual in_addr_t get_s1u_addr();
virtual bool modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtp_fteid_t dw_user_fteid, uint32_t up_ctr_fteid);
virtual bool delete_gtpu_tunnel(in_addr_t ue_ipv4);
std::string gtpu_ntoa(uint32_t addr);
spgw *m_spgw;
bool m_sgi_up;
int m_sgi;
bool m_s1u_up;
int m_s1u;
sockaddr_in m_s1u_addr;
std::map<in_addr_t, srslte::gtpc_f_teid_ie> m_ip_to_usr_teid; // Map IP to User-plane TEID for downlink traffic
srslte::log_filter *m_gtpu_log;
private:
srslte::byte_buffer_pool *m_pool;
};
inline int spgw::gtpu::get_sgi(){
return m_sgi;
}
inline int spgw::gtpu::get_s1u(){
return m_s1u;
}
inline in_addr_t spgw::gtpu::get_s1u_addr()
{
return m_s1u_addr.sin_addr.s_addr;
}
// Helper function to return a string from IPv4 address for easy printing
inline std::string spgw::gtpu::gtpu_ntoa(uint32_t addr){
char tmp_str[INET_ADDRSTRLEN+1];
bzero(tmp_str, sizeof(tmp_str));
struct in_addr tmp_addr;
tmp_addr.s_addr = addr;
const char* tmp_ptr = inet_ntop(AF_INET, &tmp_addr, tmp_str, INET_ADDRSTRLEN);
if(tmp_ptr == NULL){
return std::string("Invalid IPv4 address");
}
return std::string(tmp_str);
}
}// namespace srsepc
#endif // SRSEPC_GTPU_H

@ -33,13 +33,13 @@
#ifndef SRSEPC_SPGW_H
#define SRSEPC_SPGW_H
#include <cstddef>
#include "srslte/asn1/gtpc.h"
#include "srslte/common/buffer_pool.h"
#include "srslte/common/log.h"
#include "srslte/common/logger_file.h"
#include "srslte/common/log_filter.h"
#include "srslte/common/buffer_pool.h"
#include "srslte/common/logger_file.h"
#include "srslte/common/threads.h"
#include "srslte/asn1/gtpc.h"
#include <cstddef>
namespace srsepc {
@ -54,9 +54,9 @@ typedef struct {
} spgw_args_t;
typedef struct spgw_tunnel_ctx {
uint64_t imsi;
in_addr_t ue_ipv4;
uint8_t ebi;
uint64_t imsi;
in_addr_t ue_ipv4;
uint8_t ebi;
srslte::gtp_fteid_t up_ctrl_fteid;
srslte::gtp_fteid_t up_user_fteid;
srslte::gtp_fteid_t dw_ctrl_fteid;
@ -65,22 +65,19 @@ typedef struct spgw_tunnel_ctx {
class spgw : public thread
{
class gtpc;
class gtpu;
public:
static spgw* get_instance(void);
static void cleanup(void);
int init(spgw_args_t* args, srslte::log_filter* spgw_log);
void stop();
void run_thread();
void handle_create_session_request(struct srslte::gtpc_create_session_request* cs_req,
struct srslte::gtpc_pdu* cs_resp_pdu);
void handle_modify_bearer_request(struct srslte::gtpc_pdu* mb_req_pdu, struct srslte::gtpc_pdu* mb_resp_pdu);
void handle_delete_session_request(struct srslte::gtpc_pdu* del_req_pdu, struct srslte::gtpc_pdu* del_resp_pdu);
void handle_release_access_bearers_request(struct srslte::gtpc_pdu* rel_req_pdu,
struct srslte::gtpc_pdu* rel_resp_pdu);
int init(spgw_args_t* args, srslte::log_filter* gtpu_log, srslte::log_filter* gtpc_log, srslte::log_filter* spgw_log);
void stop();
void run_thread();
void handle_sgi_pdu(srslte::byte_buffer_t* msg);
void handle_s1u_pdu(srslte::byte_buffer_t* msg);
void handle_s11_pdu(srslte::gtpc_pdu* pdu, srslte::gtpc_pdu* reply_pdu);
private:
spgw();
@ -102,31 +99,14 @@ private:
srslte::byte_buffer_pool* m_pool;
mme_gtpc* m_mme_gtpc;
bool m_sgi_up;
int m_sgi_if;
int m_sgi_sock;
bool m_s1u_up;
int m_s1u;
uint64_t m_next_ctrl_teid;
uint64_t m_next_user_teid;
sockaddr_in m_s1u_addr;
// data-plane/control-plane mutex
pthread_mutex_t m_mutex;
std::map<uint64_t, uint32_t> m_imsi_to_ctr_teid; // IMSI to control TEID map.
// Important to check if UE is previously connected
std::map<uint32_t, spgw_tunnel_ctx*> m_teid_to_tunnel_ctx; // Map control TEID to tunnel ctx.
// Usefull to get reply ctrl TEID, UE IP, etc.
std::map<in_addr_t, srslte::gtpc_f_teid_ie> m_ip_to_teid; // Map IP to User-plane TEID for downlink traffic
uint32_t m_h_next_ue_ip;
// GTP-C and GTP-U handlers
gtpc* m_gtpc;
gtpu* m_gtpu;
/*Logs*/
// Logs
srslte::log_filter* m_spgw_log;
};

@ -52,8 +52,12 @@ typedef struct {
int nas_hex_limit;
std::string s1ap_level;
int s1ap_hex_limit;
std::string gtpc_level;
int gtpc_hex_limit;
std::string mme_gtpc_level;
int mme_gtpc_hex_limit;
std::string spgw_gtpc_level;
int spgw_gtpc_hex_limit;
std::string gtpu_level;
int gtpu_hex_limit;
std::string spgw_level;
int spgw_hex_limit;
std::string hss_level;
@ -122,24 +126,28 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("spgw.sgi_if_addr", bpo::value<string>(&sgi_if_addr)->default_value("176.16.0.1"), "IP address of TUN interface for the SGi connection")
("spgw.sgi_if_name", bpo::value<string>(&sgi_if_name)->default_value("srs_spgw_sgi"), "Name of TUN interface for the SGi connection")
("pcap.enable", bpo::value<bool>(&args->mme_args.s1ap_args.pcap_enable)->default_value(false), "Enable S1AP PCAP")
("pcap.filename", bpo::value<string>(&args->mme_args.s1ap_args.pcap_filename)->default_value("/tmp/epc.pcap"), "PCAP filename")
("log.nas_level", bpo::value<string>(&args->log_args.nas_level), "MME NAS log level")
("log.nas_hex_limit", bpo::value<int>(&args->log_args.nas_hex_limit), "MME NAS log hex dump limit")
("log.s1ap_level", bpo::value<string>(&args->log_args.s1ap_level), "MME S1AP log level")
("log.s1ap_hex_limit", bpo::value<int>(&args->log_args.s1ap_hex_limit), "MME S1AP log hex dump limit")
("log.gtpc_level", bpo::value<string>(&args->log_args.gtpc_level), "MME GTPC log level")
("log.gtpc_hex_limit", bpo::value<int>(&args->log_args.gtpc_hex_limit), "MME GTPC log hex dump limit")
("log.spgw_level", bpo::value<string>(&args->log_args.spgw_level), "SPGW log level")
("log.spgw_hex_limit", bpo::value<int>(&args->log_args.spgw_hex_limit), "SPGW log hex dump limit")
("log.hss_level", bpo::value<string>(&args->log_args.hss_level), "HSS log level")
("log.hss_hex_limit", bpo::value<int>(&args->log_args.hss_hex_limit), "HSS log hex dump limit")
("log.all_level", bpo::value<string>(&args->log_args.all_level)->default_value("info"), "ALL log level")
("log.all_hex_limit", bpo::value<int>(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit")
("log.filename", bpo::value<string>(&args->log_args.filename)->default_value("/tmp/epc.log"),"Log filename")
("pcap.enable", bpo::value<bool>(&args->mme_args.s1ap_args.pcap_enable)->default_value(false), "Enable S1AP PCAP")
("pcap.filename", bpo::value<string>(&args->mme_args.s1ap_args.pcap_filename)->default_value("/tmp/epc.pcap"), "PCAP filename")
("log.nas_level", bpo::value<string>(&args->log_args.nas_level), "MME NAS log level")
("log.nas_hex_limit", bpo::value<int>(&args->log_args.nas_hex_limit), "MME NAS log hex dump limit")
("log.s1ap_level", bpo::value<string>(&args->log_args.s1ap_level), "MME S1AP log level")
("log.s1ap_hex_limit", bpo::value<int>(&args->log_args.s1ap_hex_limit), "MME S1AP log hex dump limit")
("log.mme_gtpc_level", bpo::value<string>(&args->log_args.mme_gtpc_level), "MME GTPC log level")
("log.mme_gtpc_hex_limit", bpo::value<int>(&args->log_args.mme_gtpc_hex_limit), "MME GTPC log hex dump limit")
("log.spgw_gtpc_level", bpo::value<string>(&args->log_args.spgw_gtpc_level), "SPGW GTPC log level")
("log.spgw_gtpc_hex_limit", bpo::value<int>(&args->log_args.spgw_gtpc_hex_limit), "SPGW GTPC log hex dump limit")
("log.gtpu_level", bpo::value<string>(&args->log_args.gtpu_level), "GTP-U log level")
("log.gtpu_hex_limit", bpo::value<int>(&args->log_args.gtpu_hex_limit), "GTP-U log hex dump limit")
("log.spgw_level", bpo::value<string>(&args->log_args.spgw_level), "SPGW log level")
("log.spgw_hex_limit", bpo::value<int>(&args->log_args.spgw_hex_limit), "SPGW log hex dump limit")
("log.hss_level", bpo::value<string>(&args->log_args.hss_level), "HSS log level")
("log.hss_hex_limit", bpo::value<int>(&args->log_args.hss_hex_limit), "HSS log hex dump limit")
("log.all_level", bpo::value<string>(&args->log_args.all_level)->default_value("info"), "ALL log level")
("log.all_hex_limit", bpo::value<int>(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit")
("log.filename", bpo::value<string>(&args->log_args.filename)->default_value("/tmp/epc.log"),"Log filename")
;
// Positional options - config file location
@ -268,8 +276,14 @@ void parse_args(all_args_t* args, int argc, char* argv[])
if (!vm.count("log.s1ap_level")) {
args->log_args.s1ap_level = args->log_args.all_level;
}
if (!vm.count("log.gtpc_level")) {
args->log_args.gtpc_level = args->log_args.all_level;
if (!vm.count("log.mme_gtpc_level")) {
args->log_args.mme_gtpc_level = args->log_args.all_level;
}
if (!vm.count("log.spgw_gtpc_level")) {
args->log_args.spgw_gtpc_level = args->log_args.all_level;
}
if (!vm.count("log.gtpu_level")) {
args->log_args.gtpu_level = args->log_args.all_level;
}
if (!vm.count("log.spgw_level")) {
args->log_args.spgw_level = args->log_args.all_level;
@ -284,8 +298,14 @@ void parse_args(all_args_t* args, int argc, char* argv[])
if (!vm.count("log.s1ap_hex_limit")) {
args->log_args.s1ap_hex_limit = args->log_args.all_hex_limit;
}
if (!vm.count("log.gtpc_hex_limit")) {
args->log_args.gtpc_hex_limit = args->log_args.all_hex_limit;
if (!vm.count("log.mme_gtpc_hex_limit")) {
args->log_args.mme_gtpc_hex_limit = args->log_args.all_hex_limit;
}
if (!vm.count("log.spgw_gtpc_hex_limit")) {
args->log_args.spgw_gtpc_hex_limit = args->log_args.all_hex_limit;
}
if (!vm.count("log.gtpu_hex_limit")) {
args->log_args.gtpu_hex_limit = args->log_args.all_hex_limit;
}
if (!vm.count("log.spgw_hex_limit")) {
args->log_args.spgw_hex_limit = args->log_args.all_hex_limit;
@ -386,15 +406,25 @@ int main(int argc, char* argv[])
s1ap_log.set_hex_limit(args.log_args.s1ap_hex_limit);
srslte::log_filter mme_gtpc_log;
mme_gtpc_log.init("GTPC", logger);
mme_gtpc_log.set_level(level(args.log_args.gtpc_level));
mme_gtpc_log.set_hex_limit(args.log_args.gtpc_hex_limit);
mme_gtpc_log.init("MME GTPC", logger);
mme_gtpc_log.set_level(level(args.log_args.mme_gtpc_level));
mme_gtpc_log.set_hex_limit(args.log_args.mme_gtpc_hex_limit);
srslte::log_filter hss_log;
hss_log.init("HSS ", logger);
hss_log.set_level(level(args.log_args.hss_level));
hss_log.set_hex_limit(args.log_args.hss_hex_limit);
srslte::log_filter spgw_gtpc_log;
spgw_gtpc_log.init("SPGW GTPC", logger);
spgw_gtpc_log.set_level(level(args.log_args.spgw_gtpc_level));
spgw_gtpc_log.set_hex_limit(args.log_args.spgw_gtpc_hex_limit);
srslte::log_filter gtpu_log;
gtpu_log.init("GTPU", logger);
gtpu_log.set_level(level(args.log_args.mme_gtpc_level));
gtpu_log.set_hex_limit(args.log_args.mme_gtpc_hex_limit);
srslte::log_filter spgw_log;
spgw_log.init("SPGW", logger);
spgw_log.set_level(level(args.log_args.spgw_level));
@ -413,7 +443,7 @@ int main(int argc, char* argv[])
}
spgw* spgw = spgw::get_instance();
if (spgw->init(&args.spgw_args, &spgw_log)) {
if (spgw->init(&args.spgw_args, &gtpu_log, &spgw_gtpc_log, &spgw_log)) {
cout << "Error initializing SP-GW" << endl;
exit(1);
}

@ -81,9 +81,20 @@ bool mme_gtpc::init(srslte::log_filter* mme_gtpc_log)
return true;
}
uint32_t mme_gtpc::get_new_ctrl_teid()
void mme_gtpc::handle_s11_pdu(srslte::gtpc_pdu *pdu)
{
return m_next_ctrl_teid++; // FIXME Use a Id pool?
m_mme_gtpc_log->debug("MME Received GTP-C PDU. Message type %s\n",srslte::gtpc_msg_type_to_str(pdu->header.type));
switch(pdu->header.type){
case srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE:
handle_create_session_response(pdu);
break;
case srslte::GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE:
handle_modify_bearer_response(pdu);
break;
default:
m_mme_gtpc_log->error("Unhandled GTP-C Message type\n");
}
return;
}
bool mme_gtpc::send_create_session_request(uint64_t imsi)
@ -150,7 +161,11 @@ bool mme_gtpc::send_create_session_request(uint64_t imsi)
bzero(&gtpc_ctx, sizeof(gtpc_ctx_t));
gtpc_ctx.mme_ctr_fteid = cs_req->sender_f_teid;
m_imsi_to_gtpc_ctx.insert(std::pair<uint64_t, gtpc_ctx_t>(imsi, gtpc_ctx));
m_spgw->handle_create_session_request(cs_req, &cs_resp_pdu);
// Send msg to SPGW
srslte::gtpc_pdu mb_resp_pdu;
m_spgw->handle_s11_pdu(&cs_req_pdu, &cs_resp_pdu);
handle_s11_pdu(&cs_resp_pdu);
return true;
}
@ -267,9 +282,10 @@ bool mme_gtpc::send_modify_bearer_request(uint64_t imsi, uint16_t erab_to_modify
addr.s_addr = enb_fteid->ipv4;
m_mme_gtpc_log->info("GTP-C Modify bearer request -- S1-U TEID 0x%x, IP %s\n", enb_fteid->teid, inet_ntoa(addr));
// Send msg to SPGW
srslte::gtpc_pdu mb_resp_pdu;
m_spgw->handle_modify_bearer_request(&mb_req_pdu, &mb_resp_pdu);
handle_modify_bearer_response(&mb_resp_pdu);
m_spgw->handle_s11_pdu(&mb_req_pdu, &mb_resp_pdu);
handle_s11_pdu(&mb_resp_pdu);
return true;
}
@ -314,11 +330,10 @@ bool mme_gtpc::send_delete_session_request(uint64_t imsi)
del_req->cause.cause_value = srslte::GTPC_CAUSE_VALUE_ISR_DEACTIVATION;
m_mme_gtpc_log->info("GTP-C Delete Session Request -- S-GW Control TEID %d\n", sgw_ctr_fteid.teid);
// Send msg to SPGW
srslte::gtpc_pdu del_resp_pdu;
m_spgw->handle_delete_session_request(&del_req_pdu, &del_resp_pdu);
// TODO Handle delete session response
m_spgw->handle_s11_pdu(&del_req_pdu, &del_resp_pdu);
// Delete GTP-C context
std::map<uint32_t, uint64_t>::iterator it_imsi = m_mme_ctr_teid_to_imsi.find(mme_ctr_fteid.teid);
if (it_imsi == m_mme_ctr_teid_to_imsi.end()) {
@ -353,10 +368,12 @@ void mme_gtpc::send_release_access_bearers_request(uint64_t imsi)
srslte::gtpc_release_access_bearers_request* rel_req = &rel_req_pdu.choice.release_access_bearers_request;
m_mme_gtpc_log->info("GTP-C Release Access Berarers Request -- S-GW Control TEID %d\n", sgw_ctr_fteid.teid);
// Send msg to SPGW
srslte::gtpc_pdu rel_resp_pdu;
m_spgw->handle_release_access_bearers_request(&rel_req_pdu, &rel_resp_pdu);
m_spgw->handle_s11_pdu(&rel_req_pdu, &rel_resp_pdu);
// The GTP-C connection will not be torn down, just the user plane bearers.
return;
}
} // namespace srsepc

@ -352,8 +352,8 @@ bool s1ap_ctx_mngmt_proc::handle_ue_context_release_complete(
// Delete user plane context at the SPGW (but keep GTP-C connection).
if (ecm_ctx->state == ECM_STATE_CONNECTED) {
// There are active E-RABs, send release access mearers request
m_s1ap_log->console("There are active E-RABs, send release access bearers request");
m_s1ap_log->info("There are active E-RABs, send release access bearers request");
m_s1ap_log->console("There are active E-RABs, send release access bearers request\n");
m_s1ap_log->info("There are active E-RABs, send release access bearers request\n");
m_mme_gtpc->send_release_access_bearers_request(emm_ctx->imsi);
// The handle_release_access_bearers_response function will make sure to mark E-RABS DEACTIVATED
// It will release the UEs downstream S1-U and keep the upstream S1-U connection active.

@ -0,0 +1,316 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2017 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srsepc/hdr/spgw/gtpc.h"
#include <algorithm>
#include <fcntl.h>
#include <inttypes.h> // for printing uint64_t
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/ip.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
namespace srsepc {
/**********************************************
*
* GTP-C class that handles the GTP-C protocol
* comminication with the MME
*
**********************************************/
spgw::gtpc::gtpc() : m_h_next_ue_ip(0), m_next_ctrl_teid(1), m_next_user_teid(1)
{
return;
}
spgw::gtpc::~gtpc()
{
return;
}
int spgw::gtpc::init(spgw_args_t* args, spgw* spgw, gtpu_interface_gtpc* gtpu, srslte::log_filter* gtpc_log)
{
srslte::error_t err;
m_pool = srslte::byte_buffer_pool::get_instance();
// Init log
m_gtpc_log = gtpc_log;
// Init interfaces
m_spgw = spgw;
m_gtpu = gtpu;
// Init IP pool
err = init_ue_ip(args);
if (err != srslte::ERROR_NONE) {
m_gtpc_log->console("Could not initialize the IP pool.\n");
return -1;
}
m_gtpc_log->info("SPGW S11 Initialized.\n");
m_gtpc_log->console("SPGW S11 Initialized.\n");
return 0;
}
void spgw::gtpc::stop()
{
std::map<uint32_t, spgw_tunnel_ctx*>::iterator it = m_teid_to_tunnel_ctx.begin();
while (it != m_teid_to_tunnel_ctx.end()) {
m_gtpc_log->info("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi);
m_gtpc_log->console("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi);
delete it->second;
m_teid_to_tunnel_ctx.erase(it++);
}
return;
}
void spgw::gtpc::handle_s11_pdu(srslte::gtpc_pdu* pdu, srslte::gtpc_pdu* reply_pdu)
{
m_gtpc_log->console("Received GTP-C PDU. Message type: %s\n", srslte::gtpc_msg_type_to_str(pdu->header.type));
m_gtpc_log->debug("Received GTP-C PDU. Message type: %s\n", srslte::gtpc_msg_type_to_str(pdu->header.type));
switch (pdu->header.type) {
case srslte::GTPC_MSG_TYPE_CREATE_SESSION_REQUEST:
handle_create_session_request(pdu->choice.create_session_request, reply_pdu);
break;
case srslte::GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST:
handle_modify_bearer_request(pdu->header, pdu->choice.modify_bearer_request, reply_pdu);
break;
case srslte::GTPC_MSG_TYPE_DELETE_SESSION_REQUEST:
handle_delete_session_request(pdu->header, pdu->choice.delete_session_request, reply_pdu);
break;
case srslte::GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST:
handle_release_access_bearers_request(pdu->header, pdu->choice.release_access_bearers_request, reply_pdu);
break;
default:
m_gtpc_log->error("Unhandled GTP-C message type\n");
}
return;
}
void spgw::gtpc::handle_create_session_request(const struct srslte::gtpc_create_session_request& cs_req, srslte::gtpc_pdu *cs_resp_pdu)
{
m_gtpc_log->info("SPGW Received Create Session Request\n");
spgw_tunnel_ctx_t* tunnel_ctx;
int default_bearer_id = 5;
// Check if IMSI has active GTP-C and/or GTP-U
bool gtpc_present = m_imsi_to_ctr_teid.count(cs_req.imsi);
if (gtpc_present) {
m_gtpc_log->console("SPGW: GTP-C context for IMSI %015lu already exists.\n", cs_req.imsi);
delete_gtpc_ctx(m_imsi_to_ctr_teid[cs_req.imsi]);
m_gtpc_log->console("SPGW: Deleted previous context.\n");
}
m_gtpc_log->info("Creating new GTP-C context\n");
tunnel_ctx = create_gtpc_ctx(cs_req);
// Create session response message
srslte::gtpc_header* header = &cs_resp_pdu->header;
srslte::gtpc_create_session_response* cs_resp = &cs_resp_pdu->choice.create_session_response;
// Setup GTP-C header
header->piggyback = false;
header->teid_present = true;
header->teid = tunnel_ctx->dw_ctrl_fteid.teid; // Send create session response to the UE's MME Ctrl TEID
header->type = srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE;
// Initialize to zero
bzero(cs_resp, sizeof(struct srslte::gtpc_create_session_response));
// Setup Cause
cs_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
// Setup sender F-TEID (ctrl)
cs_resp->sender_f_teid.ipv4_present = true;
cs_resp->sender_f_teid = tunnel_ctx->up_ctrl_fteid;
// Bearer context created
cs_resp->eps_bearer_context_created.ebi = default_bearer_id;
cs_resp->eps_bearer_context_created.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present = true;
cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid = tunnel_ctx->up_user_fteid;
// Fill in the PAA
cs_resp->paa_present = true;
cs_resp->paa.pdn_type = srslte::GTPC_PDN_TYPE_IPV4;
cs_resp->paa.ipv4_present = true;
cs_resp->paa.ipv4 = tunnel_ctx->ue_ipv4;
m_gtpc_log->info("Sending Create Session Response\n");
// Send Create session response to MME
return;
}
void spgw::gtpc::handle_modify_bearer_request(const struct srslte::gtpc_header& mb_req_hdr,
const struct srslte::gtpc_modify_bearer_request& mb_req,
srslte::gtpc_pdu* mb_resp_pdu)
{
m_gtpc_log->info("Received Modified Bearer Request\n");
// Get control tunnel info from mb_req PDU
uint32_t ctrl_teid = mb_req_hdr.teid;
std::map<uint32_t, spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid);
if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
m_gtpc_log->warning("Could not find TEID %d to modify\n", ctrl_teid);
return;
}
spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second;
// Store user DW link TEID
tunnel_ctx->dw_user_fteid.teid = mb_req.eps_bearer_context_to_modify.s1_u_enb_f_teid.teid;
tunnel_ctx->dw_user_fteid.ipv4 = mb_req.eps_bearer_context_to_modify.s1_u_enb_f_teid.ipv4;
// Set up actual tunnel
m_gtpc_log->info("Setting Up GTP-U tunnel. Tunnel info: \n");
struct in_addr addr;
addr.s_addr = tunnel_ctx->ue_ipv4;
m_gtpc_log->info("IMSI: %lu, UE IP: %s \n", tunnel_ctx->imsi, inet_ntoa(addr));
m_gtpc_log->info("S-GW Rx Ctrl TEID 0x%x, MME Rx Ctrl TEID 0x%x\n", tunnel_ctx->up_ctrl_fteid.teid,
tunnel_ctx->dw_ctrl_fteid.teid);
m_gtpc_log->info("S-GW Rx Ctrl IP (NA), MME Rx Ctrl IP (NA)\n");
struct in_addr addr2;
addr2.s_addr = tunnel_ctx->up_user_fteid.ipv4;
m_gtpc_log->info("S-GW Rx User TEID 0x%x, S-GW Rx User IP %s\n", tunnel_ctx->up_user_fteid.teid, inet_ntoa(addr2));
struct in_addr addr3;
addr3.s_addr = tunnel_ctx->dw_user_fteid.ipv4;
m_gtpc_log->info("eNB Rx User TEID 0x%x, eNB Rx User IP %s\n", tunnel_ctx->dw_user_fteid.teid, inet_ntoa(addr3));
// Setup IP to F-TEID map
m_gtpu->modify_gtpu_tunnel(tunnel_ctx->ue_ipv4, tunnel_ctx->dw_user_fteid, tunnel_ctx->up_ctrl_fteid.teid);
// Setting up Modify bearer response PDU
// Header
srslte::gtpc_header* header = &mb_resp_pdu->header;
header->piggyback = false;
header->teid_present = true;
header->teid = tunnel_ctx->dw_ctrl_fteid.teid;
header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE;
// PDU
srslte::gtpc_modify_bearer_response* mb_resp = &mb_resp_pdu->choice.modify_bearer_response;
mb_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
mb_resp->eps_bearer_context_modified.ebi = tunnel_ctx->ebi;
mb_resp->eps_bearer_context_modified.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
//Send Modify Bearer Response PDU
return;
}
void spgw::gtpc::handle_delete_session_request(const srslte::gtpc_header& header,
const srslte::gtpc_delete_session_request& del_req_pdu,
srslte::gtpc_pdu* del_resp_pdu)
{
uint32_t ctrl_teid = header.teid;
std::map<uint32_t, spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid);
if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
m_gtpc_log->warning("Could not find TEID 0x%x to delete session\n", ctrl_teid);
return;
}
spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second;
in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4;
m_gtpu->delete_gtpu_tunnel(ue_ipv4);
delete_gtpc_ctx(ctrl_teid);
return;
}
void spgw::gtpc::handle_release_access_bearers_request(const srslte::gtpc_header& header,
const srslte::gtpc_release_access_bearers_request& rel_req,
srslte::gtpc_pdu* rel_resp_pdu)
{
// Find tunel ctxt
uint32_t ctrl_teid = header.teid;
std::map<uint32_t, spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid);
if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
m_gtpc_log->warning("Could not find TEID 0x%x to release bearers\n", ctrl_teid);
return;
}
spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second;
in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4;
// Delete data tunnel & do NOT delete control tunnel
m_gtpu->delete_gtpu_tunnel(ue_ipv4);
return;
}
/*
* Context management functions
*/
spgw_tunnel_ctx_t* spgw::gtpc::create_gtpc_ctx(const struct srslte::gtpc_create_session_request& cs_req)
{
// Setup uplink control TEID
uint64_t spgw_uplink_ctrl_teid = get_new_ctrl_teid();
// Setup uplink user TEID
uint64_t spgw_uplink_user_teid = get_new_user_teid();
// Allocate UE IP
in_addr_t ue_ip = get_new_ue_ipv4();
uint8_t default_bearer_id = 5;
m_gtpc_log->console("SPGW: Allocated Ctrl TEID %" PRIu64 "\n", spgw_uplink_ctrl_teid);
m_gtpc_log->console("SPGW: Allocated User TEID %" PRIu64 "\n", spgw_uplink_user_teid);
struct in_addr ue_ip_;
ue_ip_.s_addr = ue_ip;
m_gtpc_log->console("SPGW: Allocate UE IP %s\n", inet_ntoa(ue_ip_));
// Save the UE IP to User TEID map
spgw_tunnel_ctx_t* tunnel_ctx = new spgw_tunnel_ctx_t;
tunnel_ctx->imsi = cs_req.imsi;
tunnel_ctx->ebi = default_bearer_id;
tunnel_ctx->up_ctrl_fteid.teid = spgw_uplink_ctrl_teid;
tunnel_ctx->ue_ipv4 = ue_ip;
tunnel_ctx->up_user_fteid.teid = spgw_uplink_user_teid;
tunnel_ctx->up_user_fteid.ipv4 = m_gtpu->get_s1u_addr();
tunnel_ctx->dw_ctrl_fteid.teid = cs_req.sender_f_teid.teid;
tunnel_ctx->dw_ctrl_fteid.ipv4 = cs_req.sender_f_teid.ipv4;
bzero(&tunnel_ctx->dw_user_fteid, sizeof(srslte::gtp_fteid_t));
m_teid_to_tunnel_ctx.insert(std::pair<uint32_t, spgw_tunnel_ctx_t*>(spgw_uplink_ctrl_teid, tunnel_ctx));
m_imsi_to_ctr_teid.insert(std::pair<uint64_t, uint32_t>(cs_req.imsi, spgw_uplink_ctrl_teid));
return tunnel_ctx;
}
bool spgw::gtpc::delete_gtpc_ctx(uint32_t ctrl_teid)
{
spgw_tunnel_ctx_t* tunnel_ctx;
if (!m_teid_to_tunnel_ctx.count(ctrl_teid)) {
m_gtpc_log->error("Could not find GTP context to delete.\n");
return false;
}
tunnel_ctx = m_teid_to_tunnel_ctx[ctrl_teid];
// Remove Ctrl TEID from IMSI to control TEID map
m_imsi_to_ctr_teid.erase(tunnel_ctx->imsi);
// Remove GTP context from control TEID mapping
m_teid_to_tunnel_ctx.erase(ctrl_teid);
delete tunnel_ctx;
return true;
}
} // namespace srsepc

@ -0,0 +1,329 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2017 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srsepc/hdr/spgw/gtpu.h"
#include "srsepc/hdr/mme/mme_gtpc.h"
#include "srslte/upper/gtpu.h"
#include <algorithm>
#include <fcntl.h>
#include <inttypes.h> // for printing uint64_t
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/ip.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
namespace srsepc {
/**************************************
*
* GTP-U class that handles the packet
* forwarding to and from eNBs
*
**************************************/
spgw::gtpu::gtpu() : m_sgi_up(false), m_s1u_up(false)
{
m_pool = srslte::byte_buffer_pool::get_instance();
return;
}
spgw::gtpu::~gtpu()
{
return;
}
int spgw::gtpu::init(spgw_args_t* args, spgw* spgw, srslte::log_filter* gtpu_log)
{
srslte::error_t err;
// Init log
m_gtpu_log = gtpu_log;
// Store interfaces
m_spgw = spgw;
// Init SGi interface
err = init_sgi(args);
if (err != srslte::ERROR_NONE) {
m_gtpu_log->console("Could not initialize the SGi interface.\n");
return -1;
}
// Init S1-U
err = init_s1u(args);
if (err != srslte::ERROR_NONE) {
m_gtpu_log->console("Could not initialize the S1-U interface.\n");
return -1;
}
m_gtpu_log->info("SPGW GTP-U Initialized.\n");
m_gtpu_log->console("SPGW GTP-U Initialized.\n");
return 0;
}
void spgw::gtpu::stop()
{
// Clean up SGi interface
if (m_sgi_up) {
close(m_sgi);
}
// Clean up S1-U socket
if (m_s1u_up) {
close(m_s1u);
}
}
srslte::error_t spgw::gtpu::init_sgi(spgw_args_t *args)
{
struct ifreq ifr;
int sgi_sock;
if (m_sgi_up) {
return (srslte::ERROR_ALREADY_STARTED);
}
// Construct the TUN device
m_sgi = open("/dev/net/tun", O_RDWR);
m_gtpu_log->info("TUN file descriptor = %d\n", m_sgi);
if (m_sgi < 0) {
m_gtpu_log->error("Failed to open TUN device: %s\n", strerror(errno));
return (srslte::ERROR_CANT_START);
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_ifrn.ifrn_name, args->sgi_if_name.c_str(),
std::min(args->sgi_if_name.length(), (size_t)(IFNAMSIZ - 1)));
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0';
if (ioctl(m_sgi, TUNSETIFF, &ifr) < 0) {
m_gtpu_log->error("Failed to set TUN device name: %s\n", strerror(errno));
close(m_sgi);
return (srslte::ERROR_CANT_START);
}
// Bring up the interface
sgi_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (ioctl(sgi_sock, SIOCGIFFLAGS, &ifr) < 0) {
m_gtpu_log->error("Failed to bring up socket: %s\n", strerror(errno));
close(m_sgi);
return (srslte::ERROR_CANT_START);
}
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if (ioctl(sgi_sock, SIOCSIFFLAGS, &ifr) < 0) {
m_gtpu_log->error("Failed to set socket flags: %s\n", strerror(errno));
close(sgi_sock);
close(m_sgi);
return (srslte::ERROR_CANT_START);
}
// Set IP of the interface
struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(args->sgi_if_addr.c_str());
addr->sin_port = 0;
if (ioctl(sgi_sock, SIOCSIFADDR, &ifr) < 0) {
m_gtpu_log->error("Failed to set TUN interface IP. Address: %s, Error: %s\n", args->sgi_if_addr.c_str(),
strerror(errno));
close(m_sgi);
close(sgi_sock);
return srslte::ERROR_CANT_START;
}
ifr.ifr_netmask.sa_family = AF_INET;
((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0");
if (ioctl(sgi_sock, SIOCSIFNETMASK, &ifr) < 0) {
m_gtpu_log->error("Failed to set TUN interface Netmask. Error: %s\n", strerror(errno));
close(m_sgi);
close(sgi_sock);
return srslte::ERROR_CANT_START;
}
close(sgi_sock);
m_sgi_up = true;
m_gtpu_log->info("Initialized SGi interface\n");
return (srslte::ERROR_NONE);
}
srslte::error_t spgw::gtpu::init_s1u(spgw_args_t* args)
{
// Open S1-U socket
m_s1u = socket(AF_INET, SOCK_DGRAM, 0);
if (m_s1u == -1) {
m_gtpu_log->error("Failed to open socket: %s\n", strerror(errno));
return srslte::ERROR_CANT_START;
}
m_s1u_up = true;
// Bind the socket
m_s1u_addr.sin_family = AF_INET;
m_s1u_addr.sin_addr.s_addr = inet_addr(args->gtpu_bind_addr.c_str());
m_s1u_addr.sin_port = htons(GTPU_RX_PORT);
if (bind(m_s1u, (struct sockaddr*)&m_s1u_addr, sizeof(struct sockaddr_in))) {
m_gtpu_log->error("Failed to bind socket: %s\n", strerror(errno));
return srslte::ERROR_CANT_START;
}
m_gtpu_log->info("S1-U socket = %d\n", m_s1u);
m_gtpu_log->info("S1-U IP = %s, Port = %d \n", inet_ntoa(m_s1u_addr.sin_addr), ntohs(m_s1u_addr.sin_port));
m_gtpu_log->info("Initialized S1-U interface\n");
return srslte::ERROR_NONE;
}
void spgw::gtpu::handle_sgi_pdu(srslte::byte_buffer_t *msg)
{
uint8_t version = 0;
uint32_t dest_ip;
bool usr_found = false;
bool ctr_found = false;
struct in_addr dest_addr;
std::map<uint32_t, srslte::gtpc_f_teid_ie>::iterator gtpu_fteid_it;
std::map<in_addr_t, uint32_t>::iterator gtpc_teid_it;
srslte::gtpc_f_teid_ie enb_fteid;
uint32_t spgw_teid;
struct iphdr *iph = (struct iphdr *)msg->msg;
m_gtpu_log->debug("Received SGi PDU. Bytes %d\n", msg->N_bytes);
if (iph->version != 4) {
m_gtpu_log->warning("IPv6 not supported yet.\n");
return;
}
if (ntohs(iph->tot_len) < 20) {
m_gtpu_log->warning("Invalid IP header length. IP lenght %d.\n", ntohs(iph->tot_len));
return;
}
//Logging PDU info
m_gtpu_log->debug("SGi PDU -- IP version %d, Total length %d\n", iph->version, ntohs(iph->tot_len));
m_gtpu_log->debug("SGi PDU -- IP src addr %s\n", gtpu_ntoa(iph->saddr).c_str());
m_gtpu_log->debug("SGi PDU -- IP dst addr %s\n", gtpu_ntoa(iph->daddr).c_str());
// Find user and control tunnel
pthread_mutex_lock(&m_spgw->m_mutex);
gtpu_fteid_it = m_ip_to_usr_teid.find(iph->daddr);
if (gtpu_fteid_it != m_ip_to_usr_teid.end()) {
usr_found = true;
enb_fteid = gtpu_fteid_it->second;
}
pthread_mutex_unlock(&m_spgw->m_mutex);
if (usr_found == false) {
m_gtpu_log->debug("Packet for unknown UE. Discarding packet.\n");
return;
} else {
send_s1u_pdu(enb_fteid, msg);
}
return;
}
void spgw::gtpu::handle_s1u_pdu(srslte::byte_buffer_t* msg)
{
srslte::gtpu_header_t header;
srslte::gtpu_read_header(msg, &header, m_gtpu_log);
m_gtpu_log->debug("Received PDU from S1-U. Bytes=%d\n", msg->N_bytes);
m_gtpu_log->debug("TEID 0x%x. Bytes=%d\n", header.teid, msg->N_bytes);
int n = write(m_sgi, msg->msg, msg->N_bytes);
if (n < 0) {
m_gtpu_log->error("Could not write to TUN interface.\n");
} else {
m_gtpu_log->debug("Forwarded packet to TUN interface. Bytes= %d/%d\n", n, msg->N_bytes);
}
return;
}
void spgw::gtpu::send_s1u_pdu(srslte::gtp_fteid_t enb_fteid, srslte::byte_buffer_t* msg)
{
// Set eNB destination address
struct sockaddr_in enb_addr;
enb_addr.sin_family = AF_INET;
enb_addr.sin_port = htons(GTPU_RX_PORT);
enb_addr.sin_addr.s_addr = enb_fteid.ipv4;
// Setup GTP-U header
srslte::gtpu_header_t header;
header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL;
header.message_type = GTPU_MSG_DATA_PDU;
header.length = msg->N_bytes;
header.teid = enb_fteid.teid;
m_gtpu_log->debug("User plane tunnel found SGi PDU. Forwarding packet to S1-U.\n");
m_gtpu_log->debug("eNB F-TEID -- eNB IP %s, eNB TEID 0x%x.\n", inet_ntoa(enb_addr.sin_addr), enb_fteid.teid);
// Write header into packet
if (!srslte::gtpu_write_header(&header, msg, m_gtpu_log)) {
m_gtpu_log->error("Error writing GTP-U header on PDU\n");
return;
}
// Send packet to destination
int n = sendto(m_s1u, msg->msg, msg->N_bytes, 0, (struct sockaddr*)&enb_addr, sizeof(enb_addr));
if (n < 0) {
m_gtpu_log->error("Error sending packet to eNB\n");
} else if ((unsigned int)n != msg->N_bytes) {
m_gtpu_log->error("Mis-match between packet bytes and sent bytes: Sent: %d/%d\n", n, msg->N_bytes);
}
return;
}
/*
* Tunnel managment
*/
bool spgw::gtpu::modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtpc_f_teid_ie dw_user_fteid,
uint32_t up_ctrl_teid)
{
m_gtpu_log->info("Modifying GTP-U Tunnel.\n");
m_gtpu_log->info("UE IP %s\n", gtpu_ntoa(ue_ipv4).c_str());
m_gtpu_log->info("Downlink eNB addr %s, U-TEID 0x%x\n", gtpu_ntoa(dw_user_fteid.ipv4).c_str(), dw_user_fteid.teid);
m_gtpu_log->info("Uplink C-TEID: 0x%x\n", up_ctrl_teid);
pthread_mutex_lock(&m_spgw->m_mutex);
m_ip_to_usr_teid[ue_ipv4] = dw_user_fteid;
pthread_mutex_unlock(&m_spgw->m_mutex);
return true;
}
bool spgw::gtpu::delete_gtpu_tunnel(in_addr_t ue_ipv4)
{
// Remove GTP-U connections, if any.
pthread_mutex_lock(&m_spgw->m_mutex);
if (m_ip_to_usr_teid.count(ue_ipv4)) {
m_ip_to_usr_teid.erase(ue_ipv4);
pthread_mutex_unlock(&m_spgw->m_mutex);
} else {
m_gtpu_log->error("Could not find GTP-U Tunnel to delete.\n");
pthread_mutex_unlock(&m_spgw->m_mutex);
return false;
}
return true;
}
} // namespace srsepc

@ -26,31 +26,27 @@
#include "srsepc/hdr/spgw/spgw.h"
#include "srsepc/hdr/mme/mme_gtpc.h"
#include "srsepc/hdr/spgw/gtpc.h"
#include "srsepc/hdr/spgw/gtpu.h"
#include "srslte/upper/gtpu.h"
#include <algorithm>
#include <fcntl.h>
#include <inttypes.h> // for printing uint64_t
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/ip.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
namespace srsepc {
spgw* spgw::m_instance = NULL;
pthread_mutex_t spgw_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
const uint16_t SPGW_BUFFER_SIZE = 2500;
spgw::spgw() : m_running(false), m_sgi_up(false), m_s1u_up(false), m_next_ctrl_teid(1), m_next_user_teid(1)
spgw::spgw() : m_running(false)
{
m_gtpc = new spgw::gtpc;
m_gtpu = new spgw::gtpu;
return;
}
spgw::~spgw()
{
delete m_gtpc;
delete m_gtpu;
return;
}
@ -74,7 +70,10 @@ void spgw::cleanup()
pthread_mutex_unlock(&spgw_instance_mutex);
}
int spgw::init(spgw_args_t* args, srslte::log_filter* spgw_log)
int spgw::init(spgw_args_t* args,
srslte::log_filter* gtpu_log,
srslte::log_filter* gtpc_log,
srslte::log_filter* spgw_log)
{
srslte::error_t err;
m_pool = srslte::byte_buffer_pool::get_instance();
@ -83,22 +82,14 @@ int spgw::init(spgw_args_t* args, srslte::log_filter* spgw_log)
m_spgw_log = spgw_log;
m_mme_gtpc = mme_gtpc::get_instance();
// Init SGi interface
err = init_sgi_if(args);
if (err != srslte::ERROR_NONE) {
m_spgw_log->console("Could not initialize the SGi interface.\n");
// Init GTP-U
if (m_gtpu->init(args, this, gtpu_log) != 0) {
m_spgw_log->console("Could not initialize the SPGW's GTP-U.\n");
return -1;
}
// Init S1-U
err = init_s1u(args);
if (err != srslte::ERROR_NONE) {
m_spgw_log->console("Could not initialize the S1-U interface.\n");
return -1;
}
// Initialize UE ip pool
err = init_ue_ip(args);
if (err != srslte::ERROR_NONE) {
// Init GTP-C
if (m_gtpc->init(args, this, m_gtpu, gtpc_log) != 0) {
m_spgw_log->console("Could not initialize the S1-U interface.\n");
return -1;
}
@ -116,130 +107,13 @@ void spgw::stop()
m_running = false;
thread_cancel();
wait_thread_finish();
// Clean up SGi interface
if (m_sgi_up) {
close(m_sgi_if);
close(m_sgi_sock);
}
// Clean up S1-U socket
if (m_s1u_up) {
close(m_s1u);
}
}
//Delete GTP-C tunnel
std::map<uint32_t, spgw_tunnel_ctx*>::iterator it = m_teid_to_tunnel_ctx.begin();
while (it != m_teid_to_tunnel_ctx.end()) {
m_spgw_log->info("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi);
m_spgw_log->console("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi);
delete it->second;
m_teid_to_tunnel_ctx.erase(it++);
}
m_gtpu->stop();
m_gtpc->stop();
return;
}
srslte::error_t spgw::init_sgi_if(spgw_args_t* args)
{
if (m_sgi_up) {
return (srslte::ERROR_ALREADY_STARTED);
}
// Construct the TUN device
m_sgi_if = open("/dev/net/tun", O_RDWR);
m_spgw_log->info("TUN file descriptor = %d\n", m_sgi_if);
if (m_sgi_if < 0) {
m_spgw_log->error("Failed to open TUN device: %s\n", strerror(errno));
return (srslte::ERROR_CANT_START);
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_ifrn.ifrn_name, args->sgi_if_name.c_str(),
std::min(args->sgi_if_name.length(), (size_t)(IFNAMSIZ - 1)));
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0';
if (ioctl(m_sgi_if, TUNSETIFF, &ifr) < 0) {
m_spgw_log->error("Failed to set TUN device name: %s\n", strerror(errno));
close(m_sgi_if);
return (srslte::ERROR_CANT_START);
}
// Bring up the interface
m_sgi_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (ioctl(m_sgi_sock, SIOCGIFFLAGS, &ifr) < 0) {
m_spgw_log->error("Failed to bring up socket: %s\n", strerror(errno));
close(m_sgi_if);
return (srslte::ERROR_CANT_START);
}
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if (ioctl(m_sgi_sock, SIOCSIFFLAGS, &ifr) < 0) {
m_spgw_log->error("Failed to set socket flags: %s\n", strerror(errno));
close(m_sgi_if);
return (srslte::ERROR_CANT_START);
}
// Set IP of the interface
struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(args->sgi_if_addr.c_str());
addr->sin_port = 0;
if (ioctl(m_sgi_sock, SIOCSIFADDR, &ifr) < 0) {
m_spgw_log->error("Failed to set TUN interface IP. Address: %s, Error: %s\n", args->sgi_if_addr.c_str(),
strerror(errno));
close(m_sgi_if);
close(m_sgi_sock);
return srslte::ERROR_CANT_START;
}
ifr.ifr_netmask.sa_family = AF_INET;
((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0");
if (ioctl(m_sgi_sock, SIOCSIFNETMASK, &ifr) < 0) {
m_spgw_log->error("Failed to set TUN interface Netmask. Error: %s\n", strerror(errno));
close(m_sgi_if);
close(m_sgi_sock);
return srslte::ERROR_CANT_START;
}
m_sgi_up = true;
return (srslte::ERROR_NONE);
}
srslte::error_t spgw::init_s1u(spgw_args_t* args)
{
// Open S1-U socket
m_s1u = socket(AF_INET, SOCK_DGRAM, 0);
if (m_s1u == -1) {
m_spgw_log->error("Failed to open socket: %s\n", strerror(errno));
return srslte::ERROR_CANT_START;
}
m_s1u_up = true;
// Bind the socket
m_s1u_addr.sin_family = AF_INET;
m_s1u_addr.sin_addr.s_addr = inet_addr(args->gtpu_bind_addr.c_str());
m_s1u_addr.sin_port = htons(GTPU_RX_PORT);
if (bind(m_s1u, (struct sockaddr*)&m_s1u_addr, sizeof(struct sockaddr_in))) {
m_spgw_log->error("Failed to bind socket: %s\n", strerror(errno));
return srslte::ERROR_CANT_START;
}
m_spgw_log->info("S1-U socket = %d\n", m_s1u);
m_spgw_log->info("S1-U IP = %s, Port = %d \n", inet_ntoa(m_s1u_addr.sin_addr), ntohs(m_s1u_addr.sin_port));
return srslte::ERROR_NONE;
}
srslte::error_t spgw::init_ue_ip(spgw_args_t* args)
{
m_h_next_ue_ip = ntohl(inet_addr(args->sgi_if_addr.c_str()));
return srslte::ERROR_NONE;
}
void spgw::run_thread()
{
// Mark the thread as running
@ -250,29 +124,28 @@ void spgw::run_thread()
struct sockaddr src_addr;
socklen_t addrlen;
struct iphdr* ip_pkt;
int sgi = m_sgi_if;
int sgi = m_gtpu->get_sgi();
int s1u = m_gtpu->get_s1u();
fd_set set;
// struct timeval to;
int max_fd = std::max(m_s1u, sgi);
int max_fd = std::max(s1u, sgi);
while (m_running) {
msg->reset();
FD_ZERO(&set);
FD_SET(m_s1u, &set);
FD_SET(s1u, &set);
FD_SET(sgi, &set);
// m_spgw_log->info("Waiting for S1-U or SGi packets.\n");
int n = select(max_fd + 1, &set, NULL, NULL, NULL);
if (n == -1) {
m_spgw_log->error("Error from select\n");
} else if (n) {
if (FD_ISSET(m_s1u, &set)) {
msg->N_bytes = recvfrom(m_s1u, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES, 0, &src_addr, &addrlen);
handle_s1u_pdu(msg);
if (FD_ISSET(s1u, &set)) {
msg->N_bytes = recvfrom(s1u, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES, 0, &src_addr, &addrlen);
m_gtpu->handle_s1u_pdu(msg);
}
if (FD_ISSET(m_sgi_if, &set)) {
if (FD_ISSET(sgi, &set)) {
msg->N_bytes = read(sgi, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES);
handle_sgi_pdu(msg);
m_gtpu->handle_sgi_pdu(msg);
}
} else {
m_spgw_log->debug("No data from select.\n");
@ -282,323 +155,7 @@ void spgw::run_thread()
return;
}
void spgw::handle_sgi_pdu(srslte::byte_buffer_t* msg)
{
uint8_t version = 0;
uint32_t dest_ip;
struct in_addr dest_addr;
std::map<uint32_t, srslte::gtpc_f_teid_ie>::iterator gtp_fteid_it;
bool ip_found = false;
srslte::gtpc_f_teid_ie enb_fteid;
struct iphdr* iph = (struct iphdr*)msg->msg;
if (iph->version != 4) {
m_spgw_log->warning("IPv6 not supported yet.\n");
return;
}
if (iph->tot_len < 20) {
m_spgw_log->warning("Invalid IP header length.\n");
return;
}
pthread_mutex_lock(&m_mutex);
gtp_fteid_it = m_ip_to_teid.find(iph->daddr);
if (gtp_fteid_it != m_ip_to_teid.end()) {
ip_found = true;
enb_fteid = gtp_fteid_it->second;
}
pthread_mutex_unlock(&m_mutex);
if (ip_found == false) {
return;
}
struct sockaddr_in enb_addr;
enb_addr.sin_family = AF_INET;
enb_addr.sin_port = htons(GTPU_RX_PORT);
enb_addr.sin_addr.s_addr = enb_fteid.ipv4;
// Setup GTP-U header
srslte::gtpu_header_t header;
header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL;
header.message_type = GTPU_MSG_DATA_PDU;
header.length = msg->N_bytes;
header.teid = enb_fteid.teid;
// Write header into packet
if (!srslte::gtpu_write_header(&header, msg, m_spgw_log)) {
m_spgw_log->console("Error writing GTP-U header on PDU\n");
}
// Send packet to destination
int n = sendto(m_s1u, msg->msg, msg->N_bytes, 0, (struct sockaddr*)&enb_addr, sizeof(enb_addr));
if (n < 0) {
m_spgw_log->error("Error sending packet to eNB\n");
return;
} else if ((unsigned int)n != msg->N_bytes) {
m_spgw_log->error("Mis-match between packet bytes and sent bytes: Sent: %d, Packet: %d \n", n, msg->N_bytes);
}
return;
}
void spgw::handle_s1u_pdu(srslte::byte_buffer_t* msg)
{
// m_spgw_log->console("Received PDU from S1-U. Bytes=%d\n",msg->N_bytes);
srslte::gtpu_header_t header;
srslte::gtpu_read_header(msg, &header, m_spgw_log);
// m_spgw_log->console("TEID 0x%x. Bytes=%d\n", header.teid, msg->N_bytes);
int n = write(m_sgi_if, msg->msg, msg->N_bytes);
if (n < 0) {
m_spgw_log->error("Could not write to TUN interface.\n");
} else {
// m_spgw_log->console("Forwarded packet to TUN interface. Bytes= %d/%d\n", n, msg->N_bytes);
}
return;
void spgw::handle_s11_pdu(srslte::gtpc_pdu *pdu, srslte::gtpc_pdu *reply_pdu){
m_gtpc->handle_s11_pdu(pdu, reply_pdu);
}
/*
* GTP-C Handler Functions
*/
void spgw::handle_create_session_request(struct srslte::gtpc_create_session_request* cs_req,
struct srslte::gtpc_pdu* cs_resp_pdu)
{
m_spgw_log->info("Received Create Session Request\n");
spgw_tunnel_ctx_t* tunnel_ctx;
int default_bearer_id = 5;
// Check if IMSI has active GTP-C and/or GTP-U
bool gtpc_present = m_imsi_to_ctr_teid.count(cs_req->imsi);
if (gtpc_present) {
m_spgw_log->console("SPGW: GTP-C context for IMSI %015" PRIu64 " already exists.\n", cs_req->imsi);
delete_gtp_ctx(m_imsi_to_ctr_teid[cs_req->imsi]);
m_spgw_log->console("SPGW: Deleted previous context.\n");
}
m_spgw_log->info("Creating new GTP-C context\n");
tunnel_ctx = create_gtp_ctx(cs_req);
// Create session response message
srslte::gtpc_header* header = &cs_resp_pdu->header;
srslte::gtpc_create_session_response* cs_resp = &cs_resp_pdu->choice.create_session_response;
// Setup GTP-C header
header->piggyback = false;
header->teid_present = true;
header->teid = tunnel_ctx->dw_ctrl_fteid.teid; // Send create session requesponse to the UE's MME Ctrl TEID
header->type = srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE;
// Initialize to zero
bzero(cs_resp, sizeof(struct srslte::gtpc_create_session_response));
// Setup Cause
cs_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
// Setup sender F-TEID (ctrl)
cs_resp->sender_f_teid.ipv4_present = true;
cs_resp->sender_f_teid = tunnel_ctx->up_ctrl_fteid;
// Bearer context created
cs_resp->eps_bearer_context_created.ebi = default_bearer_id;
cs_resp->eps_bearer_context_created.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present = true;
cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid = tunnel_ctx->up_user_fteid;
// Fill in the PAA
cs_resp->paa_present = true;
cs_resp->paa.pdn_type = srslte::GTPC_PDN_TYPE_IPV4;
cs_resp->paa.ipv4_present = true;
cs_resp->paa.ipv4 = tunnel_ctx->ue_ipv4;
m_spgw_log->info("Sending Create Session Response\n");
m_mme_gtpc->handle_create_session_response(cs_resp_pdu);
return;
}
void spgw::handle_modify_bearer_request(struct srslte::gtpc_pdu* mb_req_pdu, struct srslte::gtpc_pdu* mb_resp_pdu)
{
m_spgw_log->info("Received Modified Bearer Request\n");
// Get control tunnel info from mb_req PDU
uint32_t ctrl_teid = mb_req_pdu->header.teid;
std::map<uint32_t, spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid);
if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
m_spgw_log->warning("Could not find TEID %d to modify\n", ctrl_teid);
return;
}
spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second;
// Store user DW link TEID
srslte::gtpc_modify_bearer_request* mb_req = &mb_req_pdu->choice.modify_bearer_request;
tunnel_ctx->dw_user_fteid.teid = mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.teid;
tunnel_ctx->dw_user_fteid.ipv4 = mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.ipv4;
// Set up actual tunnel
m_spgw_log->info("Setting Up GTP-U tunnel. Tunnel info: \n");
struct in_addr addr;
addr.s_addr = tunnel_ctx->ue_ipv4;
m_spgw_log->info("IMSI: %lu, UE IP, %s \n", tunnel_ctx->imsi, inet_ntoa(addr));
m_spgw_log->info("S-GW Rx Ctrl TEID 0x%x, MME Rx Ctrl TEID 0x%x\n", tunnel_ctx->up_ctrl_fteid.teid,
tunnel_ctx->dw_ctrl_fteid.teid);
m_spgw_log->info("S-GW Rx Ctrl IP (NA), MME Rx Ctrl IP (NA)\n");
struct in_addr addr2;
addr2.s_addr = tunnel_ctx->up_user_fteid.ipv4;
m_spgw_log->info("S-GW Rx User TEID 0x%x, S-GW Rx User IP %s\n", tunnel_ctx->up_user_fteid.teid, inet_ntoa(addr2));
struct in_addr addr3;
addr3.s_addr = tunnel_ctx->dw_user_fteid.ipv4;
m_spgw_log->info("eNB Rx User TEID 0x%x, eNB Rx User IP %s\n", tunnel_ctx->dw_user_fteid.teid, inet_ntoa(addr3));
// Setup IP to F-TEID map
// bool ret = false;
pthread_mutex_lock(&m_mutex);
m_ip_to_teid[tunnel_ctx->ue_ipv4] = tunnel_ctx->dw_user_fteid;
// ret = m_ip_to_teid.insert(std::pair<uint32_t,srslte::gtpc_f_teid_ie>(tunnel_ctx->ue_ipv4,
// tunnel_ctx->dw_user_fteid));
pthread_mutex_unlock(&m_mutex);
// Setting up Modify bearer response PDU
// Header
srslte::gtpc_header* header = &mb_resp_pdu->header;
header->piggyback = false;
header->teid_present = true;
header->teid = tunnel_ctx->dw_ctrl_fteid.teid; //
header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE;
// PDU
srslte::gtpc_modify_bearer_response* mb_resp = &mb_resp_pdu->choice.modify_bearer_response;
mb_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
mb_resp->eps_bearer_context_modified.ebi = tunnel_ctx->ebi;
mb_resp->eps_bearer_context_modified.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
return;
}
void spgw::handle_delete_session_request(struct srslte::gtpc_pdu* del_req_pdu, struct srslte::gtpc_pdu* del_resp_pdu)
{
// Find tunel ctxt
uint32_t ctrl_teid = del_req_pdu->header.teid;
std::map<uint32_t, spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid);
if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
m_spgw_log->warning("Could not find TEID %d to delete\n", ctrl_teid);
return;
}
spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second;
in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4;
// Delete data tunnel
pthread_mutex_lock(&m_mutex);
std::map<in_addr_t, srslte::gtp_fteid_t>::iterator data_it = m_ip_to_teid.find(tunnel_ctx->ue_ipv4);
if (data_it != m_ip_to_teid.end()) {
m_ip_to_teid.erase(data_it);
}
pthread_mutex_unlock(&m_mutex);
m_teid_to_tunnel_ctx.erase(tunnel_it);
delete tunnel_ctx;
return;
}
void spgw::handle_release_access_bearers_request(struct srslte::gtpc_pdu* rel_req_pdu,
struct srslte::gtpc_pdu* rel_resp_pdu)
{
// Find tunel ctxt
uint32_t ctrl_teid = rel_req_pdu->header.teid;
std::map<uint32_t, spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid);
if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
m_spgw_log->warning("Could not find TEID %d to release bearers from\n", ctrl_teid);
return;
}
spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second;
in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4;
// Delete data tunnel
pthread_mutex_lock(&m_mutex);
std::map<in_addr_t, srslte::gtpc_f_teid_ie>::iterator data_it = m_ip_to_teid.find(tunnel_ctx->ue_ipv4);
if (data_it != m_ip_to_teid.end()) {
m_ip_to_teid.erase(data_it);
}
pthread_mutex_unlock(&m_mutex);
// Do NOT delete control tunnel
return;
}
/*
* Helper Functions
*/
uint64_t spgw::get_new_ctrl_teid()
{
return m_next_ctrl_teid++;
}
uint64_t spgw::get_new_user_teid()
{
return m_next_user_teid++;
}
in_addr_t spgw::get_new_ue_ipv4()
{
m_h_next_ue_ip++;
return ntohl(m_h_next_ue_ip); // FIXME Tmp hack
}
spgw_tunnel_ctx_t* spgw::create_gtp_ctx(struct srslte::gtpc_create_session_request* cs_req)
{
// Setup uplink control TEID
uint64_t spgw_uplink_ctrl_teid = get_new_ctrl_teid();
// Setup uplink user TEID
uint64_t spgw_uplink_user_teid = get_new_user_teid();
// Allocate UE IP
in_addr_t ue_ip = get_new_ue_ipv4();
// in_addr_t ue_ip = inet_addr("172.16.0.2");
uint8_t default_bearer_id = 5;
m_spgw_log->console("SPGW: Allocated Ctrl TEID %" PRIu64 "\n", spgw_uplink_ctrl_teid);
m_spgw_log->console("SPGW: Allocated User TEID %" PRIu64 "\n", spgw_uplink_user_teid);
struct in_addr ue_ip_;
ue_ip_.s_addr = ue_ip;
m_spgw_log->console("SPGW: Allocate UE IP %s\n", inet_ntoa(ue_ip_));
// Save the UE IP to User TEID map
spgw_tunnel_ctx_t* tunnel_ctx = new spgw_tunnel_ctx_t;
bzero(tunnel_ctx, sizeof(spgw_tunnel_ctx_t));
tunnel_ctx->imsi = cs_req->imsi;
tunnel_ctx->ebi = default_bearer_id;
tunnel_ctx->up_user_fteid.teid = spgw_uplink_user_teid;
tunnel_ctx->up_user_fteid.ipv4 = m_s1u_addr.sin_addr.s_addr;
tunnel_ctx->dw_ctrl_fteid.teid = cs_req->sender_f_teid.teid;
tunnel_ctx->dw_ctrl_fteid.ipv4 = cs_req->sender_f_teid.ipv4;
tunnel_ctx->up_ctrl_fteid.teid = spgw_uplink_ctrl_teid;
tunnel_ctx->ue_ipv4 = ue_ip;
m_teid_to_tunnel_ctx.insert(std::pair<uint32_t, spgw_tunnel_ctx_t*>(spgw_uplink_ctrl_teid, tunnel_ctx));
m_imsi_to_ctr_teid.insert(std::pair<uint64_t, uint32_t>(cs_req->imsi, spgw_uplink_ctrl_teid));
return tunnel_ctx;
}
bool spgw::delete_gtp_ctx(uint32_t ctrl_teid)
{
spgw_tunnel_ctx_t* tunnel_ctx;
if (!m_teid_to_tunnel_ctx.count(ctrl_teid)) {
m_spgw_log->error("Could not find GTP context to delete.\n");
return false;
}
tunnel_ctx = m_teid_to_tunnel_ctx[ctrl_teid];
// Remove GTP-U connections, if any.
if (m_ip_to_teid.count(tunnel_ctx->ue_ipv4)) {
pthread_mutex_lock(&m_mutex);
m_ip_to_teid.erase(tunnel_ctx->ue_ipv4);
pthread_mutex_unlock(&m_mutex);
}
// Remove Ctrl TEID from IMSI to control TEID map
m_imsi_to_ctr_teid.erase(tunnel_ctx->imsi);
// Remove GTP context from control TEID mapping
m_teid_to_tunnel_ctx.erase(ctrl_teid);
delete tunnel_ctx;
return true;
}
} // namespace srsepc

Loading…
Cancel
Save