diff --git a/src/mme/esm-sm.c b/src/mme/esm-sm.c index 8dc394757..c5e987d8b 100644 --- a/src/mme/esm-sm.c +++ b/src/mme/esm-sm.c @@ -134,7 +134,8 @@ void esm_state_inactive(ogs_fsm_t *s, mme_event_t *e) mme_gtp_send_delete_session_request(enb_ue, sgw_ue, sess, OGS_GTP_DELETE_SEND_DEACTIVATE_BEARER_CONTEXT_REQUEST)); } else { - r = nas_eps_send_deactivate_bearer_context_request(bearer); + r = nas_eps_send_deactivate_bearer_context_request( + bearer, OGS_NAS_ESM_CAUSE_REGULAR_DEACTIVATION); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); } @@ -342,7 +343,8 @@ void esm_state_active(ogs_fsm_t *s, mme_event_t *e) mme_gtp_send_delete_session_request(enb_ue, sgw_ue, sess, OGS_GTP_DELETE_SEND_DEACTIVATE_BEARER_CONTEXT_REQUEST)); } else { - r = nas_eps_send_deactivate_bearer_context_request(bearer); + r = nas_eps_send_deactivate_bearer_context_request( + bearer, OGS_NAS_ESM_CAUSE_REGULAR_DEACTIVATION); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); } diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index 68d4bfb63..ad2e63851 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -752,6 +752,7 @@ struct mme_ue_s { ogs_debug("[%s] Clear Paging Info", (__mME)->imsi_bcd); \ (__mME)->paging.type = 0; \ (__mME)->paging.failed = false; \ + (__mME)->paging.esm_cause = OGS_NAS_ESM_CAUSE_REGULAR_DEACTIVATION; \ } while(0) #define MME_STORE_PAGING_INFO(__mME, __tYPE, __dATA) \ @@ -775,6 +776,8 @@ struct mme_ue_s { int type; void *data; bool failed; + uint8_t esm_cause; /* NAS ESM cause carried across paging to the + * deferred (post-paging) deactivation/detach */ } paging; /* SGW UE context */ diff --git a/src/mme/mme-path.c b/src/mme/mme-path.c index 3661e09f9..95938a90c 100644 --- a/src/mme/mme-path.c +++ b/src/mme/mme-path.c @@ -280,7 +280,15 @@ void mme_send_after_paging(mme_ue_t *mme_ue, bool failed) mme_gtp_send_delete_bearer_response( bearer, OGS_GTP2_CAUSE_UNABLE_TO_PAGE_UE)); } else { - r = nas_eps_send_deactivate_bearer_context_request(bearer); + /* + * Use the NAS ESM cause carried across paging (stored when the + * Delete Bearer Request was received). This preserves + * "reactivation requested" for an ECM-IDLE UE so the (IMS) PDN + * connection is re-established after paging. See + * mme_s11_handle_delete_bearer_request(). + */ + r = nas_eps_send_deactivate_bearer_context_request( + bearer, mme_ue->paging.esm_cause); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); } diff --git a/src/mme/mme-s11-handler.c b/src/mme/mme-s11-handler.c index 94c6ad322..f3bbbab65 100644 --- a/src/mme/mme-s11-handler.c +++ b/src/mme/mme-s11-handler.c @@ -859,7 +859,8 @@ void mme_s11_handle_delete_session_response( return; } - r = nas_eps_send_deactivate_bearer_context_request(bearer); + r = nas_eps_send_deactivate_bearer_context_request( + bearer, OGS_NAS_ESM_CAUSE_REGULAR_DEACTIVATION); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); @@ -1336,6 +1337,7 @@ void mme_s11_handle_delete_bearer_request( { int r; uint8_t cause_value = OGS_GTP2_CAUSE_UNDEFINED_VALUE; + ogs_nas_esm_cause_t esm_cause = OGS_NAS_ESM_CAUSE_REGULAR_DEACTIVATION; mme_bearer_t *bearer = NULL; mme_sess_t *sess = NULL; @@ -1438,15 +1440,47 @@ void mme_s11_handle_delete_bearer_request( ogs_assert(xact->id >= OGS_MIN_POOL_ID && xact->id <= OGS_MAX_POOL_ID); bearer->delete.xact_id = xact->id; + /* + * 3GPP TS 29.274 Table C.3: map the GTPv2 Cause in the Delete Bearer + * Request to the NAS ESM cause. For the default bearer, "Reactivation + * requested" (GTPv2 cause #8) is mapped to ESM cause #39 "reactivation + * requested" so the UE re-establishes the PDN connection (e.g. the IMS + * PDN for VoLTE). Any other case results in a regular deactivation. + * + * Cause #8 is defined for the default bearer (PGW-initiated default + * bearer deactivation), so the mapping is restricted to the Linked EBI + * (default bearer) case. A Cause #8 carried on a dedicated bearer is + * ignored and that bearer is deactivated normally. + * + * The mapped cause is applied on the ECM-CONNECTED path below, and is + * carried across paging via mme_ue->paging.esm_cause for the ECM-IDLE + * path (see mme-path.c, MME_PAGING_TYPE_DELETE_BEARER). + * + * LIMITATION: Table C.3 also maps "reactivation requested" to the NAS + * "re-attach required" detach type when this is the *last* PDN + * connection in E-UTRAN. That is not implemented: this EPC does not + * support Attach without PDN connectivity or SCEF PDN connections, and + * in a normal deployment a UE keeps at least one other (e.g. internet) + * PDN, so the last-PDN case does not arise here. The last default + * bearer is deactivated with ESM cause #39 as well. + */ + if (req->cause.presence && req->cause.data && + req->linked_eps_bearer_id.presence) { + ogs_gtp2_cause_t *cause = req->cause.data; + if (cause->value == OGS_GTP2_CAUSE_REACTIVATION_REQUESTED) + esm_cause = OGS_NAS_ESM_CAUSE_REACTIVATION_REQUESTED; + } + if (ECM_IDLE(mme_ue)) { MME_STORE_PAGING_INFO(mme_ue, MME_PAGING_TYPE_DELETE_BEARER, bearer->id); + mme_ue->paging.esm_cause = esm_cause; r = s1ap_send_paging(mme_ue, S1AP_CNDomain_ps); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); } else { MME_CLEAR_PAGING_INFO(mme_ue); - r = nas_eps_send_deactivate_bearer_context_request(bearer); + r = nas_eps_send_deactivate_bearer_context_request(bearer, esm_cause); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); } diff --git a/src/mme/nas-path.c b/src/mme/nas-path.c index 4bf60f335..82e6b8c26 100644 --- a/src/mme/nas-path.c +++ b/src/mme/nas-path.c @@ -723,7 +723,8 @@ int nas_eps_send_modify_bearer_context_request( return rv; } -int nas_eps_send_deactivate_bearer_context_request(mme_bearer_t *bearer) +int nas_eps_send_deactivate_bearer_context_request( + mme_bearer_t *bearer, ogs_nas_esm_cause_t esm_cause) { int rv; ogs_pkbuf_t *s1apbuf = NULL; @@ -746,7 +747,7 @@ int nas_eps_send_deactivate_bearer_context_request(mme_bearer_t *bearer) } esmbuf = esm_build_deactivate_bearer_context_request( - bearer, OGS_NAS_ESM_CAUSE_REGULAR_DEACTIVATION); + bearer, esm_cause); if (!esmbuf) { ogs_error("esm_build_deactivate_bearer_context_request() failed"); return OGS_ERROR; diff --git a/src/mme/nas-path.h b/src/mme/nas-path.h index fc8de96b7..c8460496d 100644 --- a/src/mme/nas-path.h +++ b/src/mme/nas-path.h @@ -56,7 +56,8 @@ int nas_eps_send_activate_dedicated_bearer_context_request( void nas_eps_send_activate_all_dedicated_bearers(mme_bearer_t *default_bearer); int nas_eps_send_modify_bearer_context_request( mme_bearer_t *bearer, int qos_presence, int tft_presence); -int nas_eps_send_deactivate_bearer_context_request(mme_bearer_t *bearer); +int nas_eps_send_deactivate_bearer_context_request( + mme_bearer_t *bearer, ogs_nas_esm_cause_t esm_cause); int nas_eps_send_bearer_resource_allocation_reject( mme_ue_t *mme_ue, uint8_t pti, ogs_nas_esm_cause_t esm_cause); int nas_eps_send_bearer_resource_modification_reject( diff --git a/src/smf/n4-handler.c b/src/smf/n4-handler.c index e0817362a..673c7c8e1 100644 --- a/src/smf/n4-handler.c +++ b/src/smf/n4-handler.c @@ -1796,11 +1796,50 @@ uint8_t smf_n4_handle_session_report_request( /* Error Indication is handled last */ if (report_type.error_indication_report && far) { if (sess->epc == true) { - ogs_error("[%s:%s] Error Indication from SGW-C", - smf_ue->imsi_bcd, sess->session.name); - ogs_assert(OGS_OK == - smf_epc_pfcp_send_session_deletion_request( - sess, OGS_INVALID_POOL_ID)); + /* + * 3GPP TS 23.007 (GTP-U Error Indication at the PGW): + * - default bearer -> deactivate all bearers of the PDN + * connection (the PDN connection is released). + * - dedicated bearer -> deactivate only that bearer. + * + * The Error Indication Report identifies the broken GTP-U tunnel + * through the remote F-TEID carried in the downlink FAR. Running a + * PGW-initiated bearer deactivation makes the PFCP modification + * response send a Delete Bearer Request to the SGW-C/MME, so the + * MME deactivates the EPS bearer towards the UE (NAS Deactivate + * EPS Bearer Context Request). + * + * Deleting the PFCP session locally (as before) never notified the + * MME, so the UE kept a stale PDN connection: data on other APNs + * still worked, but the UE could not originate a new VoLTE call + * until it re-attached (airplane-mode toggle). + */ + ogs_list_for_each(&sess->bearer_list, bearer) { + if (bearer->dl_far == far) + break; + } + if (!bearer) { + ogs_error("[%s:%s] Error Indication from SGW-U: " + "no bearer found for FAR", + smf_ue->imsi_bcd, sess->session.name); + } else if (bearer == smf_default_bearer_in_sess(sess)) { + ogs_error("[%s:%s] Error Indication from SGW-U " + "[EBI:%d] (default bearer)", + smf_ue->imsi_bcd, sess->session.name, bearer->ebi); + ogs_assert(OGS_OK == + smf_epc_pfcp_send_deactivation( + sess, OGS_GTP2_CAUSE_REACTIVATION_REQUESTED)); + } else { + ogs_error("[%s:%s] Error Indication from SGW-U " + "[EBI:%d] (dedicated bearer)", + smf_ue->imsi_bcd, sess->session.name, bearer->ebi); + ogs_assert(OGS_OK == + smf_epc_pfcp_send_one_bearer_modification_request( + bearer, OGS_INVALID_POOL_ID, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, + OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED, + OGS_GTP2_CAUSE_UNDEFINED_VALUE)); + } } else { ogs_warn("[%s:%s] Error Indication from gNB", smf_ue->supi, sess->session.name); diff --git a/src/smf/pfcp-path.c b/src/smf/pfcp-path.c index d768082a2..0047f20ae 100644 --- a/src/smf/pfcp-path.c +++ b/src/smf/pfcp-path.c @@ -1050,6 +1050,37 @@ int smf_epc_pfcp_send_deactivation(smf_sess_t *sess, uint8_t gtp_cause) } break; + case OGS_GTP2_CAUSE_REACTIVATION_REQUESTED: + /* + * GTP-U Error Indication on the default bearer (TS 23.007): + * deactivate all bearers of this PDN connection. The PFCP + * modification response then sends a Delete Bearer Request for the + * default bearer (Linked EBI) to the SGW-C/MME. + * + * The "Reactivation requested" cause is used so the MME maps it to + * NAS ESM cause #39 "reactivation requested" (3GPP TS 29.274 + * clause 7.2.9.2 and Table C.3), instructing the UE to re-establish + * the PDN connection (e.g. the IMS PDN for VoLTE) instead of just + * deactivating it. + */ + if (ogs_list_first(&sess->bearer_list) == NULL) { + ogs_error("No Bearer List in Session"); + return OGS_ERROR; + } + + /* Deactivate this PDN connection */ + rv = smf_epc_pfcp_send_all_pdr_modification_request( + sess, OGS_INVALID_POOL_ID, NULL, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, + OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED, + OGS_GTP2_CAUSE_REACTIVATION_REQUESTED); + if (rv != OGS_OK) { + ogs_error("smf_epc_pfcp_send_all_pdr_modification_request() " + "failed"); + return OGS_ERROR; + } + break; + default: ogs_fatal("Invalid GTP-Cause[%d]", gtp_cause); ogs_assert_if_reached();