diff --git a/lib/gtp/xact.h b/lib/gtp/xact.h index 5bb25242b..8955629cc 100644 --- a/lib/gtp/xact.h +++ b/lib/gtp/xact.h @@ -121,6 +121,7 @@ typedef struct ogs_gtp_xact_s { #define OGS_GTP_CREATE_IN_ATTACH_REQUEST 1 #define OGS_GTP_CREATE_IN_UPLINK_NAS_TRANSPORT 2 #define OGS_GTP_CREATE_IN_PATH_SWITCH_REQUEST 3 +#define OGS_GTP_CREATE_IN_TRACKING_AREA_UPDATE 4 /* 3GPP TS 33.401 9.1.2 */ int create_action; #define OGS_GTP_MODIFY_IN_PATH_SWITCH_REQUEST 1 diff --git a/src/mme/emm-build.c b/src/mme/emm-build.c index 297e11fd9..e4fb76c2c 100644 --- a/src/mme/emm-build.c +++ b/src/mme/emm-build.c @@ -398,6 +398,18 @@ ogs_pkbuf_t *emm_build_security_mode_command(mme_ue_t *mme_ue) imeisv_request->type = OGS_NAS_IMEISV_TYPE; imeisv_request->value = OGS_NAS_IMEISV_REQUESTED; + if (mme_ue->nonceue) { + security_mode_command->presencemask |= + OGS_NAS_EPS_SECURITY_MODE_COMMAND_REPLAYED_NONCEUE_PRESENT; + security_mode_command->replayed_nonceue = mme_ue->nonceue; + } + + if (mme_ue->noncemme) { + security_mode_command->presencemask |= + OGS_NAS_EPS_SECURITY_MODE_COMMAND_NONCEMME_PRESENT; + security_mode_command->noncemme = mme_ue->noncemme; + } + /* * TS24.301 * 5.4.3.2 NAS security mode control initiation by the network diff --git a/src/mme/emm-handler.c b/src/mme/emm-handler.c index 8ff587a8d..82059c814 100644 --- a/src/mme/emm-handler.c +++ b/src/mme/emm-handler.c @@ -549,6 +549,36 @@ int emm_handle_service_request( return OGS_OK; } +bool emm_tau_request_ue_comes_from_gb_or_iu(const ogs_nas_eps_tracking_area_update_request_t *tau_request) +{ + /* "When the tracking area updating procedure is initiated in EMM-IDLE mode + * to perform an inter-system change from A/Gb mode or Iu mode to S1 mode + * and the TIN is set to "P-TMSI", the UE shall include the GPRS ciphering + * key sequence number applicable for A/Gb mode or Iu mode and a nonce UE in + * the TRACKING AREA UPDATE REQUEST message." + */ + if (!(tau_request->presencemask & + OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_NONCEUE_PRESENT)) + return false; + + if (tau_request->presencemask & + OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_OLD_GUTI_TYPE_PRESENT) { + /* 0 = Native, 1 = Mapped */ + return tau_request->old_guti_type.guti_type; + } else { + /* TS 23.003 2.8.2.2.2: + * "The most significant bit of the shall be set to zero; + * and the most significant bit of shall be set to + * one. Based on this definition, the most significant bit of the + * can be used to distinguish the node type, i.e. + * whether it is an MME or SGSN */ + const ogs_nas_eps_mobile_identity_t *eps_mobile_identity = &tau_request->old_guti; + if (eps_mobile_identity->imsi.type != OGS_NAS_EPS_MOBILE_IDENTITY_GUTI) + return false; + return !(eps_mobile_identity->guti.mme_gid & 0x8000); + } +} + int emm_handle_tau_request(mme_ue_t *mme_ue, ogs_nas_eps_tracking_area_update_request_t *tau_request, ogs_pkbuf_t *pkbuf) { @@ -660,6 +690,19 @@ int emm_handle_tau_request(mme_ue_t *mme_ue, sizeof(tau_request->ms_network_capability)); } + if (tau_request->presencemask & + OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_NONCEUE_PRESENT) { + mme_ue->gprs_ciphering_key_sequence_number = tau_request->gprs_ciphering_key_sequence_number.key_sequence; + } else { + /* Mark as unavailable, Table 10.5.2/3GPP TS 24.008 */ + mme_ue->gprs_ciphering_key_sequence_number = OGS_NAS_CIPHERING_KEY_SEQUENCE_NUMBER_NO_KEY_FROM_MS; + } + + if (tau_request->presencemask & + OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_NONCEUE_PRESENT) { + mme_ue->nonceue = tau_request->nonceue; + } + /* TODO: * 1) Consider if MME is changed or not. * 2) Consider if SGW is changed or not. @@ -679,6 +722,10 @@ int emm_handle_tau_request(mme_ue_t *mme_ue, nas_guti.m_tmsi, MME_UE_HAVE_IMSI(mme_ue) ? mme_ue->imsi_bcd : "Unknown"); + + memcpy(&mme_ue->next.guti, + &nas_guti, sizeof(ogs_nas_eps_guti_t)); + break; default: ogs_error("Not implemented[%d]", eps_mobile_identity->imsi.type); diff --git a/src/mme/emm-handler.h b/src/mme/emm-handler.h index 9ea14b93a..3bc1701a9 100644 --- a/src/mme/emm-handler.h +++ b/src/mme/emm-handler.h @@ -50,6 +50,9 @@ int emm_handle_extended_service_request(mme_ue_t *mme_ue, int emm_handle_security_mode_complete(mme_ue_t *mme_ue, ogs_nas_eps_security_mode_complete_t *security_mode_complete); +bool emm_tau_request_ue_comes_from_gb_or_iu( + const ogs_nas_eps_tracking_area_update_request_t *tau_request); + #ifdef __cplusplus } #endif diff --git a/src/mme/emm-sm.c b/src/mme/emm-sm.c index ffd814a32..27b59681a 100644 --- a/src/mme/emm-sm.c +++ b/src/mme/emm-sm.c @@ -20,6 +20,7 @@ #include "mme-event.h" #include "mme-timer.h" #include "s1ap-handler.h" +#include "mme-gn-handler.h" #include "mme-fd-path.h" #include "emm-handler.h" #include "emm-build.h" @@ -286,7 +287,9 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, mme_ue_t *mme_ue = NULL; enb_ue_t *enb_ue = NULL; + mme_sgsn_t *sgsn = NULL; ogs_nas_eps_message_t *message = NULL; + ogs_nas_rai_t rai; ogs_nas_security_header_type_t h; ogs_assert(e); @@ -489,6 +492,26 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, break; } + if (emm_tau_request_ue_comes_from_gb_or_iu(&message->emm.tracking_area_update_request)) { + ogs_info("TAU request : UE comes from SGSN, attempt retrieving context"); + guti_to_rai_ptmsi(&mme_ue->next.guti, &rai, NULL, NULL); + sgsn = mme_sgsn_find_by_routing_address(&rai, 0xffff); + if (!sgsn) { + ogs_plmn_id_t plmn_id; + ogs_nas_to_plmn_id(&plmn_id, &rai.lai.nas_plmn_id); + ogs_warn("No SGSN route matching RAI[MCC:%u MNC:%u LAC:%u RAC:%u]", + ogs_plmn_id_mcc(&plmn_id), ogs_plmn_id_mnc(&plmn_id), + rai.lai.lac, rai.rac); + r = nas_eps_send_tau_reject(mme_ue, + OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); + OGS_FSM_TRAN(s, &emm_state_exception); + break; + } + mme_gtp1_send_sgsn_context_request(sgsn, mme_ue); + /* FIXME: use a specific FSM state here to state we are waiting for resolution from Gn? */ + break; + } + if (!MME_UE_HAVE_IMSI(mme_ue)) { ogs_info("TAU request : Unknown UE"); r = nas_eps_send_tau_reject(mme_ue, diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index 0a371c54b..9be948d7f 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -3184,6 +3184,11 @@ void mme_ue_confirm_guti(mme_ue_t *mme_ue) /* Clear Next GUTI */ mme_ue->next.m_tmsi = NULL; + + ogs_debug("Confirm GUTI[G:%d,C:%d,M_TMSI:0x%x]", + mme_ue->current.guti.mme_gid, + mme_ue->current.guti.mme_code, + mme_ue->current.guti.m_tmsi); } static bool compare_ue_info(mme_sgw_t *node, enb_ue_t *enb_ue) diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index 359f0068a..5e5671dd2 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -489,6 +489,8 @@ struct mme_ue_s { } ul_count; uint8_t kenb[OGS_SHA256_DIGEST_SIZE]; uint8_t hash_mme[OGS_HASH_MME_LEN]; + uint32_t nonceue, noncemme; + uint8_t gprs_ciphering_key_sequence_number; struct { ED2(uint8_t nhcc_spare:5;, diff --git a/src/mme/mme-gn-build.c b/src/mme/mme-gn-build.c index 76acd0fdb..3882e0365 100644 --- a/src/mme/mme-gn-build.c +++ b/src/mme/mme-gn-build.c @@ -20,11 +20,11 @@ #include "mme-context.h" #include "mme-gn-build.h" +#include "mme-gn-handler.h" static int sess_fill_mm_context_decoded(mme_sess_t *sess, ogs_gtp1_mm_context_decoded_t *mmctx_dec) { mme_ue_t *mme_ue = sess->mme_ue; - mme_bearer_t *bearer = NULL; *mmctx_dec = (ogs_gtp1_mm_context_decoded_t) { .gupii = 1, /* Integrity Protection not required */ .ugipai = 1, /* Ignore "Used GPRS integrity protection algorithm" field" */ @@ -40,7 +40,6 @@ static int sess_fill_mm_context_decoded(mme_sess_t *sess, ogs_gtp1_mm_context_de .nrsrna = 0, }; - //TODO: derive cK Ki from mme_ue->kasme ogs_kdf_ck_ik_idle_mobility(mme_ue->ul_count.i32, mme_ue->kasme, &mmctx_dec->ck[0], &mmctx_dec->ik[0]); mmctx_dec->imeisv_len = sizeof(mme_ue->nas_mobile_identity_imeisv); @@ -49,12 +48,6 @@ static int sess_fill_mm_context_decoded(mme_sess_t *sess, ogs_gtp1_mm_context_de mmctx_dec->ms_network_capability_len = mme_ue->ms_network_capability.length; memcpy(&mmctx_dec->ms_network_capability[0], ((uint8_t*)&mme_ue->ms_network_capability)+1, sizeof(mme_ue->ms_network_capability) - 1); - ogs_list_for_each(&sess->bearer_list, bearer) { - - /* FIXME: only 1 PDP Context supported in the message so far. */ - break; - } - return OGS_OK; } @@ -177,6 +170,103 @@ static int sess_fill_pdp_context_decoded(mme_sess_t *sess, ogs_gtp1_pdp_context_ return OGS_OK; } +/* 3GPP TS 29.060 7.5.3 SGSN Context Request */ +ogs_pkbuf_t *mme_gn_build_sgsn_context_request( + mme_ue_t *mme_ue) +{ + ogs_gtp1_message_t gtp1_message; + ogs_gtp1_sgsn_context_request_t *req = NULL; + ogs_nas_rai_t rai; + mme_p_tmsi_t ptmsi; + uint32_t ptmsi_sig; + ogs_gtp1_gsn_addr_t mme_gnc_gsnaddr, mme_gnc_alt_gsnaddr; + int gsn_len; + int rv; + + ogs_debug("[Gn] build SGSN Context Request"); + + ogs_assert(mme_ue); + + req = >p1_message.sgsn_context_request; + memset(>p1_message, 0, sizeof(ogs_gtp1_message_t)); + + guti_to_rai_ptmsi(&mme_ue->next.guti, &rai, &ptmsi, &ptmsi_sig); + + req->imsi.presence = 0; + + req->routeing_area_identity.presence = 1; + req->routeing_area_identity.data = &rai; + req->routeing_area_identity.len = sizeof(ogs_nas_rai_t); + + req->temporary_logical_link_identifier.presence = 0; + + req->packet_tmsi.presence = 1; + req->packet_tmsi.u32 = be32toh(ptmsi); + + req->p_tmsi_signature.presence = 1; + req->p_tmsi_signature.u24 = ptmsi_sig; + + req->ms_validated.presence = 0; + + req->tunnel_endpoint_identifier_control_plane.presence = 1; + req->tunnel_endpoint_identifier_control_plane.u32 = mme_ue->gn.mme_gn_teid; + + /* SGSN Address for Control Plane */ + if (ogs_gtp_self()->gtpc_addr && ogs_gtp_self()->gtpc_addr6) { + rv = ogs_gtp1_sockaddr_to_gsn_addr(NULL, ogs_gtp_self()->gtpc_addr6, + &mme_gnc_gsnaddr, &gsn_len); + if (rv != OGS_OK) { + ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed"); + return NULL; + } + req->sgsn_address_for_control_plane.presence = 1; + req->sgsn_address_for_control_plane.data = &mme_gnc_gsnaddr; + req->sgsn_address_for_control_plane.len = gsn_len; + + rv = ogs_gtp1_sockaddr_to_gsn_addr(ogs_gtp_self()->gtpc_addr, NULL, + &mme_gnc_alt_gsnaddr, &gsn_len); + if (rv != OGS_OK) { + ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed"); + return NULL; + } + req->sgsn_address_for_control_plane.presence = 1; + req->sgsn_address_for_control_plane.data = &mme_gnc_alt_gsnaddr; + req->sgsn_address_for_control_plane.len = gsn_len; + } else if (ogs_gtp_self()->gtpc_addr6) { + rv = ogs_gtp1_sockaddr_to_gsn_addr(NULL, ogs_gtp_self()->gtpc_addr6, + &mme_gnc_gsnaddr, &gsn_len); + if (rv != OGS_OK) { + ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed"); + return NULL; + } + req->sgsn_address_for_control_plane.presence = 1; + req->sgsn_address_for_control_plane.data = &mme_gnc_gsnaddr; + req->sgsn_address_for_control_plane.len = gsn_len; + req->alternative_sgsn_address_for_control_plane.presence = 0; + } else { + rv = ogs_gtp1_sockaddr_to_gsn_addr(ogs_gtp_self()->gtpc_addr, NULL, + &mme_gnc_gsnaddr, &gsn_len); + if (rv != OGS_OK) { + ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed"); + return NULL; + } + req->sgsn_address_for_control_plane.presence = 1; + req->sgsn_address_for_control_plane.data = &mme_gnc_gsnaddr; + req->sgsn_address_for_control_plane.len = gsn_len; + req->alternative_sgsn_address_for_control_plane.presence = 0; + } + + req->sgsn_number.presence = 0; + + req->rat_type.presence = 1; + req->rat_type.u8 = OGS_GTP1_RAT_TYPE_EUTRAN; + + req->hop_counter.presence = 0; + + gtp1_message.h.type = OGS_GTP1_SGSN_CONTEXT_REQUEST_TYPE; + return ogs_gtp1_build_msg(>p1_message); +} + /* 3GPP TS 29.060 7.5.4 SGSN Context Response */ ogs_pkbuf_t *mme_gn_build_sgsn_context_response( mme_ue_t *mme_ue, uint8_t cause) @@ -275,6 +365,67 @@ ogs_pkbuf_t *mme_gn_build_sgsn_context_response( rsp->sgsn_address_for_control_plane.len = gsn_len; +build_ret: + return ogs_gtp1_build_msg(>p1_message); +} + +/* 3GPP TS 29.060 7.5.5 SGSN Context Acknowledge */ +ogs_pkbuf_t *mme_gn_build_sgsn_context_ack( + mme_ue_t *mme_ue, uint8_t cause) +{ + ogs_gtp1_message_t gtp1_message; + ogs_gtp1_sgsn_context_acknowledge_t *ack = NULL; + mme_sess_t *sess = NULL; + ogs_gtp1_gsn_addr_t reserved_gnc_gsnaddr; + ogs_gtp1_teidII_t teidII; + + ogs_debug("[Gn] build SGSN Context Acknowledge"); + + ack = >p1_message.sgsn_context_acknowledge; + memset(>p1_message, 0, sizeof(ogs_gtp1_message_t)); + gtp1_message.h.type = OGS_GTP1_SGSN_CONTEXT_ACKNOWLEDGE_TYPE; + + /* 3GPP TS 29.060 7.7.1 Cause, Mandatory */ + ack->cause.presence = 1; + ack->cause.u8 = cause; + + if (cause != OGS_GTP1_CAUSE_REQUEST_ACCEPTED) + goto build_ret; + + ogs_list_for_each(&mme_ue->sess_list, sess) { + mme_bearer_t *bearer = NULL; + if (!MME_HAVE_SGW_S1U_PATH(sess)) + continue; + ogs_list_for_each(&sess->bearer_list, bearer) { + /* MME, acting as a new SGSN, shall send the following values in the SGSN Context + * Acknowledge message in order to discard the packets received from the old SGSN + * (because the MME and the S4-SGSN do not have user plane): + * - any reserved TEID (e.g. all 0's, or all 1's) for Tunnel Endpoint Identifier + * Data II value; + * - any reserved (implementation dependent) IP address for SGSN Address for user + traffic value. + */ + /* 3GPP TS 29.060 7.7.15 Tunnel Endpoint Identifier Data II, Conditional */ + teidII.nsapi = bearer->ebi; + teidII.teid = 0xffffffff; + ack->tunnel_endpoint_identifier_data_ii.presence = 1; + ack->tunnel_endpoint_identifier_data_ii.data = &teidII; + ack->tunnel_endpoint_identifier_data_ii.len = sizeof(teidII); + + /* Use IPv4 0.0.0.0 as reserved address: */ + reserved_gnc_gsnaddr.addr = 0; + ack->sgsn_address_for_user_traffic.presence = 1; + ack->sgsn_address_for_user_traffic.data = &reserved_gnc_gsnaddr; + ack->sgsn_address_for_user_traffic.len = OGS_GTP_GSN_ADDRESS_IPV4_LEN; + + /* FIXME: only 1 PDP Context supported in the message so far. */ + break; + } + /* FIXME: right now we only support encoding 1 context in the message. */ + break; + } + + build_ret: return ogs_gtp1_build_msg(>p1_message); } diff --git a/src/mme/mme-gn-build.h b/src/mme/mme-gn-build.h index 6a7af32b5..642ff174b 100644 --- a/src/mme/mme-gn-build.h +++ b/src/mme/mme-gn-build.h @@ -30,9 +30,15 @@ extern "C" { } #endif +ogs_pkbuf_t *mme_gn_build_sgsn_context_request( + mme_ue_t *mme_ue); + ogs_pkbuf_t *mme_gn_build_sgsn_context_response( mme_ue_t *mme_ue, uint8_t cause); +ogs_pkbuf_t *mme_gn_build_sgsn_context_ack( + mme_ue_t *mme_ue, uint8_t cause); + ogs_pkbuf_t *mme_gn_build_ran_information_relay( uint8_t type, const uint8_t *buf, size_t len, const ogs_nas_rai_t *rai, uint16_t cell_id); diff --git a/src/mme/mme-gn-handler.c b/src/mme/mme-gn-handler.c index 149ec76e9..4cc0e860e 100644 --- a/src/mme/mme-gn-handler.c +++ b/src/mme/mme-gn-handler.c @@ -30,6 +30,7 @@ #include "mme-gn-handler.h" #include "s1ap-path.h" +#include "nas-path.h" void mme_gn_handle_echo_request( ogs_gtp_xact_t *xact, ogs_gtp1_echo_request_t *req) @@ -68,12 +69,26 @@ static int decode_global_enb_id(S1AP_Global_ENB_ID_t *glob_enb_id, const uint8_t return OGS_OK; } -/* 3GPP TS 23.003 2.8.2.2 Mapping from RAI and P-TMSI to GUT */ +/* 3GPP TS 23.003 2.8.2.1 Mapping from GUTI to RAI, P-TMSI and P-TMSI signature */ +void guti_to_rai_ptmsi(const ogs_nas_eps_guti_t *nas_guti, ogs_nas_rai_t *rai, mme_p_tmsi_t *ptmsi, uint32_t *ptmsi_sig) +{ + rai->lai.nas_plmn_id = nas_guti->nas_plmn_id; + rai->lai.lac = nas_guti->mme_gid; + rai->rac = nas_guti->mme_code; + if (ptmsi) + *ptmsi = 0xC0000000 | + (nas_guti->m_tmsi & 0x3f000000) | + (nas_guti->mme_code & 0x0ff) << 16 | + (nas_guti->m_tmsi & 0x0000ffff); + if (ptmsi_sig) + *ptmsi_sig = (nas_guti->m_tmsi & 0x00ff0000); +} + +/* 3GPP TS 23.003 2.8.2.2 Mapping from RAI and P-TMSI to GUTI */ static void rai_ptmsi_to_guti(const ogs_nas_rai_t *rai, mme_p_tmsi_t ptmsi, uint32_t ptmsi_sig, ogs_nas_eps_guti_t *nas_guti) { - uint16_t lac = be16toh(rai->lai.lac);; - nas_guti->nas_plmn_id =rai->lai.nas_plmn_id; - nas_guti->mme_gid = lac; + nas_guti->nas_plmn_id = rai->lai.nas_plmn_id; + nas_guti->mme_gid = rai->lai.lac; nas_guti->mme_code = rai->rac; nas_guti->m_tmsi = 0xC0000000 | (ptmsi & 0x3f000000) | (ptmsi_sig & 0x00ff0000) | (ptmsi & 0x0000ffff); } @@ -83,7 +98,8 @@ void mme_gn_handle_sgsn_context_request( ogs_gtp_xact_t *xact, ogs_gtp1_sgsn_context_request_t *req) { ogs_nas_eps_guti_t nas_guti; - ogs_nas_rai_t *rai; + ogs_plmn_id_t plmn_id; + ogs_nas_rai_t rai; mme_ue_t *mme_ue = NULL; int rv; @@ -94,9 +110,10 @@ void mme_gn_handle_sgsn_context_request( mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact); return; } - if (req->routeing_area_identity.len != sizeof(*rai)) { + + if (req->routeing_area_identity.len != sizeof(rai)) { ogs_warn("[Gn] Rx SGSN Context Request RAI wrong size %u vs exp %zu!", - req->routeing_area_identity.len, sizeof(*rai)); + req->routeing_area_identity.len, sizeof(rai)); mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_INCORRECT, xact); return; } @@ -126,17 +143,35 @@ void mme_gn_handle_sgsn_context_request( return; } - rai = req->routeing_area_identity.data; + memcpy(&rai, req->routeing_area_identity.data, sizeof(rai)); + rai.lai.lac = be16toh(rai.lai.lac); + ogs_nas_to_plmn_id(&plmn_id, &rai.lai.nas_plmn_id); + ogs_debug(" RAI[MCC:%u MNC:%u LAC:%u RAC:%u]", + ogs_plmn_id_mcc(&plmn_id), ogs_plmn_id_mnc(&plmn_id), + rai.lai.lac, rai.rac); if (req->imsi.presence) { + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + ogs_buffer_to_bcd(req->imsi.data, req->imsi.len, imsi_bcd); + ogs_debug(" IMSI[%s]", imsi_bcd); mme_ue = mme_ue_find_by_imsi(req->imsi.data, req->imsi.len); + if (!mme_ue) + ogs_warn("[Gn] Rx SGSN Context Request: Unknown UE with IMSI[%s]", imsi_bcd); } else if (req->packet_tmsi.presence) { /* P-TMSI */ if (!req->p_tmsi_signature.presence) { ogs_warn("[Gn] Rx SGSN Context Request with 'P-TMSI' but no P-TMSI Signature! Assuming value 0."); req->p_tmsi_signature.u24 = 0; } - rai_ptmsi_to_guti(rai, req->packet_tmsi.u32, req->p_tmsi_signature.u24, &nas_guti); + rai_ptmsi_to_guti(&rai, req->packet_tmsi.u32, req->p_tmsi_signature.u24, &nas_guti); + ogs_debug(" PTMSI[0x%08x] PTMSI_SIG[0x%06x] -> GUTI[G:%d,C:%d,M_TMSI:0x%x]", + req->packet_tmsi.u32, req->p_tmsi_signature.u24, + nas_guti.mme_gid, nas_guti.mme_code, nas_guti.m_tmsi); mme_ue = mme_ue_find_by_guti(&nas_guti); + if (!mme_ue) + ogs_warn("[Gn] Rx SGSN Context Request: Unknown UE with RAI[MCC:%u MNC:%u LAC:%u RAC:%u] PTMSI[0x%08x] PTMSI_SIG[0x%06x] -> GUTI[G:%d,C:%d,M_TMSI:0x%x]", + ogs_plmn_id_mcc(&plmn_id), ogs_plmn_id_mnc(&plmn_id), rai.lai.lac, rai.rac, + req->packet_tmsi.u32, req->p_tmsi_signature.u24, + nas_guti.mme_gid, nas_guti.mme_code, nas_guti.m_tmsi); } else if (req->temporary_logical_link_identifier.presence) { if (!req->p_tmsi_signature.presence) { ogs_warn("[Gn] Rx SGSN Context Request with 'TLLI' but no P-TMSI Signature! Assuming value 0."); @@ -145,8 +180,16 @@ void mme_gn_handle_sgsn_context_request( /* TS 29.060 7.5.3 "The TLLI/P-TMSI and RAI is a foreign TLLI/P-TMSI and an RAI in the old SGSN." * A foregin TLLI is "tlli = (p_tmsi & 0x3fffffff) | 0x80000000", and since we only use 0x3fffffff * bits of P-TMSI to derive the GUTI, it's totally fine passing the TLLI as P-TMSI. */ - rai_ptmsi_to_guti(rai, req->temporary_logical_link_identifier.u32, req->p_tmsi_signature.u24, &nas_guti); + rai_ptmsi_to_guti(&rai, req->temporary_logical_link_identifier.u32, req->p_tmsi_signature.u24, &nas_guti); + ogs_debug(" TLLI[0x%08x] PTMSI_SIG[0x%06x] -> GUTI[G:%d,C:%d,M_TMSI:0x%x]", + req->temporary_logical_link_identifier.u32, req->p_tmsi_signature.u24, + nas_guti.mme_gid, nas_guti.mme_code, nas_guti.m_tmsi); mme_ue = mme_ue_find_by_guti(&nas_guti); + if (!mme_ue) + ogs_warn("[Gn] Rx SGSN Context Request: Unknown UE with RAI[MCC:%u MNC:%u LAC:%u RAC:%u] TLLI[0x%08x] PTMSI_SIG[0x%06x] -> GUTI[G:%d,C:%d,M_TMSI:0x%x]", + ogs_plmn_id_mcc(&plmn_id), ogs_plmn_id_mnc(&plmn_id), rai.lai.lac, rai.rac, + req->temporary_logical_link_identifier.u32, req->p_tmsi_signature.u24, + nas_guti.mme_gid, nas_guti.mme_code, nas_guti.m_tmsi); } if (!mme_ue) { @@ -177,6 +220,223 @@ void mme_gn_handle_sgsn_context_request( mme_gtp1_send_sgsn_context_response(mme_ue, OGS_GTP1_CAUSE_REQUEST_ACCEPTED, xact); } +static mme_sess_t *mme_ue_session_from_gtp1_pdp_ctx(mme_ue_t *mme_ue, const ogs_gtp1_pdp_context_decoded_t *gtp1_pdp_ctx) +{ + mme_sess_t *sess = NULL; + mme_bearer_t *bearer = NULL; + const ogs_gtp1_qos_profile_decoded_t *qos_pdec = >p1_pdp_ctx->qos_sub; + uint8_t pti = gtp1_pdp_ctx->trans_id; + uint8_t qci = 0; + ogs_session_t *ogs_sess; + + ogs_sess = mme_session_find_by_apn(mme_ue, gtp1_pdp_ctx->apn); + if (!ogs_sess) { + ogs_assert(mme_ue->num_of_session < OGS_MAX_NUM_OF_SESS); + ogs_sess = &mme_ue->session[mme_ue->num_of_session]; + mme_ue->num_of_session++; + ogs_sess->name = ogs_strdup(gtp1_pdp_ctx->apn); + } + ogs_sess->smf_ip = gtp1_pdp_ctx->ggsn_address_c; + ogs_sess->context_identifier = gtp1_pdp_ctx->pdp_ctx_id; + ogs_sess->session_type = gtp1_pdp_ctx->pdp_type_num[0]; + ogs_sess->ue_ip = gtp1_pdp_ctx->pdp_address[0]; + /* TODO: sess->paa with gtp1_pdp_ctx->pdp_address[0], + using/implementing ogs_gtp2_ip_to_paa ? */ + ogs_ip_to_paa(&ogs_sess->ue_ip, &ogs_sess->paa); + + /* 3GPP TS 23.060 section 9.2.1A: "The QoS profiles of the PDP context and EPS bearer are mapped as specified in TS 23.401" + * 3GPP TS 23.401 Annex E: "Mapping between EPS and Release 99 QoS parameters" + */ + ogs_gtp1_qos_profile_to_qci(qos_pdec, &qci); + ogs_sess->qos.index = qci; + ogs_sess->qos.arp.priority_level = qos_pdec->qos_profile.arp; /* 3GPP TS 23.401 Annex E Table E.2 */ + ogs_sess->qos.arp.pre_emption_capability = 0; /* ignored as per 3GPP TS 23.401 Annex E */ + ogs_sess->qos.arp.pre_emption_vulnerability = 0; /* ignored as per 3GPP TS 23.401 Annex E */ + if (qos_pdec->data_octet6_to_13_present) { + ogs_sess->ambr.downlink = qos_pdec->dec_mbr_kbps_dl * 1000; + ogs_sess->ambr.uplink = qos_pdec->dec_mbr_kbps_ul * 1000; + } + + sess = mme_sess_find_by_pti(mme_ue, pti); + if (!sess) { + sess = mme_sess_add(mme_ue, pti); + ogs_assert(sess); + } + + sess->session = ogs_sess; + sess->pgw_s5c_teid = gtp1_pdp_ctx->ul_teic; + sess->pgw_s5c_ip = gtp1_pdp_ctx->ggsn_address_c; + switch (ogs_sess->session_type) { + case OGS_PDU_SESSION_TYPE_IPV4: + sess->request_type.type = OGS_NAS_EPS_PDN_TYPE_IPV4; + break; + case OGS_PDU_SESSION_TYPE_IPV6: + sess->request_type.type = OGS_NAS_EPS_PDN_TYPE_IPV6; + break; + case OGS_PDU_SESSION_TYPE_IPV4V6: + sess->request_type.type = OGS_NAS_EPS_PDN_TYPE_IPV4V6; + break; + } + sess->request_type.value = OGS_NAS_EPS_REQUEST_TYPE_INITIAL; + + bearer = mme_bearer_find_by_sess_ebi(sess, gtp1_pdp_ctx->nsapi); + if (!bearer) { + bearer = mme_default_bearer_in_sess(sess); + if (!bearer) { + bearer = mme_bearer_add(sess); + ogs_assert(bearer); + } + } + bearer->pgw_s5u_teid = gtp1_pdp_ctx->ul_teid; + bearer->pgw_s5u_ip = gtp1_pdp_ctx->ggsn_address_u; + /* Send invalid Remote Address and TEID since it makes no sense that SGW + * forwards GTPUv2 traffic to SGSN: */ + bearer->enb_s1u_ip.ipv4 = 1; + bearer->enb_s1u_ip.addr = 0; + bearer->enb_s1u_teid = 0xffffffff; + + return sess; +} + +/* TS 29.060 7.5.4 SGSN Context Response */ +int mme_gn_handle_sgsn_context_response( + ogs_gtp_xact_t *xact, mme_ue_t *mme_ue, ogs_gtp1_sgsn_context_response_t *resp) +{ + int rv; + int gtp1_cause, emm_cause = OGS_NAS_EMM_CAUSE_NETWORK_FAILURE; + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + ogs_gtp1_mm_context_decoded_t gtp1_mm_ctx; + ogs_gtp1_pdp_context_decoded_t gtp1_pdp_ctx; + mme_sess_t *sess = NULL; + uint8_t ret_cause = OGS_GTP1_CAUSE_REQUEST_ACCEPTED; + + ogs_debug("[Gn] Rx SGSN Context Response"); + + rv = ogs_gtp_xact_commit(xact); + if (rv != OGS_OK) { + ogs_error("ogs_gtp_xact_commit() failed"); + return OGS_GTP1_CAUSE_SYSTEM_FAILURE; + } + + if (!mme_ue) { + ogs_error("MME-UE Context has already been removed"); + return OGS_GTP1_CAUSE_IMSI_IMEI_NOT_KNOWN; + } + + switch (resp->cause.u8) { + case OGS_GTP1_CAUSE_REQUEST_ACCEPTED: + break; /* Handle below */ + case OGS_GTP1_CAUSE_TGT_ACC_RESTRICTED_SUBSCRIBER: + emm_cause = OGS_NAS_EMM_CAUSE_REQUESTED_SERVICE_OPTION_NOT_AUTHORIZED_IN_THIS_PLMN; + break; + case OGS_GTP1_CAUSE_IMSI_IMEI_NOT_KNOWN: + case OGS_GTP1_CAUSE_SYSTEM_FAILURE: + case OGS_GTP1_CAUSE_MANDATORY_IE_INCORRECT: + case OGS_GTP1_CAUSE_MANDATORY_IE_MISSING: + case OGS_GTP1_CAUSE_OPTIONAL_IE_INCORRECT: + case OGS_GTP1_CAUSE_INVALID_MESSAGE_FORMAT: + case OGS_GTP1_CAUSE_P_TMSI_SIGNATURE_MISMATCH: + default: + emm_cause = OGS_NAS_EMM_CAUSE_NETWORK_FAILURE; + break; + } + + if (resp->cause.u8 != OGS_GTP1_CAUSE_REQUEST_ACCEPTED) { + ogs_error("[Gn] Rx SGSN Context Response cause:%u", resp->cause.u8); + rv = nas_eps_send_tau_reject(mme_ue, emm_cause); + return OGS_GTP1_CAUSE_SYSTEM_FAILURE; + } + + if (!resp->imsi.presence) { + ogs_error("[Gn] Rx SGSN Context Response with no IMSI!"); + gtp1_cause = OGS_GTP1_CAUSE_MANDATORY_IE_MISSING; + goto nack_and_reject; + } + + ogs_buffer_to_bcd(resp->imsi.data, resp->imsi.len, imsi_bcd); + ogs_info(" IMSI[%s]", imsi_bcd); + mme_ue_set_imsi(mme_ue, imsi_bcd); + + if (!resp->tunnel_endpoint_identifier_control_plane.presence) { + ogs_error("[Gn] Rx SGSN Context Response with no Tunnel Endpoint Identifier Control Plane!"); + gtp1_cause = OGS_GTP1_CAUSE_MANDATORY_IE_MISSING; + goto nack_and_reject; + } + + mme_ue->gn.sgsn_gn_teid = resp->tunnel_endpoint_identifier_control_plane.u32; + + if (!resp->mm_context.presence) { + ogs_error("[Gn] Rx SGSN Context Response with no MM Context!"); + gtp1_cause = OGS_GTP1_CAUSE_MANDATORY_IE_MISSING; + goto nack_and_reject; + } + + if (!resp->pdp_context.presence) { + ogs_error("[Gn] Rx SGSN Context Response with no PDP Context!"); + gtp1_cause = OGS_GTP1_CAUSE_MANDATORY_IE_MISSING; + goto nack_and_reject; + } + + rv = ogs_gtp1_parse_mm_context(>p1_mm_ctx, &resp->mm_context); + if (rv != OGS_OK) { + ogs_error("[Gn] Rx SGSN Context Response: Failed parsing MM Context"); + gtp1_cause = OGS_GTP1_CAUSE_MANDATORY_IE_INCORRECT; + goto nack_and_reject; + } + + rv = ogs_gtp1_parse_pdp_context(>p1_pdp_ctx, &resp->pdp_context); + if (rv != OGS_OK) { + ogs_error("[Gn] Rx SGSN Context Response: Failed parsing PDP Context"); + gtp1_cause = OGS_GTP1_CAUSE_MANDATORY_IE_INCORRECT; + goto nack_and_reject; + } + + if (gtp1_mm_ctx.imeisv_len > 0) { + memcpy(&mme_ue->nas_mobile_identity_imeisv, >p1_mm_ctx.imeisv[0], + ogs_min(gtp1_mm_ctx.imeisv_len, sizeof(mme_ue->nas_mobile_identity_imeisv))); + } else { + /* 3GPP TS 23.401 D3.6: we need to request IMEI to the UE over EUTRAN */ + ret_cause = OGS_GTP1_CAUSE_REQUEST_IMEI; + } + + mme_ue->ms_network_capability.length = gtp1_mm_ctx.ms_network_capability_len; + if (gtp1_mm_ctx.ms_network_capability_len > 0) + memcpy(((uint8_t*)&mme_ue->ms_network_capability)+1, >p1_mm_ctx.ms_network_capability[0], + ogs_min(gtp1_mm_ctx.ms_network_capability_len, sizeof(mme_ue->ms_network_capability) - 1)); + /* TODO: how to fill first byte of mme_ue->ms_network_capability ? */ + + mme_ue->nas_eps.ksi = gtp1_mm_ctx.ksi; + /* 3GPP TS 33.401 A.10, A.11: */ + mme_ue->noncemme = ogs_random32(); + /* 3GPP TS 33.401 7.2.6.2 Establishment of keys for cryptographically protected radio bearers: */ + /* See also 3GPP TS 33.401 9.1.2 */ + ogs_kdf_kasme_idle_mobility(gtp1_mm_ctx.ck, gtp1_mm_ctx.ik, mme_ue->nonceue, mme_ue->noncemme, mme_ue->kasme); + ogs_kdf_kenb(mme_ue->kasme, mme_ue->ul_count.i32, mme_ue->kenb); + ogs_kdf_nh_enb(mme_ue->kasme, mme_ue->kenb, mme_ue->nh); + mme_ue->nhcc = 1; + + if (gtp1_mm_ctx.num_vectors > 0) { + mme_ue->xres_len = gtp1_mm_ctx.auth_quintuplets[0].xres_len; + memcpy(mme_ue->xres, gtp1_mm_ctx.auth_quintuplets[0].xres, mme_ue->xres_len); + memcpy(mme_ue->rand, gtp1_mm_ctx.auth_quintuplets[0].rand, OGS_RAND_LEN); + memcpy(mme_ue->autn, gtp1_mm_ctx.auth_quintuplets[0].autn, OGS_AUTN_LEN); + } + + sess = mme_ue_session_from_gtp1_pdp_ctx(mme_ue, >p1_pdp_ctx); + + rv = mme_gtp1_send_sgsn_context_ack(mme_ue, OGS_GTP1_CAUSE_REQUEST_ACCEPTED, xact); + + mme_gtp_send_create_session_request(sess, OGS_GTP_CREATE_IN_TRACKING_AREA_UPDATE); + + return ret_cause; + +nack_and_reject: + rv = mme_gtp1_send_sgsn_context_ack(mme_ue, gtp1_cause, xact); + ogs_info("[%s] TAU Reject [OGS_NAS_EMM_CAUSE:%d]", mme_ue->imsi_bcd, emm_cause); + rv = nas_eps_send_tau_reject(mme_ue, emm_cause); + return OGS_GTP1_CAUSE_SYSTEM_FAILURE; +} + /* TS 29.060 7.5.5 SGSN Context Acknowledge */ void mme_gn_handle_sgsn_context_acknowledge( ogs_gtp_xact_t *xact, mme_ue_t *mme_ue, ogs_gtp1_sgsn_context_acknowledge_t *req) diff --git a/src/mme/mme-gn-handler.h b/src/mme/mme-gn-handler.h index d6138a4c5..0c3a6156f 100644 --- a/src/mme/mme-gn-handler.h +++ b/src/mme/mme-gn-handler.h @@ -35,12 +35,18 @@ void mme_gn_handle_echo_response( void mme_gn_handle_sgsn_context_request( ogs_gtp_xact_t *xact, ogs_gtp1_sgsn_context_request_t *req); +int mme_gn_handle_sgsn_context_response( + ogs_gtp_xact_t *xact, mme_ue_t *mme_ue, ogs_gtp1_sgsn_context_response_t *resp); + void mme_gn_handle_sgsn_context_acknowledge( ogs_gtp_xact_t *xact, mme_ue_t *mme_ue, ogs_gtp1_sgsn_context_acknowledge_t *req); void mme_gn_handle_ran_information_relay( ogs_gtp_xact_t *xact, ogs_gtp1_ran_information_relay_t *req); +void guti_to_rai_ptmsi(const ogs_nas_eps_guti_t *nas_guti, ogs_nas_rai_t *rai, + mme_p_tmsi_t *ptmsi, uint32_t *ptmsi_sig); + #ifdef __cplusplus } #endif diff --git a/src/mme/mme-gtp-path.c b/src/mme/mme-gtp-path.c index f70ba6676..9a9b0b0a4 100644 --- a/src/mme/mme-gtp-path.c +++ b/src/mme/mme-gtp-path.c @@ -758,6 +758,45 @@ int mme_gtp_send_bearer_resource_command( return rv; } +/************************* + * GTPv1C (Gn interface): + *************************/ + +int mme_gtp1_send_sgsn_context_request( + mme_sgsn_t *sgsn, mme_ue_t *mme_ue) +{ + int rv; + ogs_gtp1_header_t h; + ogs_pkbuf_t *pkbuf = NULL; + ogs_gtp_xact_t *xact = NULL; + + ogs_assert(sgsn); + + memset(&h, 0, sizeof(ogs_gtp1_header_t)); + h.type = OGS_GTP1_SGSN_CONTEXT_REQUEST_TYPE; + h.teid = 0; + + pkbuf = mme_gn_build_sgsn_context_request(mme_ue); + if (!pkbuf) { + ogs_error("mme_gn_build_ran_information_relay() failed"); + return OGS_ERROR; + } + + xact = ogs_gtp1_xact_local_create(&sgsn->gnode, &h, pkbuf, NULL, NULL); + if (!xact) { + ogs_error("ogs_gtp1_xact_local_create() failed"); + return OGS_ERROR; + } + /* TS 29.060 8.2: "The SGSN Context Request message, where the Tunnel + * Endpoint Identifier shall be set to all zeroes." */ + xact->local_teid = 0; + + rv = ogs_gtp_xact_commit(xact); + ogs_expect(rv == OGS_OK); + + return rv; +} + int mme_gtp1_send_sgsn_context_response( mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact) { @@ -788,6 +827,38 @@ int mme_gtp1_send_sgsn_context_response( return rv; } +int mme_gtp1_send_sgsn_context_ack( + mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact) +{ + int rv; + ogs_gtp1_header_t h; + ogs_pkbuf_t *pkbuf = NULL; + + ogs_assert(mme_ue); + + memset(&h, 0, sizeof(ogs_gtp1_header_t)); + h.type = OGS_GTP1_SGSN_CONTEXT_ACKNOWLEDGE_TYPE; + h.teid = mme_ue->gn.sgsn_gn_teid; + + pkbuf = mme_gn_build_sgsn_context_ack(mme_ue, cause); + if (!pkbuf) { + ogs_error("mme_gn_build_sgsn_context_response() failed"); + return OGS_ERROR; + } + xact->local_teid = mme_ue->gn.mme_gn_teid; + + rv = ogs_gtp1_xact_update_tx(xact, &h, pkbuf); + if (rv != OGS_OK) { + ogs_error("ogs_gtp1_xact_update_tx() failed"); + return OGS_ERROR; + } + + rv = ogs_gtp_xact_commit(xact); + ogs_expect(rv == OGS_OK); + + return rv; +} + int mme_gtp1_send_ran_information_relay( mme_sgsn_t *sgsn, const uint8_t *buf, size_t len, const ogs_nas_rai_t *rai, uint16_t cell_id) diff --git a/src/mme/mme-gtp-path.h b/src/mme/mme-gtp-path.h index 1e41d34ff..f741559c5 100644 --- a/src/mme/mme-gtp-path.h +++ b/src/mme/mme-gtp-path.h @@ -55,9 +55,15 @@ int mme_gtp_send_delete_indirect_data_forwarding_tunnel_request( int mme_gtp_send_bearer_resource_command( mme_bearer_t *bearer, ogs_nas_eps_message_t *nas_message); +int mme_gtp1_send_sgsn_context_request( + mme_sgsn_t *sgsn, mme_ue_t *mme_ue); + int mme_gtp1_send_sgsn_context_response( mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact); +int mme_gtp1_send_sgsn_context_ack( + mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact); + int mme_gtp1_send_ran_information_relay( mme_sgsn_t *sgsn, const uint8_t *buf, size_t len, const ogs_nas_rai_t *rai, uint16_t cell_id); diff --git a/src/mme/mme-s11-build.c b/src/mme/mme-s11-build.c index 3b58c65b6..731eaa1e7 100644 --- a/src/mme/mme-s11-build.c +++ b/src/mme/mme-s11-build.c @@ -219,7 +219,8 @@ ogs_pkbuf_t *mme_s11_build_create_session_request( if (sess->request_type.value == OGS_NAS_EPS_REQUEST_TYPE_HANDOVER) indication.handover_indication = 1; - if (create_action == OGS_GTP_CREATE_IN_PATH_SWITCH_REQUEST) + if (create_action == OGS_GTP_CREATE_IN_PATH_SWITCH_REQUEST || + create_action == OGS_GTP_CREATE_IN_TRACKING_AREA_UPDATE) indication.operation_indication = 1; session->paa.session_type = req->pdn_type.u8; @@ -288,6 +289,10 @@ ogs_pkbuf_t *mme_s11_build_create_session_request( &enb_s1u_teid[i]; req->bearer_contexts_to_be_created[i].s1_u_enodeb_f_teid.len = enb_s1u_len[i]; + } + + if (create_action == OGS_GTP_CREATE_IN_PATH_SWITCH_REQUEST || + create_action == OGS_GTP_CREATE_IN_TRACKING_AREA_UPDATE) { /* Data Plane(DL) : PGW-S5U */ memset(&pgw_s5u_teid[i], 0, sizeof(ogs_gtp2_f_teid_t)); diff --git a/src/mme/mme-s11-handler.c b/src/mme/mme-s11-handler.c index 0d140253d..c4945d8ac 100644 --- a/src/mme/mme-s11-handler.c +++ b/src/mme/mme-s11-handler.c @@ -232,6 +232,12 @@ void mme_s11_handle_create_session_response( OGS_NAS_ESM_CAUSE_NETWORK_FAILURE); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); + } else if (create_action == OGS_GTP_CREATE_IN_TRACKING_AREA_UPDATE) { + ogs_error("[%s] TAU reject [Cause:%d]", + mme_ue->imsi_bcd, session_cause); + r = nas_eps_send_tau_reject(mme_ue, OGS_NAS_EMM_CAUSE_NETWORK_FAILURE); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); } mme_send_delete_session_or_mme_ue_context_release(mme_ue); return; @@ -342,6 +348,10 @@ void mme_s11_handle_create_session_response( ogs_debug(" ENB_S1U_TEID[%d] SGW_S1U_TEID[%d] PGW_S5U_TEID[%d]", bearer->enb_s1u_teid, bearer->sgw_s1u_teid, bearer->pgw_s5u_teid); + + if (create_action == OGS_GTP_CREATE_IN_TRACKING_AREA_UPDATE && + !OGS_FSM_CHECK(&bearer->sm, esm_state_active)) + OGS_FSM_TRAN(&bearer->sm, esm_state_active); } /* Bearer Level QoS */ @@ -428,6 +438,9 @@ void mme_s11_handle_create_session_response( ogs_assert(r != OGS_ERROR); } + } else if (create_action == OGS_GTP_CREATE_IN_TRACKING_AREA_UPDATE) { + /* 3GPP TS 23.401 D.3.6 step 13, 14: */ + mme_s6a_send_ulr(mme_ue); } else if (create_action == OGS_GTP_CREATE_IN_UPLINK_NAS_TRANSPORT) { ogs_assert(OGS_PDU_SESSION_TYPE_IS_VALID(session->paa.session_type)); r = nas_eps_send_activate_default_bearer_context_request( diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index f61f1465a..039d53b97 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -674,6 +674,17 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) case OGS_GTP1_SGSN_CONTEXT_REQUEST_TYPE: mme_gn_handle_sgsn_context_request(xact, >p1_message.sgsn_context_request); break; + case OGS_GTP1_SGSN_CONTEXT_RESPONSE_TYPE: + /* 3GPP TS 23.401 Figure D.3.6-1 step 5 */ + rv = mme_gn_handle_sgsn_context_response(xact, mme_ue, >p1_message.sgsn_context_response); + if (rv == OGS_GTP1_CAUSE_ACCEPT) { + OGS_FSM_TRAN(&mme_ue->sm, &emm_state_initial_context_setup); + } else if (rv == OGS_GTP1_CAUSE_REQUEST_IMEI) { + OGS_FSM_TRAN(&mme_ue->sm, &emm_state_security_mode); + } else { + OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception); + } + break; case OGS_GTP1_SGSN_CONTEXT_ACKNOWLEDGE_TYPE: mme_gn_handle_sgsn_context_acknowledge(xact, mme_ue, >p1_message.sgsn_context_acknowledge); break;