Merge pull request #170 from softwareradiosystems/epc

Better handling of service request and GUTI attach procedures
master
Paul Sutton 7 years ago committed by GitHub
commit 7c0ffaf768
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -52,16 +52,15 @@ const uint8_t GTPC_V2 = 2;
* n+2 | Sequence |
* n+3 | Spare |
***************************************************************************/
typedef struct gtpc_header
{
typedef struct gtpc_header
{
uint8_t version;
bool piggyback;
bool teid_present;
uint8_t type;
uint64_t teid;
uint64_t sequence;
} gtpc_header_t;
}gtpc_header_t;
/****************************************************************************
* GTP-C v2 Payload
@ -69,16 +68,17 @@ const uint8_t GTPC_V2 = 2;
*
* Union that hold the different structures for the possible message types.
***************************************************************************/
typedef union gtpc_msg_choice
{
struct gtpc_create_session_request create_session_request;
struct gtpc_create_session_response create_session_response;
struct gtpc_modify_bearer_request modify_bearer_request;
struct gtpc_modify_bearer_response modify_bearer_response;
struct gtpc_release_access_bearers_request release_access_bearers_request;
struct gtpc_release_access_bearers_response release_access_bearers_response;
struct gtpc_delete_session_request delete_session_request;
struct gtpc_delete_session_response delete_session_response;
} gtpc_msg_choice_t;
}gtpc_msg_choice_t;
/****************************************************************************
* GTP-C v2 Message
@ -88,15 +88,10 @@ typedef union gtpc_msg_choice
* of one GTP-C header and one union of structures, which can hold
* all the possible GTP-C messages
***************************************************************************/
typedef struct gtpc_pdu
{
struct gtpc_header header;
union gtpc_msg_choice choice;
} gtpc_pdu_t;
};
}gtpc_pdu_t;
}//namespace
#endif

@ -400,7 +400,8 @@ enum gtpc_interface_type
S2B_U_PGW_GTP_U_INTERFACE
};
struct gtpc_f_teid_ie
typedef struct gtpc_f_teid_ie
{
bool ipv4_present;
bool ipv6_present;
@ -408,7 +409,7 @@ struct gtpc_f_teid_ie
uint32_t teid;
in_addr_t ipv4;
struct in6_addr ipv6; //FIXME
};
} gtp_fteid_t;
//TODO
//TODO IEs between 8.22 and 8.28 missing

@ -410,5 +410,36 @@ struct gtpc_delete_session_response
//Private extension
};
/****************************************************************************
*
* GTP-C v2 Release Access Bearers Request
* Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.21.1-1
*
***************************************************************************/
struct gtpc_release_access_bearers_request
{
bool list_of_rabs_present;
//Linked EPS Bearer ID
bool originating_node_present;
//Indication Flags
//Private Extension
};
/****************************************************************************
*
* GTP-C v2 Delete Session Response
* Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.22.1-1
*
***************************************************************************/
struct gtpc_release_access_bearers_response
{
struct gtpc_cause_ie cause;
//Recovery
//Private extension
};
} //namespace
#endif //GTPC_V2_MSG_H

@ -175,6 +175,7 @@ typedef struct{
uint8 imsi[15];
uint8 imei[15];
uint8 imeisv[16];
uint32 tmsi;
}LIBLTE_MME_MOBILE_ID_STRUCT;
// Functions
LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_id_ie(LIBLTE_MME_MOBILE_ID_STRUCT *mobile_id,

@ -296,9 +296,10 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_id_ie(LIBLTE_MME_MOBILE_ID_STRUCT *mob
uint8 **ie_ptr)
{
LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS;
uint8 *id;
uint8 *id = NULL;
uint32 id32 = 0;
uint32 i;
uint8 length;
uint8 length = 0;
bool odd = false;
if(mobile_id != NULL &&
@ -317,6 +318,11 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_id_ie(LIBLTE_MME_MOBILE_ID_STRUCT *mob
id = mobile_id->imeisv;
length = 9;
odd = false;
}else if(LIBLTE_MME_MOBILE_ID_TYPE_TMSI == mobile_id->type_of_id){
id32 = mobile_id->tmsi;
length = 4;
odd = false;
}
}else{
// FIXME: Not handling these IDs
return(err);
@ -325,7 +331,8 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_id_ie(LIBLTE_MME_MOBILE_ID_STRUCT *mob
// Length
**ie_ptr = length;
*ie_ptr += 1;
if(LIBLTE_MME_MOBILE_ID_TYPE_TMSI != mobile_id->type_of_id)
{
// | Identity digit 1 | odd/even | Id type |
if(odd)
{
@ -335,6 +342,7 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_id_ie(LIBLTE_MME_MOBILE_ID_STRUCT *mob
}
*ie_ptr += 1;
// | Identity digit p+1 | Identity digit p |
for(i=0; i<7; i++)
{
@ -349,6 +357,22 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_id_ie(LIBLTE_MME_MOBILE_ID_STRUCT *mob
err = LIBLTE_SUCCESS;
}
else{
**ie_ptr = (0xFF << 4) | (0 << 3) | mobile_id->type_of_id;
*ie_ptr += 1;
//4-Byte based ids
**ie_ptr = (id32 >> 24) & 0xFF;
*ie_ptr += 1;
**ie_ptr = (id32 >> 16) & 0xFF;
*ie_ptr += 1;
**ie_ptr = (id32 >> 8) & 0xFF;
*ie_ptr += 1;
**ie_ptr = id32 & 0xFF;
*ie_ptr += 1;
err = LIBLTE_SUCCESS;
}
return(err);
}

@ -32,26 +32,27 @@
namespace srsenb {
enb* enb::instance = NULL;
boost::mutex enb_instance_mutex;
pthread_mutex_t enb_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
enb* enb::get_instance(void)
{
boost::mutex::scoped_lock lock(enb_instance_mutex);
pthread_mutex_lock(&enb_instance_mutex);
if(NULL == instance) {
instance = new enb();
}
pthread_mutex_unlock(&enb_instance_mutex);
return(instance);
}
void enb::cleanup(void)
{
srslte_dft_exit();
srslte::byte_buffer_pool::cleanup();
boost::mutex::scoped_lock lock(enb_instance_mutex);
pthread_mutex_lock(&enb_instance_mutex);
if(NULL != instance) {
delete instance;
instance = NULL;
}
pthread_mutex_unlock(&enb_instance_mutex);
}
enb::enb() : started(false) {

@ -12,6 +12,7 @@
# mnc: Mobile Network Code
# mme_bindx_addr: IP subnet to listen for eNB S1 connnections
# apn: Set Access Point Name (APN)
# mme_bind_addr: IP bind addr to listen for eNB S1 connnections
#
#####################################################################
[mme]
@ -21,7 +22,7 @@ tac = 0x0007
mcc = 001
mnc = 01
mme_bind_addr = 127.0.1.100
apn = test123
apn = srsapn
#####################################################################
# HSS configuration

@ -42,17 +42,22 @@ class mme_gtpc
{
public:
typedef struct gtpc_ctx{
srslte::gtp_fteid_t mme_ctr_fteid;
srslte::gtp_fteid_t sgw_ctr_fteid;
}gtpc_ctx_t;
static mme_gtpc* get_instance(void);
static void cleanup(void);
bool init(srslte::log_filter *mme_gtpc_log);
uint32_t get_new_ctrl_teid();
void send_create_session_request(uint64_t imsi, uint32_t mme_s1ap_id);
void send_create_session_request(uint64_t imsi);
void handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu);
void send_modify_bearer_request(erab_ctx_t *bearer_ctx);
void send_modify_bearer_request(uint64_t imsi, erab_ctx_t *bearer_ctx);
void handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu);
void send_delete_session_request(ue_ctx_t *ue_ctx);
void send_release_access_bearers_request(uint64_t imsi);
void send_delete_session_request(uint64_t imsi);
private:
@ -68,7 +73,8 @@ private:
in_addr_t m_mme_gtpc_ip;
uint32_t m_next_ctrl_teid;
std::map<uint32_t,uint32_t> m_teid_to_mme_s1ap_id;
std::map<uint32_t,uint64_t> m_mme_ctr_teid_to_imsi;
std::map<uint64_t,struct gtpc_ctx> m_imsi_to_gtpc_ctx;
};

@ -65,13 +65,12 @@ public:
int get_s1_mme();
void delete_enb_ctx(int32_t assoc_id);
void delete_ues_in_enb(uint16_t enb_id);
bool handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu, struct sctp_sndrcvinfo *enb_sri);
bool handle_initiating_message(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg, struct sctp_sndrcvinfo *enb_sri);
bool handle_successful_outcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg);
void activate_eps_bearer(uint32_t mme_s1ap_id, uint8_t ebi);
void activate_eps_bearer(uint64_t imsi, uint8_t ebi);
void print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx);
@ -79,11 +78,32 @@ public:
uint32_t get_next_mme_ue_s1ap_id();
enb_ctx_t* find_enb_ctx(uint16_t enb_id);
void add_new_enb_ctx(const enb_ctx_t &enb_ctx, const struct sctp_sndrcvinfo* enb_sri);
ue_ctx_t* find_ue_ctx(uint32_t mme_ue_s1ap_id);
void add_new_ue_ctx(const ue_ctx_t &ue_ctx);
bool delete_ue_ctx(ue_ctx_t *ue_ctx);
uint32_t allocate_m_tmsi(uint32_t mme_ue_s1ap_id);
bool add_ue_ctx_to_imsi_map(ue_ctx_t *ue_ctx);
bool add_ue_ctx_to_mme_ue_s1ap_id_map(ue_ctx_t *ue_ctx);
ue_ctx_t* find_ue_ctx_from_imsi(uint64_t imsi);
ue_ctx_t* find_ue_ctx_from_mme_ue_s1ap_id(uint32_t mme_ue_s1ap_id);
bool release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id);
void release_ues_ecm_ctx_in_enb(uint16_t enb_id);
bool delete_ue_ctx(uint64_t imsi);
//ue_ctx_t* find_ue_ctx(uint32_t mme_ue_s1ap_id);
//void add_new_ue_ctx(const ue_ctx_t &ue_ctx);
//void add_new_ue_emm_ctx(const ue_emm_ctx_t &ue_emm_ctx);
//void add_new_ue_ecm_ctx(const ue_ecm_ctx_t &ue_ecm_ctx);
//ue_emm_ctx_t* find_ue_emm_ctx_from_imsi(uint64_t imsi);
//ue_ecm_ctx_t* find_ue_ecm_ctx_from_mme_ue_s1ap_id(uint32_t mme_ue_s1ap_id);
//bool delete_ue_emm_ctx(uint64_t imsi);
//bool delete_ue_ecm_ctx(uint32_t mme_ue_s1ap_id);
//void delete_ues_ecm_ctx_in_enb(uint16_t enb_id);
//void store_tmp_ue_emm_ctx(const ue_emm_ctx_t &ue_ecm_ctx);
//bool get_tmp_ue_emm_ctx(uint32_t mme_ue_s1ap_id, ue_emm_ctx_t* ue_emm_ptr);
uint32_t allocate_m_tmsi(uint64_t imsi);
s1ap_args_t m_s1ap_args;
srslte::log_filter *m_s1ap_log;
@ -92,7 +112,7 @@ public:
s1ap_nas_transport* m_s1ap_nas_transport;
s1ap_ctx_mngmt_proc* m_s1ap_ctx_mngmt_proc;
std::map<uint32_t, uint32_t> m_tmsi_to_s1ap_id;
std::map<uint32_t, uint64_t> m_tmsi_to_imsi;
private:
s1ap();
@ -107,12 +127,20 @@ private:
int m_s1mme;
std::map<uint16_t, enb_ctx_t*> m_active_enbs;
std::map<int32_t, uint16_t> m_sctp_to_enb_id;
std::map<uint32_t, ue_ctx_t*> m_active_ues;
std::map<uint16_t,std::set<uint32_t> > m_enb_id_to_ue_ids;
std::map<uint64_t, ue_ctx_t*> m_imsi_to_ue_ctx;
std::map<uint32_t, ue_ctx_t*> m_mme_ue_s1ap_id_to_ue_ctx;
//std::map<uint64_t, ue_emm_ctx_t*> m_imsi_to_ue_emm_ctx;
//std::map<uint32_t, ue_ecm_ctx_t*> m_mme_ue_s1ap_id_to_ue_ecm_ctx;
//std::map<int32_t,ue_emm_ctx_t*> m_mme_ue_s1ap_id_to_tmp_ue_emm_ctx;
uint32_t m_next_mme_ue_s1ap_id;
uint32_t m_next_m_tmsi;
//FIXME the GTP-C should be moved to the MME class, the the packaging of GTP-C messages is done.
//FIXME the GTP-C should be moved to the MME class, when the packaging of GTP-C messages is done.
mme_gtpc *m_mme_gtpc;
};

@ -33,6 +33,47 @@ static const uint8_t MAX_TA=255; //Maximum TA supported
static const uint8_t MAX_BPLMN=6; //Maximum broadcasted PLMNs per TAC
static const uint8_t MAX_ERABS_PER_UE = 16;
// MME EMM states (3GPP 24.301 v10.0.0, section 5.1.3.4)
typedef enum {
EMM_STATE_DEREGISTERED = 0,
EMM_STATE_COMMON_PROCEDURE_INITIATED,
EMM_STATE_REGISTERED,
EMM_STATE_DEREGISTERED_INITIATED,
EMM_STATE_N_ITEMS,
} emm_state_t;
static const char emm_state_text[EMM_STATE_N_ITEMS][100] = {"DEREGISTERED",
"COMMON PROCEDURE INITIATED",
"REGISTERED",
"DEREGISTERED INITIATED"};
// MME ECM states (3GPP 23.401 v10.0.0, section 4.6.3)
typedef enum {
ECM_STATE_IDLE = 0,
ECM_STATE_CONNECTED,
ECM_STATE_N_ITEMS,
} ecm_state_t;
static const char ecm_state_text[ECM_STATE_N_ITEMS][100] = {"IDLE",
"CONNECTED"};
// MME ESM states (3GPP 23.401 v10.0.0, section 4.6.3)
typedef enum {
ESM_BEARER_CONTEXT_INACTIVE = 0,
ESM_BEARER_CONTEXT_ACTIVE_PENDING,
ESM_BEARER_CONTEXT_ACTIVE,
ESM_BEARER_CONTEXT_INACTIVE_PENDING,
ESM_BEARER_CONTEXT_MODIFY_PENDING,
ESM_BEARER_PROCEDURE_TRANSACTION_INACTIVE,
ESM_BEARER_PROCEDURE_TRANSACTION_PENDING,
ESM_STATE_N_ITEMS,
} esm_state_t;
static const char esm_state_text[ESM_STATE_N_ITEMS][100] = {"CONTEXT INACTIVE",
"CONTEXT ACTIVE PENDING",
"CONTEXT ACTIVE",
"CONTEXT_INACTIVE_PENDING",
"CONTEXT_MODIFY_PENDING",
"PROCEDURE_TRANSACTION_INACTIVE"
"PROCEDURE_TRANSACTION_PENDING"};
enum erab_state
{
ERAB_DEACTIVATED,
@ -41,6 +82,7 @@ enum erab_state
ERAB_ACTIVE
};
typedef struct{
uint8_t mme_code;
uint16_t mme_group;
@ -49,6 +91,7 @@ typedef struct{
uint16_t mnc; // BCD-coded with 0xF filler
std::string mme_bind_addr;
std::string mme_name;
std::string dns_addr;
std::string mme_apn;
} s1ap_args_t;
@ -75,29 +118,48 @@ typedef struct{
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo;
uint8_t k_nas_enc[32];
uint8_t k_nas_int[32];
} eps_security_ctx_t;
uint8_t k_enb[32];
LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT ue_network_cap;
bool ms_network_cap_present;
LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT ms_network_cap;
} eps_sec_ctx_t;
typedef struct{
enum erab_state state;
uint8_t erab_id;
srslte::gtpc_f_teid_ie enb_fteid;
srslte::gtpc_f_teid_ie sgw_ctrl_fteid;
srslte::gtpc_f_teid_ie sgw_s1u_fteid;
srslte::gtpc_pdn_address_allocation_ie pdn_addr_alloc;
} erab_ctx_t;
typedef struct{
uint64_t imsi;
LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti;
eps_sec_ctx_t security_ctxt;
uint8_t procedure_transaction_id;
emm_state_t state;
uint32_t mme_ue_s1ap_id;
uint8_t attach_type;
struct in_addr ue_ip;
srslte::gtpc_f_teid_ie sgw_ctrl_fteid;
} ue_emm_ctx_t;
typedef struct{
uint64_t imsi;
uint32_t enb_ue_s1ap_id;
uint32_t mme_ue_s1ap_id;
uint16_t enb_id;
struct sctp_sndrcvinfo enb_sri;
eps_security_ctx_t security_ctxt;
ecm_state_t state;
erab_ctx_t erabs_ctx[MAX_ERABS_PER_UE];
LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT ue_network_cap;
bool ms_network_cap_present;
LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT ms_network_cap;
bool eit;
uint8_t procedure_transaction_id;
uint8_t attach_type;
} ue_ecm_ctx_t;
typedef struct{
ue_emm_ctx_t emm_ctx;
eps_sec_ctx_t sec_ctx;
ue_ecm_ctx_t ecm_ctx;
erab_ctx_t erabs_ctx[MAX_ERABS_PER_UE];
} ue_ctx_t;
}//namespace
#endif

