diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index 281e9f9adf..4115f2d2ab 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -47,6 +47,7 @@ static OGS_POOL(mme_csmap_pool, mme_csmap_t); static OGS_POOL(mme_enb_pool, mme_enb_t); static OGS_POOL(mme_ue_pool, mme_ue_t); static OGS_POOL(mme_s11_teid_pool, ogs_pool_id_t); +static OGS_POOL(mme_gn_teid_pool, ogs_pool_id_t); static OGS_POOL(enb_ue_pool, enb_ue_t); static OGS_POOL(sgw_ue_pool, sgw_ue_t); static OGS_POOL(mme_sess_pool, mme_sess_t); @@ -110,6 +111,8 @@ void mme_context_init(void) ogs_pool_init(&mme_ue_pool, ogs_global_conf()->max.ue); ogs_pool_init(&mme_s11_teid_pool, ogs_global_conf()->max.ue); ogs_pool_random_id_generate(&mme_s11_teid_pool); + ogs_pool_init(&mme_gn_teid_pool, ogs_global_conf()->max.ue); + ogs_pool_random_id_generate(&mme_gn_teid_pool); ogs_pool_init(&enb_ue_pool, ogs_global_conf()->max.ue); ogs_pool_init(&sgw_ue_pool, ogs_global_conf()->max.ue); @@ -128,6 +131,8 @@ void mme_context_init(void) ogs_assert(self.guti_ue_hash); self.mme_s11_teid_hash = ogs_hash_make(); ogs_assert(self.mme_s11_teid_hash); + self.mme_gn_teid_hash = ogs_hash_make(); + ogs_assert(self.mme_gn_teid_hash); ogs_list_init(&self.mme_ue_list); @@ -158,12 +163,15 @@ void mme_context_final(void) ogs_hash_destroy(self.guti_ue_hash); ogs_assert(self.mme_s11_teid_hash); ogs_hash_destroy(self.mme_s11_teid_hash); + ogs_assert(self.mme_gn_teid_hash); + ogs_hash_destroy(self.mme_gn_teid_hash); ogs_pool_final(&m_tmsi_pool); ogs_pool_final(&mme_bearer_pool); ogs_pool_final(&mme_sess_pool); ogs_pool_final(&mme_ue_pool); ogs_pool_final(&mme_s11_teid_pool); + ogs_pool_final(&mme_gn_teid_pool); ogs_pool_final(&enb_ue_pool); ogs_pool_final(&sgw_ue_pool); @@ -3297,19 +3305,32 @@ mme_ue_t *mme_ue_add(enb_ue_t *enb_ue) } mme_ue->t_implicit_detach.pkbuf = NULL; + mme_ue->gn.t_gn_holding = ogs_timer_add( + ogs_app()->timer_mgr, mme_timer_gn_holding_timer_expire, mme_ue); + if (! mme_ue->gn.t_gn_holding) { + ogs_error("ogs_timer_add() failed"); + ogs_pool_free(&mme_ue_pool, mme_ue); + return NULL; + } + mme_ebi_pool_init(mme_ue); ogs_list_init(&mme_ue->sess_list); - /* Set MME-S11_TEID */ + /* Set MME-S11-TEID */ ogs_pool_alloc(&mme_s11_teid_pool, &mme_ue->mme_s11_teid_node); ogs_assert(mme_ue->mme_s11_teid_node); - mme_ue->mme_s11_teid = *(mme_ue->mme_s11_teid_node); - ogs_hash_set(self.mme_s11_teid_hash, &mme_ue->mme_s11_teid, sizeof(mme_ue->mme_s11_teid), mme_ue); + /* Set MME-Gn-TEID */ + ogs_pool_alloc(&mme_gn_teid_pool, &mme_ue->gn.mme_gn_teid_node); + ogs_assert(mme_ue->gn.mme_gn_teid_node); + mme_ue->gn.mme_gn_teid = *(mme_ue->gn.mme_gn_teid_node); + ogs_hash_set(self.mme_gn_teid_hash, + &mme_ue->gn.mme_gn_teid, sizeof(mme_ue->gn.mme_gn_teid), mme_ue); + /* * When used for the first time, if last node is set, * the search is performed from the first SGW in a round-robin manner. @@ -3353,6 +3374,8 @@ void mme_ue_remove(mme_ue_t *mme_ue) ogs_hash_set(self.mme_s11_teid_hash, &mme_ue->mme_s11_teid, sizeof(mme_ue->mme_s11_teid), NULL); + ogs_hash_set(self.mme_gn_teid_hash, + &mme_ue->gn.mme_gn_teid, sizeof(mme_ue->gn.mme_gn_teid), NULL); ogs_assert(mme_ue->sgw_ue); sgw_ue_remove(mme_ue->sgw_ue); @@ -3391,6 +3414,7 @@ void mme_ue_remove(mme_ue_t *mme_ue) ogs_timer_delete(mme_ue->t3470.timer); ogs_timer_delete(mme_ue->t_mobile_reachable.timer); ogs_timer_delete(mme_ue->t_implicit_detach.timer); + ogs_timer_delete(mme_ue->gn.t_gn_holding); enb_ue_unlink(mme_ue); @@ -3400,6 +3424,7 @@ void mme_ue_remove(mme_ue_t *mme_ue) mme_ebi_pool_final(mme_ue); ogs_pool_free(&mme_s11_teid_pool, mme_ue->mme_s11_teid_node); + ogs_pool_free(&mme_gn_teid_pool, mme_ue->gn.mme_gn_teid_node); ogs_pool_free(&mme_ue_pool, mme_ue); ogs_info("[Removed] Number of MME-UEs is now %d", @@ -3473,11 +3498,16 @@ mme_ue_t *mme_ue_find_by_guti(ogs_nas_eps_guti_t *guti) self.guti_ue_hash, guti, sizeof(ogs_nas_eps_guti_t)); } -mme_ue_t *mme_ue_find_by_teid(uint32_t teid) +mme_ue_t *mme_ue_find_by_s11_local_teid(uint32_t teid) { return ogs_hash_get(self.mme_s11_teid_hash, &teid, sizeof(teid)); } +mme_ue_t *mme_ue_find_by_gn_local_teid(uint32_t teid) +{ + return ogs_hash_get(self.mme_gn_teid_hash, &teid, sizeof(teid)); +} + mme_ue_t *mme_ue_find_by_message(ogs_nas_eps_message_t *message) { mme_ue_t *mme_ue = NULL; diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index 2bf14268ef..5fff0dd955 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -192,6 +192,7 @@ typedef struct mme_context_s { ogs_hash_t *guti_ue_hash; /* hash table (GUTI : MME_UE) */ ogs_hash_t *mme_s11_teid_hash; /* hash table (MME-S11-TEID : MME_UE) */ + ogs_hash_t *mme_gn_teid_hash; /* hash table (MME-GN-TEID : MME_UE) */ struct { struct { @@ -422,9 +423,13 @@ struct mme_ue_s { mme_p_tmsi_t p_tmsi; struct { + ogs_pool_id_t *mme_gn_teid_node; /* A node of MME-Gn-TEID */ + uint32_t mme_gn_teid; /* MME-Gn-TEID is derived from NODE */ uint32_t sgsn_gn_teid; ogs_ip_t sgsn_gn_ip; ogs_ip_t sgsn_gn_ip_alt; + /* Unnamed timer in 3GPP TS 23.401 D.3.5 step 2), see also 3GPP TS 23.060 6.9.1.2.2 */ + ogs_timer_t *t_gn_holding; } gn; struct { @@ -948,7 +953,8 @@ void mme_ue_fsm_fini(mme_ue_t *mme_ue); mme_ue_t *mme_ue_find_by_imsi(uint8_t *imsi, int imsi_len); mme_ue_t *mme_ue_find_by_imsi_bcd(char *imsi_bcd); mme_ue_t *mme_ue_find_by_guti(ogs_nas_eps_guti_t *nas_guti); -mme_ue_t *mme_ue_find_by_teid(uint32_t teid); +mme_ue_t *mme_ue_find_by_s11_local_teid(uint32_t teid); +mme_ue_t *mme_ue_find_by_gn_local_teid(uint32_t teid); mme_ue_t *mme_ue_find_by_message(ogs_nas_eps_message_t *message); int mme_ue_set_imsi(mme_ue_t *mme_ue, char *imsi_bcd); diff --git a/src/mme/mme-event.c b/src/mme/mme-event.c index 36c4bd6246..483b02b12f 100644 --- a/src/mme/mme-event.c +++ b/src/mme/mme-event.c @@ -99,6 +99,8 @@ const char *mme_event_get_name(mme_event_t *e) case MME_EVENT_GN_MESSAGE: return "MME_EVENT_GN_MESSAGE"; + case MME_EVENT_GN_TIMER: + return "MME_EVENT_GN_TIMER"; default: break; } diff --git a/src/mme/mme-event.h b/src/mme/mme-event.h index 97869070f2..f7f1af75c5 100644 --- a/src/mme/mme-event.h +++ b/src/mme/mme-event.h @@ -51,6 +51,7 @@ typedef enum { MME_EVENT_SGSAP_LO_CONNREFUSED, MME_EVENT_GN_MESSAGE, + MME_EVENT_GN_TIMER, MAX_NUM_OF_MME_EVENT, diff --git a/src/mme/mme-gn-build.c b/src/mme/mme-gn-build.c index 83f000b15e..76acd0fdbe 100644 --- a/src/mme/mme-gn-build.c +++ b/src/mme/mme-gn-build.c @@ -141,15 +141,23 @@ static int sess_fill_pdp_context_decoded(mme_sess_t *sess, ogs_gtp1_pdp_context_ .vaa = OGS_GTP1_PDPCTX_VLPMN_ADDR_ALLOWED_YES, .asi = OGS_GTP1_PDPCTX_ACTIVITY_STATUS_IND_NO, .order = OGS_GTP1_PDPCTX_REORDERING_REQUIRED_NO, + /* 3GPP TS 23.401 Annex D3.5.5 2b.: + * "The GTP equence numbers received from the old 3G-SGSN are only relevant if + * delivery order is required for the PDP context (QoS profile)." + * NOTE 4: "The GTP and PDCP sequence numbers are not relevant" */ + .snd = 0, + .snu = 0, + .send_npdu_nr = 0, + .receive_npdu_nr = 0, .ul_teic = sess->pgw_s5c_teid, + .pdp_type_org = OGS_PDP_EUA_ORG_IETF, + .pdp_type_num = {sess->session->session_type, }, + .pdp_address = {sess->session->ue_ip, }, + .ggsn_address_c = sess->pgw_s5c_ip, + .trans_id = sess->pti, }; - pdpctx_dec->ggsn_address_c = sess->pgw_s5c_ip; - pdpctx_dec->pdp_type_org = OGS_PDP_EUA_ORG_IETF; - pdpctx_dec->pdp_type_num[0] = sess->session->session_type; - pdpctx_dec->pdp_address[0] = sess->session->ue_ip; ogs_cpystrn(pdpctx_dec->apn, sess->session->name, sizeof(pdpctx_dec->apn)); - pdpctx_dec->trans_id = sess->pti; ogs_list_for_each(&sess->bearer_list, bearer) { pdpctx_dec->nsapi = bearer->ebi; @@ -158,14 +166,9 @@ static int sess_fill_pdp_context_decoded(mme_sess_t *sess, ogs_gtp1_pdp_context_ //FIXME: sort out where to get each one: memcpy(&pdpctx_dec->qos_req, &pdpctx_dec->qos_sub, sizeof(pdpctx_dec->qos_sub)); memcpy(&pdpctx_dec->qos_neg, &pdpctx_dec->qos_sub, sizeof(pdpctx_dec->qos_sub)); - pdpctx_dec->snd = 0; /* FIXME. */ - pdpctx_dec->snu = 0; /* FIXME. */ - pdpctx_dec->send_npdu_nr = 0; /* FIXME. */ - pdpctx_dec->receive_npdu_nr = 0; /* FIXME. */ pdpctx_dec->ul_teid = bearer->pgw_s5u_teid; pdpctx_dec->pdp_ctx_id = 0; /* FIXME. */ pdpctx_dec->ggsn_address_u = bearer->pgw_s5u_ip; - /* TODO: session->qos and bearer->qos to fill something in pdpctx_dec. */ /* FIXME: only 1 PDP Context supported in the message so far. */ break; @@ -211,9 +214,8 @@ ogs_pkbuf_t *mme_gn_build_sgsn_context_response( ogs_assert(mme_ue); - /* FIXME: Reuse S11 TEID as local Gn interface for now */ rsp->tunnel_endpoint_identifier_control_plane.presence = 1; - rsp->tunnel_endpoint_identifier_control_plane.u32 = mme_ue->mme_s11_teid; + rsp->tunnel_endpoint_identifier_control_plane.u32 = mme_ue->gn.mme_gn_teid; ogs_list_for_each(&mme_ue->sess_list, sess) { if (!MME_HAVE_SGW_S1U_PATH(sess)) diff --git a/src/mme/mme-gn-handler.c b/src/mme/mme-gn-handler.c index cca5dba04b..16c4270fcb 100644 --- a/src/mme/mme-gn-handler.c +++ b/src/mme/mme-gn-handler.c @@ -168,15 +168,49 @@ void mme_gn_handle_sgsn_context_request( ogs_assert(rv == OGS_OK); } - mme_gtp1_send_sgsn_context_response(mme_ue, OGS_GTP1_CAUSE_REQUEST_ACCEPTED, xact); + /* 3GPP TS 23.401 Annex D.3.5 "Routing Area Update": + * Step 2. "If the old P-TMSI Signature was valid or if the new SGSN indicates that it has authenticated the MS, + * the old SGSN starts a timer." + */ + ogs_timer_start(mme_ue->gn.t_gn_holding, mme_timer_cfg(MME_TIMER_GN_HOLDING)->duration); + + mme_gtp1_send_sgsn_context_response(mme_ue, OGS_GTP1_CAUSE_REQUEST_ACCEPTED, xact); } /* TS 29.060 7.5.5 SGSN Context Acknowledge */ void mme_gn_handle_sgsn_context_acknowledge( - ogs_gtp_xact_t *xact, ogs_gtp1_sgsn_context_acknowledge_t *req) + ogs_gtp_xact_t *xact, mme_ue_t *mme_ue, ogs_gtp1_sgsn_context_acknowledge_t *req) { - /* FIXME: find out what to do here, 3GPP TS 23.401 D.3.5 - * Probably release the Session on the SGW (without releasing in the PGW) */ + int rv; + + /* 3GPP TS 23.060 6.9.1.2.2 Step 4), 3GPP TS 23.401 D.3.5 Step 4) + * The new SGSN sends an SGSN Context Acknowledge message to the old SGSN. The old MME (which is the old + * SGSN from the new SGSN's point of view) marks in its context that the information in the GWs and the HSS are + * invalid. This triggers the GWs, and the HSS to be updated if the UE initiates a Tracking Area Update procedure + * back to the old MME before completing the ongoing Routing Area Update procedure. If the security functions do + * not authenticate the MS correctly, then the routing area update shall be rejected, and the new SGSN shall send a + * reject indication to the old SGSN. The old MME shall continue as if the SGSN Context Request was never + * received. + * "NOTE 6: The new SGSN's operation is unmodified compared to pre-Rel-8. The old MME/S-GW (old SGSN from + * the new SGSN's point of view) does not forward any data towards the new SGSN." "*/ + + if (req->cause.u8 != OGS_GTP1_CAUSE_REQUEST_ACCEPTED) { + ogs_timer_stop(mme_ue->gn.t_gn_holding); + return; + } + + /* 3GPP TS 23.060 6.9.1.2.2 Step 13) + * "If the old MME has an S1-MME association for the UE, the source MME sends a S1-U Release Command to the + * source eNodeB when receiving the SGSN Context Acknowledge message from the new SGSN. The RRC + * connection is released by the source eNodeB. The source eNodeB confirms the release of the RRC connection + * and of the S1-U connection by sending a S1-U Release Complete message to the source MME." + */ + if (mme_ue->enb_ue) { + rv = s1ap_send_ue_context_release_command(mme_ue->enb_ue, + S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release, + S1AP_UE_CTX_REL_UE_CONTEXT_REMOVE, 0); + ogs_expect(rv == OGS_OK); + } } /* TS 29.060 7.5.14.1 */ diff --git a/src/mme/mme-gn-handler.h b/src/mme/mme-gn-handler.h index 192650b553..d6138a4c59 100644 --- a/src/mme/mme-gn-handler.h +++ b/src/mme/mme-gn-handler.h @@ -36,7 +36,7 @@ void mme_gn_handle_sgsn_context_request( ogs_gtp_xact_t *xact, ogs_gtp1_sgsn_context_request_t *req); void mme_gn_handle_sgsn_context_acknowledge( - ogs_gtp_xact_t *xact, ogs_gtp1_sgsn_context_acknowledge_t *req); + 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); @@ -45,4 +45,4 @@ void mme_gn_handle_ran_information_relay( } #endif -#endif /* MME_S11_HANDLER_H */ +#endif /* MME_GN_HANDLER_H */ diff --git a/src/mme/mme-gtp-path.c b/src/mme/mme-gtp-path.c index 92ed42b6c9..ad5e695276 100644 --- a/src/mme/mme-gtp-path.c +++ b/src/mme/mme-gtp-path.c @@ -268,7 +268,7 @@ int mme_gtp_send_create_session_request(mme_sess_t *sess, int create_action) return OGS_ERROR; } xact->create_action = create_action; - xact->local_teid = mme_ue->mme_s11_teid; + xact->local_teid = mme_ue->gn.mme_gn_teid; rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); @@ -307,7 +307,7 @@ int mme_gtp_send_modify_bearer_request( return OGS_ERROR; } xact->modify_action = modify_action; - xact->local_teid = mme_ue->mme_s11_teid; + xact->local_teid = mme_ue->gn.mme_gn_teid; rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); @@ -346,7 +346,7 @@ int mme_gtp_send_delete_session_request( return OGS_ERROR; } xact->delete_action = action; - xact->local_teid = mme_ue->mme_s11_teid; + xact->local_teid = mme_ue->gn.mme_gn_teid; rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); @@ -537,7 +537,7 @@ int mme_gtp_send_release_access_bearers_request(mme_ue_t *mme_ue, int action) return OGS_ERROR; } xact->release_action = action; - xact->local_teid = mme_ue->mme_s11_teid; + xact->local_teid = mme_ue->gn.mme_gn_teid; rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); @@ -670,7 +670,7 @@ int mme_gtp_send_create_indirect_data_forwarding_tunnel_request( ogs_error("ogs_gtp_xact_local_create() failed"); return OGS_ERROR; } - xact->local_teid = mme_ue->mme_s11_teid; + xact->local_teid = mme_ue->gn.mme_gn_teid; rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); @@ -709,7 +709,7 @@ int mme_gtp_send_delete_indirect_data_forwarding_tunnel_request( return OGS_ERROR; } xact->delete_indirect_action = action; - xact->local_teid = mme_ue->mme_s11_teid; + xact->local_teid = mme_ue->gn.mme_gn_teid; rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); @@ -750,7 +750,7 @@ int mme_gtp_send_bearer_resource_command( return OGS_ERROR; } xact->xid |= OGS_GTP_CMD_XACT_ID; - xact->local_teid = mme_ue->mme_s11_teid; + xact->local_teid = mme_ue->gn.mme_gn_teid; rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); @@ -775,7 +775,7 @@ int mme_gtp1_send_sgsn_context_response( return OGS_ERROR; } /* FIXME: Reuse S11 TEID as local Gn interface for now */ - xact->local_teid = mme_ue ? mme_ue->mme_s11_teid : 0; + xact->local_teid = mme_ue ? mme_ue->gn.mme_gn_teid : 0; rv = ogs_gtp1_xact_update_tx(xact, &h, pkbuf); if (rv != OGS_OK) { diff --git a/src/mme/mme-s6a-handler.c b/src/mme/mme-s6a-handler.c index e7b128ca8d..499b351ce4 100644 --- a/src/mme/mme-s6a-handler.c +++ b/src/mme/mme-s6a-handler.c @@ -33,7 +33,7 @@ static uint8_t emm_cause_from_diameter( const uint32_t *dia_err, const uint32_t *dia_exp_err); -static uint8_t mme_ue_session_from_slice_data(mme_ue_t *mme_ue, +static uint8_t mme_ue_session_from_slice_data(mme_ue_t *mme_ue, ogs_slice_data_t *slice_data); uint8_t mme_s6a_handle_aia( @@ -281,6 +281,15 @@ void mme_s6a_handle_clr(mme_ue_t *mme_ue, ogs_diam_s6a_message_t *s6a_message) case OGS_DIAM_S6A_CT_MME_UPDATE_PROCEDURE: mme_ue->detach_type = MME_DETACH_TYPE_HSS_IMPLICIT; + /* 3GPP TS 23.401 D.3.5.5 8), 3GPP TS 23.060 6.9.1.2.2 8): + * "When the timer described in step 2 is running, the MM and PDP/EPS + * Bearer Contexts and any affected S-GW resources are removed when the + * timer expires and the SGSN received a Cancel Location". + */ + if (mme_ue->gn.t_gn_holding->running) { + break; + } + /* * There is no need to send NAS or S1AP message to the UE. * So, we don't have to check whether UE is IDLE or not. @@ -299,7 +308,7 @@ void mme_s6a_handle_clr(mme_ue_t *mme_ue, ogs_diam_s6a_message_t *s6a_message) } } -static uint8_t mme_ue_session_from_slice_data(mme_ue_t *mme_ue, +static uint8_t mme_ue_session_from_slice_data(mme_ue_t *mme_ue, ogs_slice_data_t *slice_data) { int i; diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index 3c2a2678b5..89789c18c2 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -528,14 +528,14 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) */ if (gtp_message.h.teid_presence && gtp_message.h.teid != 0) { /* Cause is not "Context not found" */ - mme_ue = mme_ue_find_by_teid(gtp_message.h.teid); + mme_ue = mme_ue_find_by_s11_local_teid(gtp_message.h.teid); } else if (xact->local_teid) { /* rx no TEID or TEID=0 */ /* 3GPP TS 29.274 5.5.2: we receive TEID=0 under some * conditions, such as cause "Session context not found". In those * cases, we still want to identify the local session which * originated the message, so try harder by using the TEID we * locally stored in xact when sending the original request: */ - mme_ue = mme_ue_find_by_teid(xact->local_teid); + mme_ue = mme_ue_find_by_s11_local_teid(xact->local_teid); } switch (gtp_message.h.type) { @@ -655,6 +655,15 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) break; } + if (gtp1_message.h.teid != 0) { + /* Cause is not "Context not found" */ + mme_ue = mme_ue_find_by_gn_local_teid(gtp1_message.h.teid); + } else if (xact->local_teid) { /* rx no TEID or TEID=0 */ + /* Try harder by using the TEID we locally stored in xact when + *sending the original request: */ + mme_ue = mme_ue_find_by_gn_local_teid(xact->local_teid); + } + switch (gtp1_message.h.type) { case OGS_GTP1_ECHO_REQUEST_TYPE: mme_gn_handle_echo_request(xact, >p1_message.echo_request); @@ -666,7 +675,7 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) mme_gn_handle_sgsn_context_request(xact, >p1_message.sgsn_context_request); break; case OGS_GTP1_SGSN_CONTEXT_ACKNOWLEDGE_TYPE: - mme_gn_handle_sgsn_context_acknowledge(xact, >p1_message.sgsn_context_acknowledge); + mme_gn_handle_sgsn_context_acknowledge(xact, mme_ue, >p1_message.sgsn_context_acknowledge); break; case OGS_GTP1_RAN_INFORMATION_RELAY_TYPE: mme_gn_handle_ran_information_relay(xact, >p1_message.ran_information_relay); @@ -678,6 +687,35 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) ogs_pkbuf_free(pkbuf); break; + case MME_EVENT_GN_TIMER: + mme_ue = e->mme_ue; + ogs_assert(mme_ue); + + switch (e->timer_id) { + case MME_TIMER_GN_HOLDING: + /* 3GPP TS 23.401 Annex D.3.5 "Routing Area Update": + * Step 13. "When the timer started in step 2) (see mme_gn_handle_sgsn_context_request()) expires the old MME + * releases any RAN and Serving GW resources. If the PLMN has configured Secondary RAT usage data reporting, + * the MME first releases RAN resource before releasing Serving GW resources." + */ + + /* + * There is no need to send NAS or S1AP message to the UE. + * So, we don't have to check whether UE is IDLE or not. + */ + if (MME_P_TMSI_IS_AVAILABLE(mme_ue)) { + ogs_assert(OGS_OK == sgsap_send_detach_indication(mme_ue)); + } else { + mme_send_delete_session_or_detach(mme_ue); + } + break; + + default: + ogs_error("Unknown timer[%s:%d]", + mme_timer_get_name(e->timer_id), e->timer_id); + } + break; + case MME_EVENT_SGSAP_LO_SCTP_COMM_UP: sock = e->sock; ogs_assert(sock); diff --git a/src/mme/mme-timer.c b/src/mme/mme-timer.c index f2b6204f75..1f196c9162 100644 --- a/src/mme/mme-timer.c +++ b/src/mme/mme-timer.c @@ -58,6 +58,9 @@ static mme_timer_cfg_t g_mme_timer_cfg[MAX_NUM_OF_MME_TIMER] = { [MME_TIMER_S11_HOLDING] = { .have = true, .duration = ogs_time_from_msec(300) }, + + [MME_TIMER_GN_HOLDING] = + { .have = true, .duration = ogs_time_from_sec(20) }, }; static void emm_timer_event_send( @@ -258,3 +261,24 @@ void mme_timer_s11_holding_timer_expire(void *data) mme_event_free(e); } } + +void mme_timer_gn_holding_timer_expire(void *data) +{ + int rv; + mme_event_t *e = NULL; + mme_ue_t *mme_ue; + + ogs_assert(data); + mme_ue = data; + + e = mme_event_new(MME_EVENT_GN_TIMER); + + e->timer_id = MME_TIMER_GN_HOLDING; + e->mme_ue = mme_ue; + + rv = ogs_queue_push(ogs_app()->queue, e); + if (rv != OGS_OK) { + ogs_error("ogs_queue_push() failed:%d", (int)rv); + mme_event_free(e); + } +} diff --git a/src/mme/mme-timer.h b/src/mme/mme-timer.h index f7cdf415c4..a17931a165 100644 --- a/src/mme/mme-timer.h +++ b/src/mme/mme-timer.h @@ -45,6 +45,8 @@ typedef enum { MME_TIMER_S11_HOLDING, + MME_TIMER_GN_HOLDING, + MME_TIMER_SGS_CLI_CONN_TO_SRV, MAX_NUM_OF_MME_TIMER, @@ -76,6 +78,7 @@ void mme_timer_implicit_detach_expire(void *data); void mme_timer_sgs_cli_conn_to_srv(void *data); void mme_timer_s1_holding_timer_expire(void *data); void mme_timer_s11_holding_timer_expire(void *data); +void mme_timer_gn_holding_timer_expire(void *data); #ifdef __cplusplus }