diff --git a/lib/diameter/s6a/dict.c b/lib/diameter/s6a/dict.c index d2c96b22d..d6768bcb6 100644 --- a/lib/diameter/s6a/dict.c +++ b/lib/diameter/s6a/dict.c @@ -526,6 +526,63 @@ int ogs_dict_s6a_entry(char *conffile) PARSE_loc_rules( rules, cmd ); } + /* Cancel-Location-Request (CLR) Command - 3GPP TS 29.272 #7.2.7 */ + { + struct dict_object * cmd; + struct dict_cmd_data data = { + 317, /* Code */ + "Cancel-Location-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Host" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "User-Name" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Cancellation-Type" }, RULE_REQUIRED, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "CLR-Flags" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 }, + }; + + CHECK_dict_new( DICT_COMMAND, &data, s6a, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Cancel-Location-Answer (CLA) Command - 3GPP TS 29.272 #7.2.8 */ + { + struct dict_object * cmd; + struct dict_cmd_data data = { + 317, /* Code */ + "Cancel-Location-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 }, + }; + + CHECK_dict_new( DICT_COMMAND, &data, s6a, &cmd); + PARSE_loc_rules( rules, cmd ); + } + } LOG_D( "Extension 'Dictionary definitions for DCCA 3GPP S6A' initialized"); diff --git a/lib/diameter/s6a/message.c b/lib/diameter/s6a/message.c index 38f370348..b770a666a 100644 --- a/lib/diameter/s6a/message.c +++ b/lib/diameter/s6a/message.c @@ -30,9 +30,13 @@ struct dict_object *ogs_diam_s6a_cmd_ulr = NULL; struct dict_object *ogs_diam_s6a_cmd_ula = NULL; struct dict_object *ogs_diam_s6a_cmd_pur = NULL; struct dict_object *ogs_diam_s6a_cmd_pua = NULL; +struct dict_object *ogs_diam_s6a_cmd_clr = NULL; +struct dict_object *ogs_diam_s6a_cmd_cla = NULL; struct dict_object *ogs_diam_s6a_ulr_flags = NULL; struct dict_object *ogs_diam_s6a_ula_flags = NULL; +struct dict_object *ogs_diam_s6a_clr_flags = NULL; +struct dict_object *ogs_diam_s6a_cancellation_type = NULL; struct dict_object *ogs_diam_s6a_subscription_data = NULL; struct dict_object *ogs_diam_s6a_req_eutran_auth_info = NULL; struct dict_object *ogs_diam_s6a_number_of_requested_vectors = NULL; @@ -91,9 +95,13 @@ int ogs_diam_s6a_init(void) CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, "Update-Location-Answer", &ogs_diam_s6a_cmd_ula); CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, "Purge-UE-Request", &ogs_diam_s6a_cmd_pur); CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, "Purge-UE-Answer", &ogs_diam_s6a_cmd_pua); + CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, "Cancel-Location-Request", &ogs_diam_s6a_cmd_clr); + CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, "Cancel-Location-Answer", &ogs_diam_s6a_cmd_cla); CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "ULR-Flags", &ogs_diam_s6a_ulr_flags); CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "ULA-Flags", &ogs_diam_s6a_ula_flags); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "CLR-Flags", &ogs_diam_s6a_clr_flags); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "Cancellation-Type", &ogs_diam_s6a_cancellation_type); CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "UE-SRVCC-Capability", &ogs_diam_s6a_ue_srvcc_capability); CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "Requested-EUTRAN-Authentication-Info", &ogs_diam_s6a_req_eutran_auth_info); diff --git a/lib/diameter/s6a/message.h b/lib/diameter/s6a/message.h index 583447e0d..943d20e8b 100644 --- a/lib/diameter/s6a/message.h +++ b/lib/diameter/s6a/message.h @@ -59,6 +59,9 @@ extern "C" { #define OGS_DIAM_S6A_ULR_INITIAL_ATTACH_IND (1 << 5) #define OGS_DIAM_S6A_ULR_PS_LCS_SUPPORTED_BY_UE (1 << 6) +#define OGS_DIAM_S6A_CLR_S6A_S6D_INDICATOR (1) +#define OGS_DIAM_S6A_CLR_REATTACH_REQUIRED (1 << 1) + #define OGS_DIAM_S6A_UE_SRVCC_NOT_SUPPORTED (0) #define OGS_DIAM_S6A_UE_SRVCC_SUPPORTED (1) @@ -76,9 +79,13 @@ extern struct dict_object *ogs_diam_s6a_cmd_ulr; extern struct dict_object *ogs_diam_s6a_cmd_ula; extern struct dict_object *ogs_diam_s6a_cmd_pur; extern struct dict_object *ogs_diam_s6a_cmd_pua; +extern struct dict_object *ogs_diam_s6a_cmd_clr; +extern struct dict_object *ogs_diam_s6a_cmd_cla; extern struct dict_object *ogs_diam_s6a_ulr_flags; extern struct dict_object *ogs_diam_s6a_ula_flags; +extern struct dict_object *ogs_diam_s6a_clr_flags; +extern struct dict_object *ogs_diam_s6a_cancellation_type; extern struct dict_object *ogs_diam_s6a_subscription_data; extern struct dict_object *ogs_diam_s6a_req_eutran_auth_info; extern struct dict_object *ogs_diam_s6a_number_of_requested_vectors; @@ -140,8 +147,15 @@ typedef struct ogs_diam_s6a_ula_message_s { ogs_subscription_data_t subscription_data; } ogs_diam_s6a_ula_message_t; +typedef struct ogs_diam_s6a_clr_message_s { +#define OGS_DIAM_S6A_CLR_FLAGS_S6A_S6D_INDICATOR (0) +#define OGS_DIAM_S6A_CLR_FLAGS_REATTACH_REQUIRED (1) + uint32_t clr_flags; +} ogs_diam_s6a_clr_message_t; + typedef struct ogs_diam_s6a_message_s { #define OGS_DIAM_S6A_CMD_CODE_UPDATE_LOCATION 316 +#define OGS_DIAM_S6A_CMD_CODE_CANCEL_LOCATION 317 #define OGS_DIAM_S6A_CMD_CODE_AUTHENTICATION_INFORMATION 318 uint16_t cmd_code; @@ -157,6 +171,9 @@ typedef struct ogs_diam_s6a_message_s { uint32_t *err; uint32_t *exp_err; + bool during_attach; + + ogs_diam_s6a_clr_message_t clr_message; ogs_diam_s6a_aia_message_t aia_message; ogs_diam_s6a_ula_message_t ula_message; } ogs_diam_s6a_message_t; diff --git a/lib/gtp/xact.h b/lib/gtp/xact.h index 9ff3f5cc4..efcc36135 100644 --- a/lib/gtp/xact.h +++ b/lib/gtp/xact.h @@ -103,6 +103,7 @@ typedef struct ogs_gtp_xact_s { #define OGS_GTP_DELETE_HANDLE_PDN_CONNECTIVITY_REQUEST 5 #define OGS_GTP_DELETE_UE_CONTEXT_REMOVE 6 #define OGS_GTP_DELETE_IN_PATH_SWITCH_REQUEST 7 +#define OGS_GTP_DELETE_NO_ACTION 8 int delete_action; #define OGS_GTP_RELEASE_SEND_UE_CONTEXT_RELEASE_COMMAND 1 diff --git a/src/mme/emm-build.c b/src/mme/emm-build.c index 6e85961aa..36f981d80 100644 --- a/src/mme/emm-build.c +++ b/src/mme/emm-build.c @@ -437,6 +437,25 @@ ogs_pkbuf_t *emm_build_security_mode_command(mme_ue_t *mme_ue) return nas_eps_security_encode(mme_ue, &message); } +ogs_pkbuf_t *emm_build_detach_request(mme_ue_t *mme_ue) +{ + ogs_nas_eps_message_t message; + + ogs_assert(mme_ue); + + memset(&message, 0, sizeof(message)); + message.h.security_header_type = + OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED; + message.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; + + message.emm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; + message.emm.h.message_type = OGS_NAS_EPS_DETACH_REQUEST; + + message.emm.detach_request_to_ue.detach_type.value = mme_ue->mme_to_ue_detach_type; + + return nas_eps_security_encode(mme_ue, &message); +} + ogs_pkbuf_t *emm_build_detach_accept(mme_ue_t *mme_ue) { ogs_nas_eps_message_t message; diff --git a/src/mme/emm-build.h b/src/mme/emm-build.h index d0a930d9b..14f01e888 100644 --- a/src/mme/emm-build.h +++ b/src/mme/emm-build.h @@ -37,6 +37,7 @@ ogs_pkbuf_t *emm_build_security_mode_command(mme_ue_t *mme_ue); ogs_pkbuf_t *emm_build_authentication_request(mme_ue_t *mme_ue); ogs_pkbuf_t *emm_build_authentication_reject(void); +ogs_pkbuf_t *emm_build_detach_request(mme_ue_t *mme_ue); ogs_pkbuf_t *emm_build_detach_accept(mme_ue_t *mme_ue); ogs_pkbuf_t *emm_build_tau_accept(mme_ue_t *mme_ue); diff --git a/src/mme/emm-sm.c b/src/mme/emm-sm.c index a1ecff282..611d72a8a 100644 --- a/src/mme/emm-sm.c +++ b/src/mme/emm-sm.c @@ -129,6 +129,16 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e) break; } + if (mme_ue->mme_to_ue_detach_pending) { + ogs_assert(OGS_OK == nas_eps_send_detach_request(mme_ue)); + mme_gtp_send_delete_all_sessions(mme_ue, OGS_GTP_DELETE_NO_ACTION); + if (MME_P_TMSI_IS_AVAILABLE(mme_ue)) { + ogs_assert(OGS_OK == + sgsap_send_detach_indication(mme_ue)); + } + break; + } + if (!MME_UE_HAVE_IMSI(mme_ue)) { ogs_info("Service request : Unknown UE"); ogs_assert(OGS_OK == @@ -539,6 +549,21 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e) OGS_FSM_TRAN(s, &emm_state_de_registered); break; + case OGS_NAS_EPS_DETACH_ACCEPT: + ogs_debug("Detach accept"); + + CLEAR_MME_UE_TIMER(mme_ue->t3422); + + if (enb_ue) { + s1ap_send_ue_context_release_command(enb_ue, + S1AP_Cause_PR_nas, S1AP_CauseNas_detach, + S1AP_UE_CTX_REL_UE_CONTEXT_REMOVE, 0); + } else { + ogs_warn("[%s] No S1 Context", mme_ue->imsi_bcd); + } + OGS_FSM_TRAN(s, &emm_state_de_registered); + break; + case OGS_NAS_EPS_UPLINK_NAS_TRANSPORT: ogs_debug("Uplink NAS Transport"); ogs_debug(" IMSI[%s]", mme_ue->imsi_bcd); @@ -627,6 +652,23 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e) } break; + case MME_TIMER_T3422: + if (mme_ue->t3422.retry_count >= + mme_timer_cfg(MME_TIMER_T3422)->max_count) { + ogs_warn("Retransmission of Detach Request failed. " + "Stop retransmission"); + OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception); + } else { + rv = nas_eps_send_detach_request(mme_ue); + if (rv == OGS_OK) { + mme_ue->t3422.retry_count++; + } else { + ogs_error("nas_eps_send_detach_request() failed"); + OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception); + } + } + break; + default: ogs_error("Unknown timer[%s:%d]", mme_timer_get_name(e->timer_id), e->timer_id); diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index d8414ff88..484378fb6 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -321,6 +321,9 @@ struct mme_ue_s { }; } nas_eps; + bool mme_to_ue_detach_pending; + uint8_t mme_to_ue_detach_type; + /* UE identity */ #define MME_UE_HAVE_IMSI(__mME) \ ((__mME) && ((__mME)->imsi_len)) diff --git a/src/mme/mme-fd-path.c b/src/mme/mme-fd-path.c index b3ac7d9e9..739409e8e 100644 --- a/src/mme/mme-fd-path.c +++ b/src/mme/mme-fd-path.c @@ -20,6 +20,9 @@ #include "mme-event.h" #include "mme-fd-path.h" +/* handler for Cancel-Location-Request cb */ +static struct disp_hdl *hdl_s6a_clr = NULL; + static struct session_handler *mme_s6a_reg = NULL; struct sess_state { @@ -226,6 +229,7 @@ static void mme_s6a_aia_cb(void *data, struct msg **msg) s6a_message = ogs_calloc(1, sizeof(ogs_diam_s6a_message_t)); ogs_assert(s6a_message); s6a_message->cmd_code = OGS_DIAM_S6A_CMD_CODE_AUTHENTICATION_INFORMATION; + s6a_message->during_attach = true; aia_message = &s6a_message->aia_message; ogs_assert(aia_message); e_utran_vector = &aia_message->e_utran_vector; @@ -634,6 +638,7 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) s6a_message = ogs_calloc(1, sizeof(ogs_diam_s6a_message_t)); ogs_assert(s6a_message); s6a_message->cmd_code = OGS_DIAM_S6A_CMD_CODE_UPDATE_LOCATION; + s6a_message->during_attach = true; ula_message = &s6a_message->ula_message; ogs_assert(ula_message); subscription_data = &ula_message->subscription_data; @@ -1403,10 +1408,129 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) return; } +/* Callback for incoming Cancel-Location-Request messages */ +static int mme_ogs_diam_s6a_clr_cb( struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + int ret; + int error = 0; + + mme_event_t *e = NULL; + mme_ue_t *mme_ue = NULL; + + struct msg *ans, *qry; + ogs_diam_s6a_clr_message_t *clr_message = NULL; + + struct avp_hdr *hdr; + union avp_value val; + + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + + uint32_t result_code = 0; + + ogs_assert(msg); + + ogs_diam_s6a_message_t *s6a_message = NULL; + + ogs_debug("Cancel-Location-Request"); + + s6a_message = ogs_calloc(1, sizeof(ogs_diam_s6a_message_t)); + ogs_assert(s6a_message); + s6a_message->cmd_code = OGS_DIAM_S6A_CMD_CODE_CANCEL_LOCATION; + clr_message = &s6a_message->clr_message; + ogs_assert(clr_message); + + + /* Create answer header */ + qry = *msg; + ret = fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0); + ogs_assert(ret == 0); + ans = *msg; + + ret = fd_msg_search_avp(qry, ogs_diam_user_name, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + ogs_cpystrn(imsi_bcd, (char*)hdr->avp_value->os.data, + ogs_min(hdr->avp_value->os.len, OGS_MAX_IMSI_BCD_LEN)+1); + + mme_ue = mme_ue_find_by_imsi_bcd(imsi_bcd); + + if (!mme_ue) { + ogs_error("Cancel Location for Unknown IMSI[%s]", imsi_bcd); + result_code = OGS_DIAM_S6A_ERROR_USER_UNKNOWN; + error++; + } + + ret = fd_msg_search_avp(qry, ogs_diam_s6a_cancellation_type, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ + ret = fd_msg_rescode_set(ans, (char*)"DIAMETER_SUCCESS", NULL, NULL, 1); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = OGS_DIAM_AUTH_SESSION_NO_STATE_MAINTAINED; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + ret = fd_msg_search_avp(qry, ogs_diam_s6a_clr_flags, &avp); + ogs_assert(ret == 0); + if (avp) { + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + clr_message->clr_flags = hdr->avp_value->i32; + } + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_S6A_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Send the answer */ + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_debug("Cancel-Location-Answer"); + + /* Add this value to the stats */ + ogs_assert( pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_echoed++; + ogs_assert( pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + if (!error) { + int rv; + e = mme_event_new(MME_EVT_S6A_MESSAGE); + ogs_assert(e); + e->mme_ue = mme_ue; + e->s6a_message = s6a_message; + rv = ogs_queue_push(ogs_app()->queue, e); + if (rv != OGS_OK) { + ogs_error("ogs_queue_push() failed:%d", (int)rv); + ogs_free(s6a_message); + mme_event_free(e); + } else { + ogs_pollset_notify(ogs_app()->pollset); + } + } else { + ret = ogs_diam_message_experimental_rescode_set(ans, result_code); + ogs_assert(ret == 0); + } + + return 0; +} int mme_fd_init(void) { int ret; + struct disp_when data; ret = ogs_diam_init(FD_MODE_CLIENT, mme_self()->diam_conf_path, mme_self()->diam_config); @@ -1420,6 +1544,12 @@ int mme_fd_init(void) ret = fd_sess_handler_create(&mme_s6a_reg, &state_cleanup, NULL, NULL); ogs_assert(ret == 0); + /* Specific handler for Cancel-Location-Request */ + data.command = ogs_diam_s6a_cmd_clr; + ret = fd_disp_register(mme_ogs_diam_s6a_clr_cb, DISP_HOW_CC, &data, NULL, + &hdl_s6a_clr); + ogs_assert(ret == 0); + /* Advertise the support for the application in the peer */ ret = fd_disp_app_support(ogs_diam_s6a_application, ogs_diam_vendor, 1, 0); ogs_assert(ret == 0); @@ -1437,5 +1567,8 @@ void mme_fd_final(void) ret = fd_sess_handler_destroy(&mme_s6a_reg, NULL); ogs_assert(ret == OGS_OK); + if (hdl_s6a_clr) + (void) fd_disp_unregister(&hdl_s6a_clr, NULL); + ogs_diam_final(); } diff --git a/src/mme/mme-s11-handler.c b/src/mme/mme-s11-handler.c index f6b70b4dd..df248e31b 100644 --- a/src/mme/mme-s11-handler.c +++ b/src/mme/mme-s11-handler.c @@ -636,6 +636,8 @@ void mme_s11_handle_delete_session_response( return; + } else if (action == OGS_GTP_DELETE_NO_ACTION) { + } else { ogs_fatal("Invalid action = %d", action); ogs_assert_if_reached(); diff --git a/src/mme/mme-s6a-handler.c b/src/mme/mme-s6a-handler.c index 572295d3f..14cf36490 100644 --- a/src/mme/mme-s6a-handler.c +++ b/src/mme/mme-s6a-handler.c @@ -101,3 +101,27 @@ void mme_s6a_handle_ula(mme_ue_t *mme_ue, mme_ue->num_of_session = i; mme_ue->context_identifier = slice_data->context_identifier; } + +void mme_s6a_handle_clr(mme_ue_t *mme_ue, + ogs_diam_s6a_clr_message_t *clr_message) +{ + ogs_assert(mme_ue); + ogs_assert(clr_message); + + if (clr_message->clr_flags & OGS_DIAM_S6A_CLR_REATTACH_REQUIRED) { + mme_ue->mme_to_ue_detach_type = OGS_NAS_DETACH_TYPE_TO_UE_RE_ATTACH_REQUIRED; + } else { + mme_ue->mme_to_ue_detach_type = OGS_NAS_DETACH_TYPE_TO_UE_RE_ATTACH_NOT_REQUIRED; + } + + if (OGS_FSM_CHECK(&mme_ue->sm, emm_state_de_registered)) { + // Remove all trace of subscriber even when detached. + mme_ue_hash_remove(mme_ue); + mme_ue_remove(mme_ue); + } else if (ECM_IDLE(mme_ue)) { + ogs_assert(OGS_OK == s1ap_send_paging(mme_ue, S1AP_CNDomain_ps)); + mme_ue->mme_to_ue_detach_pending = true; + } else { + ogs_assert(OGS_OK == nas_eps_send_detach_request(mme_ue)); + } +} diff --git a/src/mme/mme-s6a-handler.h b/src/mme/mme-s6a-handler.h index b837b2bc5..097d94ba1 100644 --- a/src/mme/mme-s6a-handler.h +++ b/src/mme/mme-s6a-handler.h @@ -30,6 +30,8 @@ void mme_s6a_handle_aia(mme_ue_t *mme_ue, ogs_diam_s6a_aia_message_t *aia_message); void mme_s6a_handle_ula(mme_ue_t *mme_ue, ogs_diam_s6a_ula_message_t *ula_message); +void mme_s6a_handle_clr(mme_ue_t *mme_ue, + ogs_diam_s6a_clr_message_t *clr_message); #ifdef __cplusplus } diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index ac0cf69f0..131397e3f 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -437,40 +437,42 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) s6a_message = e->s6a_message; ogs_assert(s6a_message); - enb_ue = enb_ue_cycle(mme_ue->enb_ue); - if (!enb_ue) { - ogs_error("S1 context has already been removed"); + if (s6a_message->during_attach) { + enb_ue = enb_ue_cycle(mme_ue->enb_ue); + if (!enb_ue) { + ogs_error("S1 context has already been removed"); - ogs_subscription_data_free( - &s6a_message->ula_message.subscription_data); - ogs_free(s6a_message); - break; - } + ogs_subscription_data_free( + &s6a_message->ula_message.subscription_data); + ogs_free(s6a_message); + break; + } - if (s6a_message->result_code != ER_DIAMETER_SUCCESS) { - /* Unfortunately fd doesn't distinguish - * between result-code and experimental-result-code. - * - * However, e.g. 5004 has different meaning - * if used in result-code than in experimental-result-code */ - uint8_t emm_cause = emm_cause_from_diameter( - mme_ue, s6a_message->err, s6a_message->exp_err); + if (s6a_message->result_code != ER_DIAMETER_SUCCESS) { + /* Unfortunately fd doesn't distinguish + * between result-code and experimental-result-code. + * + * However, e.g. 5004 has different meaning + * if used in result-code than in experimental-result-code */ + uint8_t emm_cause = emm_cause_from_diameter( + mme_ue, s6a_message->err, s6a_message->exp_err); - ogs_info("[%s] Attach reject [OGS_NAS_EMM_CAUSE:%d]", - mme_ue->imsi_bcd, emm_cause); - ogs_assert(OGS_OK == - nas_eps_send_attach_reject(mme_ue, - emm_cause, OGS_NAS_ESM_CAUSE_PROTOCOL_ERROR_UNSPECIFIED)); + ogs_info("[%s] Attach reject [OGS_NAS_EMM_CAUSE:%d]", + mme_ue->imsi_bcd, emm_cause); + ogs_assert(OGS_OK == + nas_eps_send_attach_reject(mme_ue, + emm_cause, OGS_NAS_ESM_CAUSE_PROTOCOL_ERROR_UNSPECIFIED)); - ogs_assert(OGS_OK == - s1ap_send_ue_context_release_command(enb_ue, - S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release, - S1AP_UE_CTX_REL_UE_CONTEXT_REMOVE, 0)); + ogs_assert(OGS_OK == + s1ap_send_ue_context_release_command(enb_ue, + S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release, + S1AP_UE_CTX_REL_UE_CONTEXT_REMOVE, 0)); - ogs_subscription_data_free( - &s6a_message->ula_message.subscription_data); - ogs_free(s6a_message); - break; + ogs_subscription_data_free( + &s6a_message->ula_message.subscription_data); + ogs_free(s6a_message); + break; + } } switch (s6a_message->cmd_code) { @@ -504,6 +506,17 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) ogs_assert_if_reached(); } break; + case OGS_DIAM_S6A_CMD_CODE_CANCEL_LOCATION: + mme_s6a_handle_clr(mme_ue, &s6a_message->clr_message); + + if (SESSION_CONTEXT_IS_AVAILABLE(mme_ue) && !mme_ue->mme_to_ue_detach_pending) { + mme_gtp_send_delete_all_sessions(mme_ue, OGS_GTP_DELETE_NO_ACTION); + } + if (MME_P_TMSI_IS_AVAILABLE(mme_ue)) { + ogs_assert(OGS_OK == + sgsap_send_detach_indication(mme_ue)); + } + break; default: ogs_error("Invalid Type[%d]", s6a_message->cmd_code); break; diff --git a/src/mme/nas-path.c b/src/mme/nas-path.c index 6ef8cf554..7cd25cae3 100644 --- a/src/mme/nas-path.c +++ b/src/mme/nas-path.c @@ -256,6 +256,42 @@ int nas_eps_send_authentication_reject(mme_ue_t *mme_ue) return rv; } +int nas_eps_send_detach_request(mme_ue_t *mme_ue) +{ + int rv; + + enb_ue_t *enb_ue = NULL; + ogs_pkbuf_t *emmbuf = NULL; + + ogs_assert(mme_ue); + enb_ue = enb_ue_cycle(mme_ue->enb_ue); + ogs_expect_or_return_val(enb_ue, OGS_ERROR); + + ogs_debug("[%s] Detach request to UE", mme_ue->imsi_bcd); + + if (mme_ue->t3422.pkbuf) { + emmbuf = mme_ue->t3422.pkbuf; + ogs_expect_or_return_val(emmbuf, OGS_ERROR); + } else { + emmbuf = emm_build_identity_request(mme_ue); + ogs_expect_or_return_val(emmbuf, OGS_ERROR); + } + + mme_ue->t3422.pkbuf = ogs_pkbuf_copy(emmbuf); + ogs_expect_or_return_val(mme_ue->t3422.pkbuf, OGS_ERROR); + ogs_timer_start(mme_ue->t3422.timer, + mme_timer_cfg(MME_TIMER_T3422)->duration); + + /* send detach request */ + emmbuf = emm_build_detach_request(mme_ue); + ogs_expect_or_return_val(emmbuf, OGS_ERROR); + + rv = nas_eps_send_to_downlink_nas_transport(mme_ue, emmbuf); + ogs_expect_or_return_val(rv == OGS_OK, rv); + + return rv; +} + int nas_eps_send_detach_accept(mme_ue_t *mme_ue) { int rv; diff --git a/src/mme/nas-path.h b/src/mme/nas-path.h index 374872aa9..3e3183485 100644 --- a/src/mme/nas-path.h +++ b/src/mme/nas-path.h @@ -43,6 +43,7 @@ int nas_eps_send_authentication_reject(mme_ue_t *mme_ue); int nas_eps_send_security_mode_command(mme_ue_t *mme_ue); +int nas_eps_send_detach_request(mme_ue_t *mme_ue); int nas_eps_send_detach_accept(mme_ue_t *mme_ue); int nas_eps_send_pdn_connectivity_reject( diff --git a/src/mme/sgsap-handler.c b/src/mme/sgsap-handler.c index 41991973c..458bc73b3 100644 --- a/src/mme/sgsap-handler.c +++ b/src/mme/sgsap-handler.c @@ -272,7 +272,9 @@ void sgsap_handle_detach_ack(mme_vlr_t *vlr, ogs_pkbuf_t *pkbuf) ogs_debug(" IMSI[%s]", mme_ue->imsi_bcd); - mme_send_delete_session_or_detach(mme_ue); + if (!mme_ue->mme_to_ue_detach_pending) { + mme_send_delete_session_or_detach(mme_ue); + } } void sgsap_handle_paging_request(mme_vlr_t *vlr, ogs_pkbuf_t *pkbuf)