@ -47,9 +47,12 @@ public:
void init(void);
bool send_initial_context_setup_request(uint32_t mme_ue_s1ap_id, struct srslte::gtpc_create_session_response *cs_resp, struct srslte::gtpc_f_teid_ie sgw_ctrl_fteid);
//bool send_initial_context_setup_request(uint32_t mme_ue_s1ap_id, struct srslte::gtpc_create_session_response *cs_resp, struct srslte::gtpc_f_teid_ie sgw_ctrl_fteid);
bool send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, ue_ecm_ctx_t *ecm_ctx, erab_ctx_t *erab_ctx);
bool handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *in_ctxt_resp);
bool handle_ue_context_release_request(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *ue_rel, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag);
bool send_ue_context_release_command(ue_ecm_ctx_t *ecm_ctx, srslte::byte_buffer_t *reply_buffer);
bool handle_ue_context_release_complete(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *rel_comp);
private:
s1ap_ctx_mngmt_proc();

@ -47,9 +47,9 @@ public:
bool handle_initial_ue_message(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *init_ue, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag);
bool handle_uplink_nas_transport(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag);
//Initial UE messages
bool handle_nas_attach_request( uint32_t enb_ue_s1ap_id,
const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req,
const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req,
srslte::byte_buffer_t *nas_msg,
srslte::byte_buffer_t *reply_buffer,
bool* reply_flag,
struct sctp_sndrcvinfo *enb_sri);
@ -62,28 +62,48 @@ public:
bool handle_nas_guti_attach_request(uint32_t enb_ue_s1ap_id,
const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req,
const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req,
srslte::byte_buffer_t *nas_msg,
srslte::byte_buffer_t *reply_buffer,
bool* reply_flag,
struct sctp_sndrcvinfo *enb_sri);
bool handle_nas_service_request(uint32_t m_tmsi,
uint32_t enb_ue_s1ap_id,
srslte::byte_buffer_t *nas_msg,
srslte::byte_buffer_t *reply_buffer,
bool* reply_flag,
struct sctp_sndrcvinfo *enb_sri);
bool handle_nas_detach_request(uint32_t m_tmsi,
uint32_t enb_ue_s1ap_id,
srslte::byte_buffer_t *nas_msg,
srslte::byte_buffer_t *reply_buffer,
bool* reply_flag,
struct sctp_sndrcvinfo *enb_sri);
bool handle_nas_authentication_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool* reply_flag);
bool handle_nas_security_mode_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag);
bool handle_nas_attach_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag);
bool handle_esm_information_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag);
bool handle_identity_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag);
bool handle_tracking_area_update_request(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag);
bool handle_authentication_failure(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag);
bool handle_authentication_failure(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag);
bool handle_nas_detach_request(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag);
bool integrity_check(ue_emm_ctx_t *emm_ctx, srslte::byte_buffer_t *pdu);
bool short_integrity_check(ue_emm_ctx_t *emm_ctx, srslte::byte_buffer_t *pdu);
bool pack_authentication_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t next_mme_ue_s1ap_id, uint8_t *autn,uint8_t *rand);
bool pack_authentication_reject(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id);
bool unpack_authentication_response(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp);
bool pack_security_mode_command(srslte::byte_buffer_t *reply_msg, ue_ctx_t *ue_ctx);
bool pack_esm_information_request(srslte::byte_buffer_t *reply_msg, ue_ctx_t *ue_ctx);
bool pack_security_mode_command(srslte::byte_buffer_t *reply_msg, ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx);
bool pack_esm_information_request(srslte::byte_buffer_t *reply_msg, ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx);
bool pack_attach_accept(ue_ctx_t *ue_ctx, LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctxt, struct srslte::gtpc_pdn_address_allocation_ie *paa, srslte::byte_buffer_t *nas_buffer);
bool pack_attach_accept(ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx, LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctxt, struct srslte::gtpc_pdn_address_allocation_ie *paa, srslte::byte_buffer_t *nas_buffer);
bool pack_identity_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id);
bool pack_emm_information(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id);
bool pack_emm_information(ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg);
bool pack_service_reject(srslte::byte_buffer_t *reply_msg, uint8_t emm_cause, uint32_t enb_ue_s1ap_id);
void log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req);
@ -102,7 +122,5 @@ private:
hss_interface_s1ap* m_hss;
mme_gtpc* m_mme_gtpc;
};
} //namespace srsepc
#endif //S1AP_NAS_TRANSPORT

@ -76,6 +76,7 @@ public:
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);
void handle_sgi_pdu(srslte::byte_buffer_t *msg);
void handle_s1u_pdu(srslte::byte_buffer_t *msg);
@ -94,6 +95,10 @@ private:
uint64_t get_new_user_teid();
in_addr_t get_new_ue_ipv4();
spgw_tunnel_ctx_t* create_gtp_ctx(struct srslte::gtpc_create_session_request *cs_req);
bool delete_gtp_ctx(uint32_t ctrl_teid);
bool m_running;
srslte::byte_buffer_pool *m_pool;
mme_gtpc *m_mme_gtpc;
@ -112,11 +117,17 @@ private:
sockaddr_in m_s1u_addr;
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;
/*Time*/
struct timeval m_t_last_dl;
struct timeval m_t_last_ul;
/*Logs*/
srslte::log_filter *m_spgw_log;

