diff --git a/src/mme/emm-handler.c b/src/mme/emm-handler.c index 868167b41e..837470a13e 100644 --- a/src/mme/emm-handler.c +++ b/src/mme/emm-handler.c @@ -185,7 +185,7 @@ int emm_handle_attach_request(mme_ue_t *mme_ue, OGS_NAS_SECURITY_ALGORITHMS_EIA0) { ogs_warn("Encrypt[0x%x] can be skipped with EEA0, " "but Integrity[0x%x] cannot be bypassed with EIA0", - mme_selected_enc_algorithm(mme_ue), + mme_selected_enc_algorithm(mme_ue), mme_selected_int_algorithm(mme_ue)); r = nas_eps_send_attach_reject(mme_ue, OGS_NAS_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH, @@ -549,6 +549,27 @@ int emm_handle_service_request( return OGS_OK; } + +bool emm_tau_request_guti_comes_from_sgsn(const ogs_nas_eps_tracking_area_update_request_t *tau_request) +{ + 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) { @@ -679,6 +700,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 9ea14b93a4..b6c82a79ad 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_guti_comes_from_sgsn( + 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 b1ce78098f..c557591c3c 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" @@ -285,7 +286,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); @@ -488,6 +491,25 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, break; } + if (emm_tau_request_guti_comes_from_sgsn(&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_from_plmn_id(&rai.lai.nas_plmn_id, &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); + } + mme_gtp1_send_sgsn_context_request(sgsn, mme_ue); + OGS_FSM_TRAN(s, &emm_state_registered); /* FIXME: use a specific FSM state here? */ + 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 371a229025..4a403464b3 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -2488,6 +2488,15 @@ mme_sgsn_t *mme_sgsn_find_by_routing_address(const ogs_nas_rai_t *rai, uint16_t } } + /* If no exact match found, try using any with same RAI: */ + ogs_list_for_each(&self.sgsn_list, sgsn) { + mme_sgsn_route_t *rt = NULL; + ogs_list_for_each(&sgsn->route_list, rt) { + if (memcmp(&rt->rai, rai, sizeof(ogs_nas_rai_t)) == 0) + return sgsn; + } + } + /* No route found, return default route if available: */ return mme_sgsn_find_by_default_routing_address(); } diff --git a/src/mme/mme-gn-build.c b/src/mme/mme-gn-build.c index 76acd0fdbe..6725dc1859 100644 --- a/src/mme/mme-gn-build.c +++ b/src/mme/mme-gn-build.c @@ -177,6 +177,26 @@ 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_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)); + + (void)req; + + 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) diff --git a/src/mme/mme-gn-build.h b/src/mme/mme-gn-build.h index 6a7af32b5a..ad24d7c176 100644 --- a/src/mme/mme-gn-build.h +++ b/src/mme/mme-gn-build.h @@ -33,6 +33,9 @@ extern "C" { 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_request( + mme_ue_t *mme_ue); + 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 149ec76e99..da032cc2bb 100644 --- a/src/mme/mme-gn-handler.c +++ b/src/mme/mme-gn-handler.c @@ -68,10 +68,25 @@ 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 = htobe16(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);; + uint16_t lac = be16toh(rai->lai.lac); nas_guti->nas_plmn_id =rai->lai.nas_plmn_id; nas_guti->mme_gid = lac; nas_guti->mme_code = rai->rac; diff --git a/src/mme/mme-gn-handler.h b/src/mme/mme-gn-handler.h index d6138a4c59..2feaa20a5a 100644 --- a/src/mme/mme-gn-handler.h +++ b/src/mme/mme-gn-handler.h @@ -41,6 +41,9 @@ void mme_gn_handle_sgsn_context_acknowledge( 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 f70ba66767..d64c4f9ea9 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) { diff --git a/src/mme/mme-gtp-path.h b/src/mme/mme-gtp-path.h index 1e41d34ffd..9de0222773 100644 --- a/src/mme/mme-gtp-path.h +++ b/src/mme/mme-gtp-path.h @@ -55,6 +55,9 @@ 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);