@ -37,7 +37,7 @@ using namespace srslte;
namespace srsepc{
hss* hss::m_instance = NULL;
boost::mutex hss_instance_mutex;
pthread_mutex_t hss_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
hss::hss()
{
@ -53,21 +53,23 @@ hss::~hss()
hss*
hss::get_instance(void)
{
boost::mutex::scoped_lock lock(hss_instance_mutex);
pthread_mutex_lock(&hss_instance_mutex);
if(NULL == m_instance) {
m_instance = new hss();
}
pthread_mutex_unlock(&hss_instance_mutex);
return(m_instance);
}
void
hss::cleanup(void)
{
boost::mutex::scoped_lock lock(hss_instance_mutex);
pthread_mutex_lock(&hss_instance_mutex);
if(NULL != m_instance) {
delete m_instance;
m_instance = NULL;
}
pthread_mutex_unlock(&hss_instance_mutex);
}
int

@ -52,7 +52,6 @@ typedef struct {
int spgw_hex_limit;
std::string hss_level;
int hss_hex_limit;
std::string all_level;
int all_hex_limit;
std::string filename;
@ -84,6 +83,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) {
string mme_apn;
string spgw_bind_addr;
string sgi_if_addr;
string dns_addr;
string hss_db_file;
string hss_auth_algo;
string log_filename;
@ -106,6 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) {
("mme.mcc", bpo::value<string>(&mcc)->default_value("001"), "Mobile Country Code")
("mme.mnc", bpo::value<string>(&mnc)->default_value("01"), "Mobile Network Code")
("mme.mme_bind_addr", bpo::value<string>(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection")
("mme.dns_addr", bpo::value<string>(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs")
("mme.apn", bpo::value<string>(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services")
("hss.db_file", bpo::value<string>(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys")
("hss.auth_algo", bpo::value<string>(&hss_auth_algo)->default_value("milenage"),"HSS uthentication algorithm.")
@ -206,6 +207,8 @@ parse_args(all_args_t *args, int argc, char* argv[]) {
}
args->mme_args.s1ap_args.mme_bind_addr = mme_bind_addr;
args->mme_args.s1ap_args.mme_name = mme_name;
args->mme_args.s1ap_args.dns_addr = dns_addr;
args->mme_args.s1ap_args.mme_apn = mme_apn;
args->spgw_args.gtpu_bind_addr = spgw_bind_addr;
args->spgw_args.sgi_if_addr = sgi_if_addr;

@ -29,13 +29,12 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/sctp.h>
#include <boost/thread/mutex.hpp>
#include "mme/mme.h"
namespace srsepc{
mme* mme::m_instance = NULL;
boost::mutex mme_instance_mutex;
pthread_mutex_t mme_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
mme::mme():
m_running(false)
@ -52,21 +51,23 @@ mme::~mme()
mme*
mme::get_instance(void)
{
boost::mutex::scoped_lock lock(mme_instance_mutex);
pthread_mutex_lock(&mme_instance_mutex);
if(NULL == m_instance) {
m_instance = new mme();
}
pthread_mutex_unlock(&mme_instance_mutex);
return(m_instance);
}
void
mme::cleanup(void)
{
boost::mutex::scoped_lock lock(mme_instance_mutex);
pthread_mutex_lock(&mme_instance_mutex);
if(NULL != m_instance) {
delete m_instance;
m_instance = NULL;
}
pthread_mutex_unlock(&mme_instance_mutex);
}
int

@ -33,7 +33,8 @@
namespace srsepc{
mme_gtpc* mme_gtpc::m_instance = NULL;
boost::mutex mme_gtpc_instance_mutex;
pthread_mutex_t mme_gtpc_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
mme_gtpc::mme_gtpc()
{
@ -46,21 +47,23 @@ mme_gtpc::~mme_gtpc()
mme_gtpc*
mme_gtpc::get_instance(void)
{
boost::mutex::scoped_lock lock(mme_gtpc_instance_mutex);
pthread_mutex_lock(&mme_gtpc_instance_mutex);
if(NULL == m_instance) {
m_instance = new mme_gtpc();
}
pthread_mutex_unlock(&mme_gtpc_instance_mutex);
return(m_instance);
}
void
mme_gtpc::cleanup(void)
{
boost::mutex::scoped_lock lock(mme_gtpc_instance_mutex);
pthread_mutex_lock(&mme_gtpc_instance_mutex);
if(NULL != m_instance) {
delete m_instance;
m_instance = NULL;
}
pthread_mutex_unlock(&mme_gtpc_instance_mutex);
}
@ -88,7 +91,7 @@ mme_gtpc::get_new_ctrl_teid()
return m_next_ctrl_teid++; //FIXME Use a Id pool?
}
void
mme_gtpc::send_create_session_request(uint64_t imsi, uint32_t mme_ue_s1ap_id)
mme_gtpc::send_create_session_request(uint64_t imsi)
{
m_mme_gtpc_log->info("Sending Create Session Request.\n");
m_mme_gtpc_log->console("Sending Create Session Request.\n");
@ -97,7 +100,6 @@ mme_gtpc::send_create_session_request(uint64_t imsi, uint32_t mme_ue_s1ap_id)
struct srslte::gtpc_pdu cs_resp_pdu;
//Initialize GTP-C message to zero
bzero(&cs_req_pdu, sizeof(struct srslte::gtpc_pdu));
@ -113,19 +115,45 @@ mme_gtpc::send_create_session_request(uint64_t imsi, uint32_t mme_ue_s1ap_id)
cs_req->sender_f_teid.teid = get_new_ctrl_teid();
cs_req->sender_f_teid.ipv4 = m_mme_gtpc_ip;
m_mme_gtpc_log->info("Next control TEID: %lu \n", m_next_ctrl_teid);
m_mme_gtpc_log->info("Allocated control TEID: %lu \n", cs_req->sender_f_teid.teid);
m_mme_gtpc_log->info("Next MME control TEID: %lu \n", m_next_ctrl_teid);
m_mme_gtpc_log->info("Allocated MME control TEID: %lu \n", cs_req->sender_f_teid.teid);
m_mme_gtpc_log->console("Creating Session Response -- IMSI: %015lu \n", imsi);
m_mme_gtpc_log->console("Creating Session Response -- MME control TEID: %lu \n", cs_req->sender_f_teid.teid);
// APN
strncpy(cs_req->apn, m_s1ap->m_s1ap_args.mme_apn.c_str(), sizeof(cs_req->apn)-1);
cs_req->apn[sizeof(cs_req->apn)-1] = 0;
// RAT Type
//cs_req->rat_type = srslte::GTPC_RAT_TYPE::EUTRAN;
//Check whether this UE is already registed
std::map<uint64_t, struct gtpc_ctx>::iterator it = m_imsi_to_gtpc_ctx.find(imsi);
if(it != m_imsi_to_gtpc_ctx.end())
{
m_mme_gtpc_log->warning("Create Session Request being called for an UE with an active GTP-C connection.\n");
m_mme_gtpc_log->warning("Deleting previous GTP-C connection.\n");
std::map<uint32_t, uint64_t>::iterator jt = m_mme_ctr_teid_to_imsi.find(it->second.mme_ctr_fteid.teid);
if(jt == m_mme_ctr_teid_to_imsi.end())
{
m_mme_gtpc_log->error("Could not find IMSI from MME Ctrl TEID. MME Ctr TEID: %d\n", it->second.mme_ctr_fteid.teid);
}
else
{
m_mme_ctr_teid_to_imsi.erase(jt);
}
m_imsi_to_gtpc_ctx.erase(it);
//No need to send delete session request to the SPGW.
//The create session request will be interpreted as a new request and SPGW will delete locally in existing context.
}
//Save RX Control TEID
m_teid_to_mme_s1ap_id.insert(std::pair<uint32_t,uint32_t>(cs_req->sender_f_teid.teid, mme_ue_s1ap_id));
m_mme_ctr_teid_to_imsi.insert(std::pair<uint32_t,uint64_t>(cs_req->sender_f_teid.teid, imsi));
//Save GTP-C context
gtpc_ctx_t gtpc_ctx;
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);
}
@ -139,7 +167,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu)
if (cs_resp_pdu->header.type != srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE)
{
m_mme_gtpc_log->warning("Could not create GTPC session. Not a create session response\n");
//TODO Handle err
//TODO Handle error
return;
}
if (cs_resp->cause.cause_value != srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED){
@ -148,40 +176,99 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu)
return;
}
//Get MME_UE_S1AP_ID from the control TEID
std::map<uint32_t,uint32_t>::iterator id_it = m_teid_to_mme_s1ap_id.find(cs_resp_pdu->header.teid);
if(id_it == m_teid_to_mme_s1ap_id.end())
//Get IMSI from the control TEID
std::map<uint32_t,uint64_t>::iterator id_it = m_mme_ctr_teid_to_imsi.find(cs_resp_pdu->header.teid);
if(id_it == m_mme_ctr_teid_to_imsi.end())
{
//Could not find MME UE S1AP TEID
m_mme_gtpc_log->warning("Could not find MME UE S1AP TEID.\n");
m_mme_gtpc_log->warning("Could not find IMSI from Ctrl TEID.\n");
return;
}
uint32_t mme_s1ap_id = id_it->second;
uint64_t imsi = id_it->second;
m_mme_gtpc_log->info("MME GTPC Ctrl TEID %d, IMSI %d\n", cs_resp_pdu->header.teid, imsi);
//Get S-GW Control F-TEID
srslte::gtpc_f_teid_ie sgw_ctrl_fteid;
sgw_ctrl_fteid.teid = cs_resp_pdu->header.teid;
sgw_ctrl_fteid.ipv4 = 0; //FIXME This is not used for now. In the future it will be obtained from the socket addr_info
srslte::gtp_fteid_t sgw_ctr_fteid;
sgw_ctr_fteid.teid = cs_resp_pdu->header.teid;
sgw_ctr_fteid.ipv4 = 0; //FIXME This is not used for now. In the future it will be obtained from the socket addr_info
m_mme_gtpc_log->console("Create Session Response -- SPGW control TEID %d\n", sgw_ctrl_fteid.teid);
//Get S-GW S1-u F-TEID
if (cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present == false){
m_mme_gtpc_log->error("Did not receive SGW S1-U F-TEID in create session response\n");
return;
}
m_mme_gtpc_log->console("Create Session Response -- SPGW control TEID %d\n", sgw_ctr_fteid.teid);
m_mme_gtpc_log->info("Create Session Response -- SPGW control TEID %d\n", sgw_ctr_fteid.teid);
in_addr s1u_addr;
s1u_addr.s_addr = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4;
m_mme_gtpc_log->console("Create Session Response -- SPGW S1-U Address: %s\n", inet_ntoa(s1u_addr));
m_s1ap->m_s1ap_ctx_mngmt_proc->send_initial_context_setup_request(mme_s1ap_id, cs_resp, sgw_ctrl_fteid);
m_mme_gtpc_log->info("Create Session Response -- SPGW S1-U Address: %s\n", inet_ntoa(s1u_addr));
//Check UE Ipv4 address was allocated
if(cs_resp->paa_present != true)
{
m_mme_gtpc_log->error("PDN Adress Allocation not present\n");
return;
}
if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4)
{
m_mme_gtpc_log->error("IPv6 not supported yet\n");
return;
}
//Save create session response info to E-RAB context
ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_imsi(imsi);
if(ue_ctx == NULL){
m_mme_gtpc_log->error("Could not find UE context. IMSI %015lu\n", imsi);
return;
}
ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx;
ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx;
//Save UE IP to nas ctxt
emm_ctx->ue_ip.s_addr = cs_resp->paa.ipv4;
m_mme_gtpc_log->console("SPGW Allocated IP %s to ISMI %015lu\n",inet_ntoa(emm_ctx->ue_ip),emm_ctx->imsi);
//Save SGW ctrl F-TEID in GTP-C context
std::map<uint64_t,struct gtpc_ctx>::iterator it_g = m_imsi_to_gtpc_ctx.find(imsi);
if(it_g == m_imsi_to_gtpc_ctx.end())
{
//Could not find GTP-C Context
m_mme_gtpc_log->error("Could not find GTP-C context\n");
return;
}
gtpc_ctx_t *gtpc_ctx = &it_g->second;
gtpc_ctx->sgw_ctr_fteid = sgw_ctr_fteid;
//Set EPS bearer context
//FIXME default EPS bearer is hard-coded
int default_bearer=5;
erab_ctx_t *erab_ctx = &ecm_ctx->erabs_ctx[default_bearer];
erab_ctx->pdn_addr_alloc= cs_resp->paa;
erab_ctx->sgw_s1u_fteid = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid;
m_s1ap->m_s1ap_ctx_mngmt_proc->send_initial_context_setup_request(emm_ctx, ecm_ctx, erab_ctx);
return;
}
void
mme_gtpc::send_modify_bearer_request(erab_ctx_t *erab_ctx)
mme_gtpc::send_modify_bearer_request(uint64_t imsi, erab_ctx_t *erab_ctx)
{
m_mme_gtpc_log->info("Sending GTP-C Modify bearer request\n");
srslte::gtpc_pdu mb_req_pdu;
srslte::gtpc_f_teid_ie *enb_fteid = &erab_ctx->enb_fteid;
srslte::gtpc_f_teid_ie *sgw_ctrl_fteid = &erab_ctx->sgw_ctrl_fteid;
srslte::gtp_fteid_t *enb_fteid = &erab_ctx->enb_fteid;
std::map<uint64_t,gtpc_ctx_t>::iterator it = m_imsi_to_gtpc_ctx.find(imsi);
if(it == m_imsi_to_gtpc_ctx.end())
{
m_mme_gtpc_log->error("Modify bearer request for UE without GTP-C connection\n");
return;
}
srslte::gtp_fteid_t sgw_ctr_fteid = it->second.sgw_ctr_fteid;
srslte::gtpc_header *header = &mb_req_pdu.header;
header->teid_present = true;
header->teid = sgw_ctrl_fteid->teid;
header->teid = sgw_ctr_fteid.teid;
header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST;
srslte::gtpc_modify_bearer_request *mb_req = &mb_req_pdu.choice.modify_bearer_request;
@ -189,7 +276,7 @@ mme_gtpc::send_modify_bearer_request(erab_ctx_t *erab_ctx)
mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.ipv4 = enb_fteid->ipv4;
mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.teid = enb_fteid->teid;
m_mme_gtpc_log->info("GTP-C Modify bearer request -- S-GW Control TEID %d\n", sgw_ctrl_fteid->teid );
m_mme_gtpc_log->info("GTP-C Modify bearer request -- S-GW Control TEID %d\n", sgw_ctr_fteid.teid );
struct in_addr addr;
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) );
@ -205,54 +292,93 @@ void
mme_gtpc::handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu)
{
uint32_t mme_ctrl_teid = mb_resp_pdu->header.teid;
std::map<uint32_t,uint32_t>::iterator mme_s1ap_id_it = m_teid_to_mme_s1ap_id.find(mme_ctrl_teid);
if(mme_s1ap_id_it == m_teid_to_mme_s1ap_id.end())
std::map<uint32_t,uint64_t>::iterator imsi_it = m_mme_ctr_teid_to_imsi.find(mme_ctrl_teid);
if(imsi_it == m_mme_ctr_teid_to_imsi.end())
{
m_mme_gtpc_log->error("Could not find MME S1AP Id from control TEID\n");
m_mme_gtpc_log->error("Could not find IMSI from control TEID\n");
return;
}
uint8_t ebi = mb_resp_pdu->choice.modify_bearer_response.eps_bearer_context_modified.ebi;
m_mme_gtpc_log->debug("Activating EPS bearer with id %d\n", ebi);
m_s1ap->activate_eps_bearer(mme_s1ap_id_it->second,ebi);
m_s1ap->activate_eps_bearer(imsi_it->second,ebi);
return;
}
void
mme_gtpc::send_delete_session_request(ue_ctx_t *ue_ctx)
mme_gtpc::send_delete_session_request(uint64_t imsi)
{
m_mme_gtpc_log->info("Sending GTP-C Delete Session Request request\n");
m_mme_gtpc_log->info("Sending GTP-C Delete Session Request request. IMSI %d\n",imsi);
srslte::gtpc_pdu del_req_pdu;
srslte::gtpc_f_teid_ie *sgw_ctrl_fteid = NULL;
//FIXME the UE control TEID sould be stored in the UE ctxt, not in the E-RAB ctxt
//Maybe a mme_s1ap_id to ctrl teid map as well?
for(int i = 0; i<MAX_ERABS_PER_UE; i++)
srslte::gtp_fteid_t sgw_ctr_fteid;
srslte::gtp_fteid_t mme_ctr_fteid;
//Get S-GW Ctr TEID
std::map<uint64_t,gtpc_ctx_t>::iterator it_ctx = m_imsi_to_gtpc_ctx.find(imsi);
if(it_ctx == m_imsi_to_gtpc_ctx.end())
{
if(ue_ctx->erabs_ctx[i].state != ERAB_DEACTIVATED)
{
sgw_ctrl_fteid = &ue_ctx->erabs_ctx[i].sgw_ctrl_fteid;
break;
}
m_mme_gtpc_log->error("Could not find GTP-C context to remove\n");
return;
}
//FIXME: add proper error handling
assert(sgw_ctrl_fteid != NULL);
sgw_ctr_fteid = it_ctx->second.sgw_ctr_fteid;
mme_ctr_fteid = it_ctx->second.mme_ctr_fteid;
srslte::gtpc_header *header = &del_req_pdu.header;
header->teid_present = true;
header->teid = sgw_ctrl_fteid->teid;
header->teid = sgw_ctr_fteid.teid;
header->type = srslte::GTPC_MSG_TYPE_DELETE_SESSION_REQUEST;
srslte::gtpc_delete_session_request *del_req = &del_req_pdu.choice.delete_session_request;
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_ctrl_fteid->teid );
m_mme_gtpc_log->info("GTP-C Delete Session Request -- S-GW Control TEID %d\n", sgw_ctr_fteid.teid );
srslte::gtpc_pdu del_resp_pdu;
m_spgw->handle_delete_session_request(&del_req_pdu, &del_resp_pdu);
//TODO Handle delete session response
//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())
{
m_mme_gtpc_log->error("Could not find IMSI from MME ctr TEID");
}
else
{
m_mme_ctr_teid_to_imsi.erase(it_imsi);
}
m_imsi_to_gtpc_ctx.erase(it_ctx);
return;
}
void
mme_gtpc::send_release_access_bearers_request(uint64_t imsi)
{
m_mme_gtpc_log->info("Sending GTP-C Delete Access Bearers Request\n");
srslte::gtpc_pdu rel_req_pdu;
srslte::gtp_fteid_t sgw_ctr_fteid;
//Get S-GW Ctr TEID
std::map<uint64_t,gtpc_ctx_t>::iterator it_ctx = m_imsi_to_gtpc_ctx.find(imsi);
if(it_ctx == m_imsi_to_gtpc_ctx.end())
{
m_mme_gtpc_log->error("Could not find GTP-C context to remove\n");
return;
}
sgw_ctr_fteid = it_ctx->second.sgw_ctr_fteid;
//Set GTP-C header
srslte::gtpc_header *header = &rel_req_pdu.header;
header->teid_present = true;
header->teid = sgw_ctr_fteid.teid;
header->type = srslte::GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST;
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 );
srslte::gtpc_pdu rel_resp_pdu;
m_spgw->handle_release_access_bearers_request(&rel_req_pdu, &rel_resp_pdu);
//The GTP-C connection will not be torn down, just the user plane bearers.
return;
}
} //namespace srsepc

@ -34,7 +34,7 @@
namespace srsepc{
s1ap* s1ap::m_instance = NULL;
boost::mutex s1ap_instance_mutex;
pthread_mutex_t s1ap_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
s1ap::s1ap():
m_s1mme(-1),
@ -51,21 +51,24 @@ s1ap::~s1ap()
s1ap*
s1ap::get_instance(void)
{
boost::mutex::scoped_lock lock(s1ap_instance_mutex);
if(NULL == m_instance) {
pthread_mutex_lock(&s1ap_instance_mutex);
if(m_instance == NULL) {
m_instance = new s1ap();
}
pthread_mutex_unlock(&s1ap_instance_mutex);
return(m_instance);
}
void
s1ap::cleanup(void)
{
boost::mutex::scoped_lock lock(s1ap_instance_mutex);
pthread_mutex_lock(&s1ap_instance_mutex);
if(NULL != m_instance) {
delete m_instance;
m_instance = NULL;
}
pthread_mutex_unlock(&s1ap_instance_mutex);
}
int
@ -75,7 +78,7 @@ s1ap::init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log, hss_interface_s1
m_s1ap_args = s1ap_args;
srslte::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &m_plmn);
m_next_m_tmsi = rand();
m_next_m_tmsi = 0xF000;
//Init log
m_s1ap_log = s1ap_log;
@ -90,10 +93,8 @@ s1ap::init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log, hss_interface_s1
m_s1ap_ctx_mngmt_proc = s1ap_ctx_mngmt_proc::get_instance(); //Context Management Procedures
m_s1ap_ctx_mngmt_proc->init();
//Get pointer to GTP-C class
m_mme_gtpc = mme_gtpc::get_instance();
//Initialize S1-MME
m_s1mme = enb_listen();
@ -107,16 +108,23 @@ s1ap::stop()
if (m_s1mme != -1){
close(m_s1mme);
}
std::map<uint16_t,enb_ctx_t*>::iterator it = m_active_enbs.begin();
while(it!=m_active_enbs.end())
std::map<uint16_t,enb_ctx_t*>::iterator enb_it = m_active_enbs.begin();
while(enb_it!=m_active_enbs.end())
{
m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", it->second->enb_id);
m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", it->second->enb_id);
delete_ues_in_enb(it->second->enb_id);
delete it->second;
m_active_enbs.erase(it++);
m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", enb_it->second->enb_id);
m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", enb_it->second->enb_id);
delete enb_it->second;
m_active_enbs.erase(enb_it++);
}
std::map<uint64_t,ue_ctx_t*>::iterator ue_it = m_imsi_to_ue_ctx.begin();
while(ue_it!=m_imsi_to_ue_ctx.end())
{
m_s1ap_log->info("Deleting UE EMM context. IMSI: %015lu\n", ue_it->first);
m_s1ap_log->console("Deleting UE EMM context. IMSI: %015lu\n", ue_it->first);
delete ue_it->second;
m_imsi_to_ue_ctx.erase(ue_it++);
}
//Cleanup message handlers
s1ap_mngmt_proc::cleanup();
s1ap_nas_transport::cleanup();
@ -124,55 +132,18 @@ s1ap::stop()
return;
}
void
s1ap::delete_enb_ctx(int32_t assoc_id)
int
s1ap::get_s1_mme()
{
std::map<int32_t,uint16_t>::iterator it_assoc = m_sctp_to_enb_id.find(assoc_id);
uint16_t enb_id = it_assoc->second;
std::map<uint16_t,enb_ctx_t*>::iterator it_ctx = m_active_enbs.find(enb_id);
if(it_ctx == m_active_enbs.end() || it_assoc == m_sctp_to_enb_id.end())
{
m_s1ap_log->error("Could not find eNB to delete. Association: %d\n",assoc_id);
return;
}
m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", enb_id);
m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", enb_id);
//Delete connected UEs ctx
delete_ues_in_enb(enb_id);
//Delete eNB
delete it_ctx->second;
m_active_enbs.erase(it_ctx);
m_sctp_to_enb_id.erase(it_assoc);
return;
return m_s1mme;
}
void
s1ap::delete_ues_in_enb(uint16_t enb_id)
uint32_t
s1ap::get_next_mme_ue_s1ap_id()
{
//delete UEs ctx
std::map<uint16_t,std::set<uint32_t> >::iterator ues_in_enb = m_enb_id_to_ue_ids.find(enb_id);
std::set<uint32_t>::iterator ue_id = ues_in_enb->second.begin();
while(ue_id != ues_in_enb->second.end() )
{
std::map<uint32_t, ue_ctx_t*>::iterator ue_ctx = m_active_ues.find(*ue_id);
m_s1ap_log->info("Deleting UE context. UE IMSI: %lu\n", ue_ctx->second->imsi);
m_s1ap_log->console("Deleting UE context. UE IMSI: %lu\n", ue_ctx->second->imsi);
delete ue_ctx->second; //delete UE context
m_active_ues.erase(ue_ctx); //remove from general MME map
ues_in_enb->second.erase(ue_id++); //erase from the eNB's UE set
}
return m_next_mme_ue_s1ap_id++;
}
int
s1ap::get_s1_mme()
{
return m_s1mme;
}
int
s1ap::enb_listen()
@ -282,7 +253,8 @@ s1ap::handle_initiating_message(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg, stru
m_s1ap_ctx_mngmt_proc->handle_ue_context_release_request(&msg->choice.UEContextReleaseRequest, enb_sri, reply_buffer, &reply_flag);
break;
default:
m_s1ap_log->error("Unhandled intiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]);
m_s1ap_log->error("Unhandled S1AP intiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]);
m_s1ap_log->console("Unhandled S1APintiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]);
}
//Send Reply to eNB
if(reply_flag == true)
@ -290,7 +262,8 @@ s1ap::handle_initiating_message(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg, stru
ssize_t n_sent = sctp_send(m_s1mme,reply_buffer->msg, reply_buffer->N_bytes, enb_sri, 0);
if(n_sent == -1)
{
m_s1ap_log->console("Failed to send S1 Setup Setup Reply\n");
m_s1ap_log->console("Failed to send S1AP Initiating Reply.\n");
m_s1ap_log->error("Failed to send S1AP Initiating Reply. \n");
m_pool->deallocate(reply_buffer);
return false;
}
@ -306,53 +279,128 @@ s1ap::handle_successful_outcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg)
case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPRESPONSE:
m_s1ap_log->info("Received Initial Context Setup Response.\n");
return m_s1ap_ctx_mngmt_proc->handle_initial_context_setup_response(&msg->choice.InitialContextSetupResponse);
case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTRELEASECOMPLETE:
m_s1ap_log->info("Received UE Context Release Complete\n");
return m_s1ap_ctx_mngmt_proc->handle_ue_context_release_complete(&msg->choice.UEContextReleaseComplete);
default:
m_s1ap_log->error("Unhandled successful outcome message: %s\n", liblte_s1ap_successfuloutcome_choice_text[msg->choice_type]);
}
return true;
}
//eNB Context Managment
void
s1ap::add_new_enb_ctx(const enb_ctx_t &enb_ctx, const struct sctp_sndrcvinfo *enb_sri)
{
m_s1ap_log->info("Adding new eNB context. eNB ID %d\n", enb_ctx.enb_id);
std::set<uint32_t> ue_set;
enb_ctx_t *enb_ptr = new enb_ctx_t;
memcpy(enb_ptr,&enb_ctx,sizeof(enb_ctx_t));
m_active_enbs.insert(std::pair<uint16_t,enb_ctx_t*>(enb_ptr->enb_id,enb_ptr));
m_sctp_to_enb_id.insert(std::pair<int32_t,uint16_t>(enb_sri->sinfo_assoc_id, enb_ptr->enb_id));
m_enb_id_to_ue_ids.insert(std::pair<uint16_t,std::set<uint32_t> >(enb_ptr->enb_id,ue_set));
return;
}
enb_ctx_t*
s1ap::find_enb_ctx(uint16_t enb_id)
{
std::map<uint16_t,enb_ctx_t*>::iterator it = m_active_enbs.find(enb_id);
if(it == m_active_enbs.end())
{
return NULL;
}
else
{
return it->second;
}
}
void
s1ap::delete_enb_ctx(int32_t assoc_id)
{
std::map<int32_t,uint16_t>::iterator it_assoc = m_sctp_to_enb_id.find(assoc_id);
uint16_t enb_id = it_assoc->second;
std::map<uint16_t,enb_ctx_t*>::iterator it_ctx = m_active_enbs.find(enb_id);
if(it_ctx == m_active_enbs.end() || it_assoc == m_sctp_to_enb_id.end())
{
m_s1ap_log->error("Could not find eNB to delete. Association: %d\n",assoc_id);
return;
}
m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", enb_id);
m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", enb_id);
//Delete connected UEs ctx
release_ues_ecm_ctx_in_enb(enb_id);
//Delete eNB
delete it_ctx->second;
m_active_enbs.erase(it_ctx);
m_sctp_to_enb_id.erase(it_assoc);
return;
}
//UE Context Management
bool
s1ap::delete_ue_ctx(ue_ctx_t *ue_ctx)
s1ap::add_ue_ctx_to_imsi_map(ue_ctx_t *ue_ctx)
{
uint32_t mme_ue_s1ap_id = ue_ctx->mme_ue_s1ap_id;
std::map<uint32_t, ue_ctx_t*>::iterator ue_ctx_it = m_active_ues.find(mme_ue_s1ap_id);
if(ue_ctx_it == m_active_ues.end() )
std::map<uint64_t, ue_ctx_t*>::iterator ctx_it = m_imsi_to_ue_ctx.find(ue_ctx->emm_ctx.imsi);
if(ctx_it != m_imsi_to_ue_ctx.end())
{
m_s1ap_log->info("UE not found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
m_s1ap_log->error("UE Context already exists. IMSI %015lu",ue_ctx->emm_ctx.imsi);
return false;
}
if(ue_ctx->ecm_ctx.mme_ue_s1ap_id != 0)
{
std::map<uint32_t,ue_ctx_t*>::iterator ctx_it2 = m_mme_ue_s1ap_id_to_ue_ctx.find(ue_ctx->ecm_ctx.mme_ue_s1ap_id);
if(ctx_it2 != m_mme_ue_s1ap_id_to_ue_ctx.end() && ctx_it2->second != ue_ctx)
{
m_s1ap_log->error("Context identified with IMSI does not match context identified by MME UE S1AP Id.\n");
return false;
}
}
m_imsi_to_ue_ctx.insert(std::pair<uint64_t,ue_ctx_t*>(ue_ctx->emm_ctx.imsi, ue_ctx));
m_s1ap_log->debug("Saved UE context corresponding to IMSI %015lu\n",ue_ctx->emm_ctx.imsi);
return true;
}
//Delete UE within eNB UE set
std::map<int32_t,uint16_t>::iterator it = m_sctp_to_enb_id.find(ue_ctx->enb_sri.sinfo_assoc_id);
if(it == m_sctp_to_enb_id.end() )
bool
s1ap::add_ue_ctx_to_mme_ue_s1ap_id_map(ue_ctx_t *ue_ctx)
{
if(ue_ctx->ecm_ctx.mme_ue_s1ap_id == 0)
{
m_s1ap_log->error("Could not find eNB for this request.\n");
m_s1ap_log->error("Could not add UE context to MME UE S1AP map. MME UE S1AP ID 0 is not valid.");
return false;
}
uint16_t enb_id = it->second;
std::map<uint16_t,std::set<uint32_t> >::iterator ue_set = m_enb_id_to_ue_ids.find(enb_id);
if(ue_set == m_enb_id_to_ue_ids.end())
std::map<uint32_t, ue_ctx_t*>::iterator ctx_it = m_mme_ue_s1ap_id_to_ue_ctx.find(ue_ctx->ecm_ctx.mme_ue_s1ap_id);
if(ctx_it != m_mme_ue_s1ap_id_to_ue_ctx.end())
{
m_s1ap_log->error("Could not find the eNB's UEs.\n");
m_s1ap_log->error("UE Context already exists. MME UE S1AP Id %015lu",ue_ctx->emm_ctx.imsi);
return false;
}
ue_set->second.erase(mme_ue_s1ap_id);
//Delete UE context
delete ue_ctx;
m_active_ues.erase(ue_ctx_it);
m_s1ap_log->info("Deleted UE Context.\n");
if(ue_ctx->ecm_ctx.imsi != 0)
{
std::map<uint32_t,ue_ctx_t*>::iterator ctx_it2 = m_mme_ue_s1ap_id_to_ue_ctx.find(ue_ctx->ecm_ctx.mme_ue_s1ap_id);
if(ctx_it2 != m_mme_ue_s1ap_id_to_ue_ctx.end() && ctx_it2->second != ue_ctx)
{
m_s1ap_log->error("Context identified with MME UE S1AP Id does not match context identified by IMSI.\n");
return false;
}
}
m_mme_ue_s1ap_id_to_ue_ctx.insert(std::pair<uint32_t,ue_ctx_t*>(ue_ctx->ecm_ctx.mme_ue_s1ap_id, ue_ctx));
m_s1ap_log->debug("Saved UE context corresponding to MME UE S1AP Id %d\n",ue_ctx->ecm_ctx.mme_ue_s1ap_id);
return true;
}
enb_ctx_t*
s1ap::find_enb_ctx(uint16_t enb_id)
ue_ctx_t*
s1ap::find_ue_ctx_from_mme_ue_s1ap_id(uint32_t mme_ue_s1ap_id)
{
std::map<uint16_t,enb_ctx_t*>::iterator it = m_active_enbs.find(enb_id);
if(it == m_active_enbs.end())
std::map<uint32_t, ue_ctx_t*>::iterator it = m_mme_ue_s1ap_id_to_ue_ctx.find(mme_ue_s1ap_id);
if(it == m_mme_ue_s1ap_id_to_ue_ctx.end())
{
return NULL;
}
@ -362,25 +410,11 @@ s1ap::find_enb_ctx(uint16_t enb_id)
}
}
void
s1ap::add_new_enb_ctx(const enb_ctx_t &enb_ctx, const struct sctp_sndrcvinfo *enb_sri)
{
m_s1ap_log->info("Adding new eNB context. eNB ID %d\n", enb_ctx.enb_id);
std::set<uint32_t> ue_set;
enb_ctx_t *enb_ptr = new enb_ctx_t;
memcpy(enb_ptr,&enb_ctx,sizeof(enb_ctx_t));
m_active_enbs.insert(std::pair<uint16_t,enb_ctx_t*>(enb_ptr->enb_id,enb_ptr));
m_sctp_to_enb_id.insert(std::pair<int32_t,uint16_t>(enb_sri->sinfo_assoc_id, enb_ptr->enb_id));
m_enb_id_to_ue_ids.insert(std::pair<uint16_t,std::set<uint32_t> >(enb_ptr->enb_id,ue_set));
return;
}
ue_ctx_t*
s1ap::find_ue_ctx(uint32_t mme_ue_s1ap_id)
s1ap::find_ue_ctx_from_imsi(uint64_t imsi)
{
std::map<uint32_t, ue_ctx_t*>::iterator it = m_active_ues.find(mme_ue_s1ap_id);
if(it == m_active_ues.end())
std::map<uint64_t, ue_ctx_t*>::iterator it = m_imsi_to_ue_ctx.find(imsi);
if(it == m_imsi_to_ue_ctx.end())
{
return NULL;
}
@ -391,58 +425,127 @@ s1ap::find_ue_ctx(uint32_t mme_ue_s1ap_id)
}
void
s1ap::add_new_ue_ctx(const ue_ctx_t &ue_ctx)
s1ap::release_ues_ecm_ctx_in_enb(uint16_t enb_id)
{
//delete UEs ctx
std::map<uint16_t,std::set<uint32_t> >::iterator ues_in_enb = m_enb_id_to_ue_ids.find(enb_id);
std::set<uint32_t>::iterator ue_id = ues_in_enb->second.begin();
while(ue_id != ues_in_enb->second.end() )
{
std::map<uint32_t, ue_ctx_t*>::iterator ue_ctx = m_mme_ue_s1ap_id_to_ue_ctx.find(*ue_id);
ue_ecm_ctx_t *ecm_ctx = &ue_ctx->second->ecm_ctx;
m_s1ap_log->info("Releasing UE ECM context. UE-MME S1AP Id: %d\n", ecm_ctx->mme_ue_s1ap_id);
m_s1ap_log->console("Releasing UE ECM context. UE-MME S1AP Id: %d\n", ecm_ctx->mme_ue_s1ap_id);
ues_in_enb->second.erase(ecm_ctx->mme_ue_s1ap_id);
ecm_ctx->state = ECM_STATE_IDLE;
ecm_ctx->mme_ue_s1ap_id = 0;
ecm_ctx->enb_ue_s1ap_id = 0;
}
}
bool
s1ap::release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id)
{
ue_ctx_t *ue_ptr = new ue_ctx_t;
memcpy(ue_ptr,&ue_ctx,sizeof(ue_ctx));
m_active_ues.insert(std::pair<uint32_t,ue_ctx_t*>(ue_ptr->mme_ue_s1ap_id,ue_ptr));
std::map<int32_t,uint16_t>::iterator it_enb = m_sctp_to_enb_id.find(ue_ptr->enb_sri.sinfo_assoc_id);
uint16_t enb_id = it_enb->second;
std::map<uint16_t,std::set<uint32_t> >::iterator it_ue_id = m_enb_id_to_ue_ids.find(enb_id);
if(it_ue_id==m_enb_id_to_ue_ids.end())
ue_ctx_t *ue_ctx = find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if(ue_ctx == NULL)
{
m_s1ap_log->error("Could not find eNB's UEs\n");
return;
m_s1ap_log->error("Cannot release UE ECM context, UE not found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
return false;
}
it_ue_id->second.insert(ue_ptr->mme_ue_s1ap_id);
return;
ue_ecm_ctx_t* ecm_ctx = &ue_ctx->ecm_ctx;
//Delete UE within eNB UE set
std::map<int32_t,uint16_t>::iterator it = m_sctp_to_enb_id.find(ecm_ctx->enb_sri.sinfo_assoc_id);
if(it == m_sctp_to_enb_id.end() )
{
m_s1ap_log->error("Could not find eNB for UE release request.\n");
return false;
}
uint16_t enb_id = it->second;
std::map<uint16_t,std::set<uint32_t> >::iterator ue_set = m_enb_id_to_ue_ids.find(enb_id);
if(ue_set == m_enb_id_to_ue_ids.end())
{
m_s1ap_log->error("Could not find the eNB's UEs.\n");
return false;
}
ue_set->second.erase(mme_ue_s1ap_id);
//Release UE ECM context
m_mme_ue_s1ap_id_to_ue_ctx.erase(mme_ue_s1ap_id);
ecm_ctx->state = ECM_STATE_IDLE;
ecm_ctx->mme_ue_s1ap_id = 0;
ecm_ctx->enb_ue_s1ap_id = 0;
m_s1ap_log->info("Released UE ECM Context.\n");
return true;
}
uint32_t
s1ap::get_next_mme_ue_s1ap_id()
bool
s1ap::delete_ue_ctx(uint64_t imsi)
{
return m_next_mme_ue_s1ap_id++;
ue_ctx_t *ue_ctx = find_ue_ctx_from_imsi(imsi);
if(ue_ctx == NULL)
{
m_s1ap_log->info("Cannot delete UE context, UE not found. IMSI: %d\n", imsi);
return false;
}
//Make sure to release ECM ctx
if(ue_ctx->ecm_ctx.mme_ue_s1ap_id != 0)
{
release_ue_ecm_ctx(ue_ctx->ecm_ctx.mme_ue_s1ap_id);
}
//Delete UE context
m_imsi_to_ue_ctx.erase(imsi);
delete ue_ctx;
m_s1ap_log->info("Deleted UE Context.\n");
return true;
}
//UE Bearer Managment
void
s1ap::activate_eps_bearer(uint32_t mme_s1ap_id, uint8_t ebi)
s1ap::activate_eps_bearer(uint64_t imsi, uint8_t ebi)
{
std::map<uint32_t,ue_ctx_t*>::iterator ue_ctx_it = m_active_ues.find(mme_s1ap_id);
if(ue_ctx_it == m_active_ues.end())
std::map<uint64_t,ue_ctx_t*>::iterator ue_ctx_it = m_imsi_to_ue_ctx.find(imsi);
if(ue_ctx_it == m_imsi_to_ue_ctx.end())
{
m_s1ap_log->error("Could not activate EPS bearer: Could not find UE context\n");
return;
}
//Make sure NAS is active
uint32_t mme_ue_s1ap_id = ue_ctx_it->second->ecm_ctx.mme_ue_s1ap_id;
std::map<uint32_t,ue_ctx_t*>::iterator it = m_mme_ue_s1ap_id_to_ue_ctx.find(mme_ue_s1ap_id);
if(it == m_mme_ue_s1ap_id_to_ue_ctx.end())
{
m_s1ap_log->error("Could not find UE context\n");
m_s1ap_log->error("Could not activate EPS bearer: ECM context seems to be missing\n");
return;
}
ue_ctx_t * ue_ctx = ue_ctx_it->second;
if (ue_ctx->erabs_ctx[ebi].state != ERAB_CTX_SETUP)
ue_ecm_ctx_t * ecm_ctx = &ue_ctx_it->second->ecm_ctx;
if (ecm_ctx->erabs_ctx[ebi].state != ERAB_CTX_SETUP)
{
m_s1ap_log->error("EPS Bearer could not be activated. MME S1AP Id %d, EPS Bearer id %d, state %d\n",mme_s1ap_id,ebi,ue_ctx->erabs_ctx[ebi].state);
m_s1ap_log->console("EPS Bearer could not be activated. MME S1AP Id %d, EPS Bearer id %d\n",mme_s1ap_id,ebi,ue_ctx->erabs_ctx[ebi].state);
m_s1ap_log->error("Could not be activate EPS Bearer, bearer in wrong state: MME S1AP Id %d, EPS Bearer id %d, state %d\n",mme_ue_s1ap_id,ebi,ecm_ctx->erabs_ctx[ebi].state);
m_s1ap_log->console("Could not be activate EPS Bearer, bearer in wrong state: MME S1AP Id %d, EPS Bearer id %d\n",mme_ue_s1ap_id,ebi,ecm_ctx->erabs_ctx[ebi].state);
return;
}
ue_ctx->erabs_ctx[ebi].state = ERAB_ACTIVE;
m_s1ap_log->info("Activated EPS Bearer\n");
ecm_ctx->erabs_ctx[ebi].state = ERAB_ACTIVE;
ecm_ctx->state = ECM_STATE_CONNECTED;
m_s1ap_log->info("Activated EPS Bearer: Bearer id %d\n",ebi);
return;
}
uint32_t
s1ap::allocate_m_tmsi(uint32_t mme_ue_s1ap_id)
s1ap::allocate_m_tmsi(uint64_t imsi)
{
//uint32_t m_tmsi = m_next_m_tmsi++;
//m_tmsi_to_s1ap_id.insert(std::pair<uint32_t,uint32_t>(m_tmsi,mme_ue_s1ap_id));
uint32_t m_tmsi = 0x0123;
// uint32_t m_tmsi = m_next_m_tmsi++;
uint32_t m_tmsi = m_next_m_tmsi;
m_tmsi_to_imsi.insert(std::pair<uint32_t,uint64_t>(m_tmsi,imsi));
m_s1ap_log->debug("Allocated M-TMSI 0x%x to IMSI %015lu,\n",m_tmsi,imsi);
return m_tmsi;
}
@ -478,3 +581,4 @@ s1ap::print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx)
}
} //namespace srsepc

@ -34,7 +34,7 @@
namespace srsepc{
s1ap_ctx_mngmt_proc* s1ap_ctx_mngmt_proc::m_instance = NULL;
boost::mutex s1ap_ctx_mngmt_proc_instance_mutex;
pthread_mutex_t s1ap_ctx_mngmt_proc_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
s1ap_ctx_mngmt_proc::s1ap_ctx_mngmt_proc()
@ -48,21 +48,23 @@ s1ap_ctx_mngmt_proc::~s1ap_ctx_mngmt_proc()
s1ap_ctx_mngmt_proc*
s1ap_ctx_mngmt_proc::get_instance(void)
{
boost::mutex::scoped_lock lock(s1ap_ctx_mngmt_proc_instance_mutex);
pthread_mutex_lock(&s1ap_ctx_mngmt_proc_instance_mutex);
if(NULL == m_instance) {
m_instance = new s1ap_ctx_mngmt_proc();
}
pthread_mutex_unlock(&s1ap_ctx_mngmt_proc_instance_mutex);
return(m_instance);
}
void
s1ap_ctx_mngmt_proc::cleanup(void)
{
boost::mutex::scoped_lock lock(s1ap_ctx_mngmt_proc_instance_mutex);
pthread_mutex_lock(&s1ap_ctx_mngmt_proc_instance_mutex);
if(NULL != m_instance) {
delete m_instance;
m_instance = NULL;
}
pthread_mutex_unlock(&s1ap_ctx_mngmt_proc_instance_mutex);
}
void
@ -77,8 +79,11 @@ s1ap_ctx_mngmt_proc::init(void)
}
bool
s1ap_ctx_mngmt_proc::send_initial_context_setup_request(uint32_t mme_ue_s1ap_id, struct srslte::gtpc_create_session_response *cs_resp, struct srslte::gtpc_f_teid_ie sgw_ctrl_fteid)
s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx,
ue_ecm_ctx_t *ecm_ctx,
erab_ctx_t *erab_ctx)
{
int s1mme = m_s1ap->get_s1_mme();
//Prepare reply PDU
@ -92,58 +97,50 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(uint32_t mme_ue_s1ap_id,
LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *in_ctxt_req = &init->choice.InitialContextSetupRequest;
LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctxt = &in_ctxt_req->E_RABToBeSetupListCtxtSUReq.buffer[0]; //FIXME support more than one erab
LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctx_req = &in_ctxt_req->E_RABToBeSetupListCtxtSUReq.buffer[0]; //FIXME support more than one erab
srslte::byte_buffer_t *reply_buffer = m_pool->allocate();
m_s1ap_log->info("Preparing to send Initial Context Setup request\n");
//Find UE Context
ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx(mme_ue_s1ap_id);
if(ue_ctx == NULL)
{
m_s1ap_log->error("Could not find UE to send Setup Context Request. MME S1AP Id: %d", mme_ue_s1ap_id);
return false;
}
//Add MME and eNB S1AP Ids
in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctx->mme_ue_s1ap_id;
in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctx->enb_ue_s1ap_id;
in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ecm_ctx->mme_ue_s1ap_id;
in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ecm_ctx->enb_ue_s1ap_id;
//Set UE-AMBR
in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL.BitRate=1000000000;//2^32-1
in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL.BitRate=1000000000;
in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateUL.BitRate=1000000000;//FIXME Get UE-AMBR from HSS
//Setup eRAB context
in_ctxt_req->E_RABToBeSetupListCtxtSUReq.len = 1;
erab_ctxt->e_RAB_ID.E_RAB_ID = cs_resp->eps_bearer_context_created.ebi;
erab_ctx_req->e_RAB_ID.E_RAB_ID = erab_ctx->erab_id;
//Setup E-RAB QoS parameters
erab_ctxt->e_RABlevelQoSParameters.qCI.QCI = 9;
erab_ctxt->e_RABlevelQoSParameters.allocationRetentionPriority.priorityLevel.PriorityLevel = 15 ;//Lowest
erab_ctxt->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionCapability = LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_SHALL_NOT_TRIGGER_PRE_EMPTION;
erab_ctxt->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionVulnerability = LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_PRE_EMPTABLE;
erab_ctx_req->e_RABlevelQoSParameters.qCI.QCI = 9;
erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.priorityLevel.PriorityLevel = 15 ;//Lowest
erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionCapability = LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_SHALL_NOT_TRIGGER_PRE_EMPTION;
erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionVulnerability = LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_PRE_EMPTABLE;
erab_ctxt->e_RABlevelQoSParameters.gbrQosInformation_present=false;
erab_ctx_req->e_RABlevelQoSParameters.gbrQosInformation_present=false;
//Set E-RAB S-GW F-TEID
if (cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present == false){
m_s1ap_log->error("Did not receive S1-U TEID in create session response\n");
return false;
}
erab_ctxt->transportLayerAddress.n_bits = 32; //IPv4
uint32_t sgw_s1u_ip = htonl(cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4);
//if (cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present == false){
// m_s1ap_log->error("Did not receive S1-U TEID in create session response\n");
// return false;
//}
erab_ctx_req->transportLayerAddress.n_bits = 32; //IPv4
uint32_t sgw_s1u_ip = htonl(erab_ctx->sgw_s1u_fteid.ipv4);
//uint32_t sgw_s1u_ip = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4;
uint8_t *tmp_ptr = erab_ctxt->transportLayerAddress.buffer;
uint8_t *tmp_ptr = erab_ctx_req->transportLayerAddress.buffer;
liblte_value_2_bits(sgw_s1u_ip, &tmp_ptr, 32);//FIXME consider ipv6
uint32_t tmp_teid = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.teid;
memcpy(erab_ctxt->gTP_TEID.buffer, &tmp_teid, sizeof(uint32_t));
uint32_t sgw_s1u_teid = erab_ctx->sgw_s1u_fteid.teid;
memcpy(erab_ctx_req->gTP_TEID.buffer, &sgw_s1u_teid, sizeof(uint32_t));
//Set UE security capabilities and k_enb
bzero(in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer,sizeof(uint8_t)*16);
bzero(in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer,sizeof(uint8_t)*16);
for(int i = 0; i<3; i++)
{
if(ue_ctx->ue_network_cap.eea[i+1] == true)
if(emm_ctx->security_ctxt.ue_network_cap.eea[i+1] == true)
{
in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 1; //EEA supported
}
@ -151,7 +148,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(uint32_t mme_ue_s1ap_id,
{
in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 0; //EEA not supported
}
if(ue_ctx->ue_network_cap.eia[i+1] == true)
if(emm_ctx->security_ctxt.ue_network_cap.eia[i+1] == true)
{
in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 1; //EEA supported
}
@ -159,25 +156,19 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(uint32_t mme_ue_s1ap_id,
{
in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 0; //EEA not supported
}
// in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[0] = 1; //EIA1
}
uint8_t key_enb[32];
liblte_security_generate_k_enb(ue_ctx->security_ctxt.k_asme, ue_ctx->security_ctxt.ul_nas_count, key_enb);
liblte_unpack(key_enb, 32, in_ctxt_req->SecurityKey.buffer);
m_s1ap_log->info("Generating KeNB with UL NAS COUNT: %d\n",ue_ctx->security_ctxt.ul_nas_count);
//Set Attach accepted and activat default bearer NAS messages
if(cs_resp->paa_present != true)
{
m_s1ap_log->error("PAA not present\n");
return false;
}
if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4)
//Get K eNB
liblte_unpack(emm_ctx->security_ctxt.k_enb, 32, in_ctxt_req->SecurityKey.buffer);
m_s1ap_log->info_hex(emm_ctx->security_ctxt.k_enb, 32, "Initial Context Setup Request -- Key eNB\n");
srslte::byte_buffer_t *nas_buffer = m_pool->allocate();
if(emm_ctx->state == EMM_STATE_DEREGISTERED)
{
m_s1ap_log->error("IPv6 not supported yet\n");
return false;
//Attach procedure initiated from an attach request
m_s1ap_log->console("Adding attach accept to Initial Context Setup Request\n");
m_s1ap_log->info("Adding attach accept to Initial Context Setup Request\n");
m_s1ap_nas_transport->pack_attach_accept(emm_ctx, ecm_ctx, erab_ctx_req, &erab_ctx->pdn_addr_alloc, nas_buffer);
}
srslte::byte_buffer_t *nas_buffer = m_pool->allocate();
m_s1ap_nas_transport->pack_attach_accept(ue_ctx, erab_ctxt, &cs_resp->paa, nas_buffer);
LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer);
@ -186,8 +177,9 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(uint32_t mme_ue_s1ap_id,
m_s1ap_log->error("Could not pack Initial Context Setup Request Message\n");
return false;
}
//Send Reply to eNB
ssize_t n_sent = sctp_send(s1mme,reply_buffer->msg, reply_buffer->N_bytes, &ue_ctx->enb_sri, 0);
ssize_t n_sent = sctp_send(s1mme,reply_buffer->msg, reply_buffer->N_bytes, &ecm_ctx->enb_sri, 0);
if(n_sent == -1)
{
m_s1ap_log->error("Failed to send Initial Context Setup Request\n");
@ -195,16 +187,18 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(uint32_t mme_ue_s1ap_id,
}
//Change E-RAB state to Context Setup Requested and save S-GW control F-TEID
ue_ctx->erabs_ctx[erab_ctxt->e_RAB_ID.E_RAB_ID].state = ERAB_CTX_REQUESTED;
ue_ctx->erabs_ctx[erab_ctxt->e_RAB_ID.E_RAB_ID].sgw_ctrl_fteid.teid = sgw_ctrl_fteid.teid;
ue_ctx->erabs_ctx[erab_ctxt->e_RAB_ID.E_RAB_ID].sgw_ctrl_fteid.ipv4 = sgw_ctrl_fteid.ipv4;
ecm_ctx->erabs_ctx[erab_ctx_req->e_RAB_ID.E_RAB_ID].state = ERAB_CTX_REQUESTED;
//ecm_ctx->erabs_ctx[erab_ctx_req->e_RAB_ID.E_RAB_ID].sgw_ctrl_fteid.teid = sgw_ctrl_fteid.teid;
//ecm_ctx->erabs_ctx[erab_ctx_req->e_RAB_ID.E_RAB_ID].sgw_ctrl_fteid.ipv4 = sgw_ctrl_fteid.ipv4;
struct in_addr addr;
addr.s_addr = htonl(sgw_s1u_ip);
m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctxt->e_RAB_ID.E_RAB_ID);
m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", tmp_teid,inet_ntoa(addr));
m_s1ap_log->console("Sent Intial Context Setup Request, E-RAB id %d\n",erab_ctxt->e_RAB_ID.E_RAB_ID);
m_s1ap_log->console("Initial Context -- S1-U TEID 0x%x. IP %s \n", tmp_teid,inet_ntoa(addr));
m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID);
m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr));
m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID);
m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID);
m_s1ap_log->console("Initial Context Setup Request -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr));
m_s1ap_log->console("Initial Context Setup Request -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr));
m_pool->deallocate(reply_buffer);
m_pool->deallocate(nas_buffer);
@ -214,23 +208,25 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(uint32_t mme_ue_s1ap_id,
bool
s1ap_ctx_mngmt_proc::handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *in_ctxt_resp)
{
uint32_t mme_ue_s1ap_id = in_ctxt_resp->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx(mme_ue_s1ap_id);
ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if (ue_ctx == NULL)
{
m_s1ap_log->error("Could not find UE's context in active UE's map\n");
return false;
}
ue_emm_ctx_t * emm_ctx = &ue_ctx->emm_ctx;
ue_ecm_ctx_t * ecm_ctx = &ue_ctx->ecm_ctx;
m_s1ap_log->console("Received Initial Context Setup Response\n");
//Setup E-RABs
for(uint32_t i=0; i<in_ctxt_resp->E_RABSetupListCtxtSURes.len;i++)
{
uint8_t erab_id = in_ctxt_resp->E_RABSetupListCtxtSURes.buffer[i].e_RAB_ID.E_RAB_ID;
erab_ctx_t *erab_ctx = &ue_ctx->erabs_ctx[erab_id];
erab_ctx_t *erab_ctx = &ecm_ctx->erabs_ctx[erab_id];
if (erab_ctx->state != ERAB_CTX_REQUESTED)
{
m_s1ap_log->error("E-RAB requested was not active %d\n",erab_id);
m_s1ap_log->error("E-RAB requested was not previously requested %d\n",erab_id);
return false;
}
//Mark E-RAB with context setup
@ -255,6 +251,12 @@ s1ap_ctx_mngmt_proc::handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_I
m_s1ap_log->console("E-RAB Context -- eNB TEID 0x%x; eNB GTP-U Address %s\n", erab_ctx->enb_fteid.teid, enb_addr_str);
}
if(emm_ctx->state == EMM_STATE_REGISTERED)
{
m_s1ap_log->console("Initial Context Setup Response triggered from Service Request.\n");
m_s1ap_log->console("Sending Modify Bearer Request.\n");
m_mme_gtpc->send_modify_bearer_request(emm_ctx->imsi, &ecm_ctx->erabs_ctx[5]);
}
return true;
}
@ -262,41 +264,155 @@ bool
s1ap_ctx_mngmt_proc::handle_ue_context_release_request(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *ue_rel, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag)
{
LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT ue_rel_req;
uint32_t mme_ue_s1ap_id = ue_rel->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
m_s1ap_log->info("Received UE Context Release Request. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("Received UE Context Release Request. MME-UE S1AP Id %d\n", mme_ue_s1ap_id);
ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx(mme_ue_s1ap_id);
ue_ctx_t * ue_ctx = m_s1ap->find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if(ue_ctx == NULL)
{
m_s1ap_log->info("UE not found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
m_s1ap_log->info("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
return false;
}
ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx;
//Delete any context at the SPGW
bool active = false;
//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 mearers request");
m_s1ap_log->info("There are active E-RABs, send release access mearers request");
//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.
m_mme_gtpc->send_release_access_bearers_request(ecm_ctx->imsi);
//Send release context command to enb, so that it can release it's bearers
send_ue_context_release_command(ecm_ctx,reply_buffer);
}
else
{
//No ECM Context to release
m_s1ap_log->info("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id);
//Make sure E-RABS are merked as DEACTIVATED.
for(int i=0;i<MAX_ERABS_PER_UE;i++)
{
if(ue_ctx->erabs_ctx[i].state != ERAB_DEACTIVATED)
ecm_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED;
}
}
//Delete UE context
ecm_ctx->state = ECM_STATE_IDLE;
ecm_ctx->enb_ue_s1ap_id = 0;
ecm_ctx->mme_ue_s1ap_id = 0;
m_s1ap_log->info("UE is ECM IDLE.\n");
m_s1ap_log->console("UE is ECM IDLE.\n");
return true;
}
bool
s1ap_ctx_mngmt_proc::send_ue_context_release_command(ue_ecm_ctx_t *ecm_ctx, srslte::byte_buffer_t *reply_buffer)
{
int s1mme = m_s1ap->get_s1_mme();
//Prepare reply PDU
LIBLTE_S1AP_S1AP_PDU_STRUCT pdu;
bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT));
pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE;
LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &pdu.choice.initiatingMessage;
init->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASE;
init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND;
LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *ctx_rel_cmd = &init->choice.UEContextReleaseCommand;
ctx_rel_cmd->UE_S1AP_IDs.choice_type = LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR;
ctx_rel_cmd->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.mME_UE_S1AP_ID.MME_UE_S1AP_ID = ecm_ctx->mme_ue_s1ap_id;
ctx_rel_cmd->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ecm_ctx->enb_ue_s1ap_id;
ctx_rel_cmd->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_NAS;
ctx_rel_cmd->Cause.choice.nas.ext = false;
ctx_rel_cmd->Cause.choice.nas.e = LIBLTE_S1AP_CAUSENAS_NORMAL_RELEASE;
LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer);
if(err != LIBLTE_SUCCESS)
{
active = true;
//ue_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED;
break;
m_s1ap_log->error("Could not pack Initial Context Setup Request Message\n");
return false;
}
//Send Reply to eNB
int n_sent = sctp_send(s1mme,reply_buffer->msg, reply_buffer->N_bytes, &ecm_ctx->enb_sri, 0);
if(n_sent == -1)
{
m_s1ap_log->error("Failed to send Initial Context Setup Request\n");
return false;
}
return true;
}
bool
s1ap_ctx_mngmt_proc::handle_ue_context_release_complete(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *rel_comp)
{
/*
typedef struct{
bool ext;
LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID;
LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID;
LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics;
bool CriticalityDiagnostics_present;
LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT UserLocationInformation;
bool UserLocationInformation_present;
}LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT;
*/
uint32_t mme_ue_s1ap_id = rel_comp->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
m_s1ap_log->info("Received UE Context Release Complete. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("Received UE Context Release Complete. MME-UE S1AP Id %d\n", mme_ue_s1ap_id);
ue_ctx_t * ue_ctx = m_s1ap->find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if(ue_ctx == NULL)
{
m_s1ap_log->info("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
return false;
}
if(active == true)
ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx;
//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 delete session request
m_mme_gtpc->send_delete_session_request(ue_ctx);
//There are active E-RABs, send release access mearers request
m_s1ap_log->console("There are active E-RABs, send release access mearers request");
m_s1ap_log->info("There are active E-RABs, send release access mearers request");
m_mme_gtpc->send_release_access_bearers_request(ecm_ctx->imsi);
//The handle_releease_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.
}
//m_s1ap->delete_ue_ctx(ue_ctx);
else
{
//No ECM Context to release
m_s1ap_log->info("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id);
//Make sure E-RABS are merked as DEACTIVATED.
for(int i=0;i<MAX_ERABS_PER_UE;i++)
{
ue_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED;
ecm_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED;
}
}
//Delete UE context
m_s1ap_log->info("Deleted UE Context.\n");
m_s1ap->release_ue_ecm_ctx(ue_ctx->ecm_ctx.mme_ue_s1ap_id);
m_s1ap_log->info("UE Context Release Completed.\n");
m_s1ap_log->console("UE Context Release Completed.\n");
return true;
}
} //namespace srsepc

@ -32,7 +32,7 @@
namespace srsepc{
s1ap_mngmt_proc* s1ap_mngmt_proc::m_instance = NULL;
boost::mutex s1ap_mngmt_proc_instance_mutex;
pthread_mutex_t s1ap_mngmt_proc_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
s1ap_mngmt_proc::s1ap_mngmt_proc()
@ -46,21 +46,23 @@ s1ap_mngmt_proc::~s1ap_mngmt_proc()
s1ap_mngmt_proc*
s1ap_mngmt_proc::get_instance(void)
{
boost::mutex::scoped_lock lock(s1ap_mngmt_proc_instance_mutex);
pthread_mutex_lock(&s1ap_mngmt_proc_instance_mutex);
if(NULL == m_instance) {
m_instance = new s1ap_mngmt_proc();
}
pthread_mutex_unlock(&s1ap_mngmt_proc_instance_mutex);
return(m_instance);
}
void
s1ap_mngmt_proc::cleanup(void)
{
boost::mutex::scoped_lock lock(s1ap_mngmt_proc_instance_mutex);
pthread_mutex_lock(&s1ap_mngmt_proc_instance_mutex);
if(NULL != m_instance) {
delete m_instance;
m_instance = NULL;
}
pthread_mutex_unlock(&s1ap_mngmt_proc_instance_mutex);
}
void
@ -102,6 +104,7 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU
{
//eNB already registered
//TODO replace enb_ctx
m_s1ap_log->warning("eNB Already registered\n");
}
else
{
@ -234,7 +237,7 @@ s1ap_mngmt_proc::pack_s1_setup_response(s1ap_args_t s1ap_args, srslte::byte_buff
LIBLTE_S1AP_SERVEDGUMMEISITEM_STRUCT *serv_gummei = &s1_resp->ServedGUMMEIs.buffer[0];
serv_gummei->ext=false;
//serv_gummei->iE_Extensions=false;
serv_gummei->iE_Extensions_present = false;
uint32_t plmn=0;
srslte::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &plmn);
@ -258,6 +261,8 @@ s1ap_mngmt_proc::pack_s1_setup_response(s1ap_args_t s1ap_args, srslte::byte_buff
//Relay Unsupported
s1_resp->MMERelaySupportIndicator_present=false;
s1_resp->CriticalityDiagnostics_present = false;
liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)msg);
return true;

File diff suppressed because it is too large Load Diff

@ -41,7 +41,7 @@
namespace srsepc{
spgw* spgw::m_instance = NULL;
boost::mutex spgw_instance_mutex;
pthread_mutex_t spgw_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
const uint16_t SPGW_BUFFER_SIZE = 2500;
@ -63,21 +63,23 @@ spgw::~spgw()
spgw*
spgw::get_instance(void)
{
boost::mutex::scoped_lock lock(spgw_instance_mutex);
pthread_mutex_lock(&spgw_instance_mutex);
if(NULL == m_instance) {
m_instance = new spgw();
}
pthread_mutex_unlock(&spgw_instance_mutex);
return(m_instance);
}
void
spgw::cleanup(void)
{
boost::mutex::scoped_lock lock(spgw_instance_mutex);
pthread_mutex_lock(&spgw_instance_mutex);
if(NULL != m_instance) {
delete m_instance;
m_instance = NULL;
}
pthread_mutex_unlock(&spgw_instance_mutex);
}
int
@ -144,8 +146,8 @@ spgw::stop()
std::map<uint32_t,spgw_tunnel_ctx*>::iterator it = m_teid_to_tunnel_ctx.begin(); //Map control TEID to tunnel ctx. Usefull to get reply ctrl TEID, UE IP, etc.
while(it!=m_teid_to_tunnel_ctx.end())
{
m_spgw_log->info("Deleting SP-GW Tunnel. IMSI: %lu\n", it->second->imsi);
m_spgw_log->console("Deleting SP-GW Tunnel. IMSI: %lu\n", it->second->imsi);
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++);
}
@ -224,6 +226,9 @@ spgw::init_sgi_if(spgw_args_t *args)
return srslte::ERROR_CANT_START;
}
//Set initial time of setup
gettimeofday(&m_t_last_dl, NULL);
m_sgi_up = true;
return(srslte::ERROR_NONE);
}
@ -297,15 +302,11 @@ spgw::run_thread()
if (FD_ISSET(m_s1u, &set))
{
msg->N_bytes = recvfrom(m_s1u, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES, 0, &src_addr, &addrlen );
//m_spgw_log->console("Received PDU from S1-U. Bytes %d\n", msg->N_bytes);
//m_spgw_log->debug("Received PDU from S1-U. Bytes %d\n", msg->N_bytes);
handle_s1u_pdu(msg);
}
if (FD_ISSET(m_sgi_if, &set))
{
msg->N_bytes = read(sgi, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES);
//m_spgw_log->console("Received PDU from SGi. Bytes %d\n", msg->N_bytes);
//m_spgw_log->debug("Received PDU from SGi. Bytes %d\n", msg->N_bytes);
handle_sgi_pdu(msg);
}
}
@ -328,6 +329,8 @@ spgw::handle_sgi_pdu(srslte::byte_buffer_t *msg)
bool ip_found = false;
srslte::gtpc_f_teid_ie enb_fteid;
struct timeval t_now, t_delta;
version = msg->msg[0]>>4;
((uint8_t*)&dest_ip)[0] = msg->msg[16];
((uint8_t*)&dest_ip)[1] = msg->msg[17];
@ -380,8 +383,10 @@ spgw::handle_sgi_pdu(srslte::byte_buffer_t *msg)
m_spgw_log->error("Error sending packet to eNB\n");
return;
}
//m_spgw_log->console("Sent packet to %s:%d. Bytes=%d/%d\n",inet_ntoa(enb_addr.sin_addr), GTPU_RX_PORT,n,msg->N_bytes);
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;
}
@ -405,6 +410,10 @@ spgw::handle_s1u_pdu(srslte::byte_buffer_t *msg)
}
return;
}
/*
* Helper Functions
*/
uint64_t
spgw::get_new_ctrl_teid()
{
@ -424,25 +433,29 @@ spgw::get_new_ue_ipv4()
return ntohl(m_h_next_ue_ip);//FIXME Tmp hack
}
void
spgw::handle_create_session_request(struct srslte::gtpc_create_session_request *cs_req, struct srslte::gtpc_pdu *cs_resp_pdu)
spgw_tunnel_ctx_t*
spgw::create_gtp_ctx(struct srslte::gtpc_create_session_request *cs_req)
{
srslte::gtpc_header *header = &cs_resp_pdu->header;
srslte::gtpc_create_session_response *cs_resp = &cs_resp_pdu->choice.create_session_response;
m_spgw_log->info("Received Create Session Request\n");
//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 %d\n", spgw_uplink_ctrl_teid);
m_spgw_log->console("SPGW: Allocated User TEID %d\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;
@ -453,32 +466,82 @@ spgw::handle_create_session_request(struct srslte::gtpc_create_session_request *
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;
}
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 %015lu 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 = cs_req->sender_f_teid.teid; //Send create session requesponse to the CS Request TEID
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.teid = spgw_uplink_ctrl_teid;
cs_resp->sender_f_teid.ipv4 = 0;//FIXME This is not relevant, as the GTP-C is not transmitted over sockets yet.
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.teid = spgw_uplink_user_teid;
cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4 = m_s1u_addr.sin_addr.s_addr;
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 = ue_ip;
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;
@ -523,8 +586,10 @@ spgw::handle_modify_bearer_request(struct srslte::gtpc_pdu *mb_req_pdu, struct s
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.insert(std::pair<uint32_t,srslte::gtpc_f_teid_ie>(tunnel_ctx->ue_ipv4, tunnel_ctx->dw_user_fteid));
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
@ -559,7 +624,7 @@ spgw::handle_delete_session_request(struct srslte::gtpc_pdu *del_req_pdu, struct
//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);
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);
@ -571,4 +636,31 @@ spgw::handle_delete_session_request(struct srslte::gtpc_pdu *del_req_pdu, struct
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;
}
} //namespace srsepc

Loading…
Cancel
Save