diff --git a/lib/diameter/s6a/message.h b/lib/diameter/s6a/message.h index f4d796860..8fbf5f302 100644 --- a/lib/diameter/s6a/message.h +++ b/lib/diameter/s6a/message.h @@ -168,6 +168,8 @@ typedef struct ogs_diam_s6a_message_s { #define OGS_DIAM_S6A_ERROR_EQUIPMENT_UNKNOWN 5422 #define OGS_DIAM_S6A_ERROR_UNKOWN_SERVING_NODE 5423 uint32_t result_code; + uint32_t *err; + uint32_t *exp_err; ogs_diam_s6a_aia_message_t aia_message; ogs_diam_s6a_ula_message_t ula_message; diff --git a/src/mme/emm-sm.c b/src/mme/emm-sm.c index f4b4a1551..0e20cef08 100644 --- a/src/mme/emm-sm.c +++ b/src/mme/emm-sm.c @@ -129,18 +129,16 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e) if (!MME_UE_HAVE_IMSI(mme_ue)) { ogs_warn("[EMM] Service request : Unknown UE"); - rv = nas_send_service_reject(mme_ue, + nas_send_service_reject(mme_ue, EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); - ogs_assert(rv == OGS_OK); OGS_FSM_TRAN(s, &emm_state_exception); return; } if (!SECURITY_CONTEXT_IS_VALID(mme_ue)) { ogs_warn("No Security Context : IMSI[%s]", mme_ue->imsi_bcd); - rv = nas_send_service_reject(mme_ue, + nas_send_service_reject(mme_ue, EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); - ogs_assert(rv == OGS_OK); OGS_FSM_TRAN(s, &emm_state_exception); return; } @@ -217,9 +215,8 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e) if (!MME_UE_HAVE_IMSI(mme_ue)) { ogs_warn("[EMM] Extended Service request : Unknown UE"); - rv = nas_send_service_reject(mme_ue, + nas_send_service_reject(mme_ue, EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); - ogs_assert(rv == OGS_OK); OGS_FSM_TRAN(s, &emm_state_exception); return; } @@ -399,27 +396,24 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e) if (!MME_P_TMSI_IS_AVAILABLE(mme_ue)) { ogs_warn("No P-TMSI : UE[%s]", mme_ue->imsi_bcd); - rv = nas_send_service_reject(mme_ue, + nas_send_service_reject(mme_ue, EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); - ogs_assert(rv == OGS_OK); OGS_FSM_TRAN(s, emm_state_exception); break; } if (!SESSION_CONTEXT_IS_AVAILABLE(mme_ue)) { ogs_warn("No PDN Connection : UE[%s]", mme_ue->imsi_bcd); - rv = nas_send_service_reject(mme_ue, + nas_send_service_reject(mme_ue, EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); - ogs_assert(rv == OGS_OK); OGS_FSM_TRAN(s, emm_state_exception); break; } if (!SECURITY_CONTEXT_IS_VALID(mme_ue)) { ogs_warn("No Security Context : IMSI[%s]", mme_ue->imsi_bcd); - rv = nas_send_service_reject(mme_ue, + nas_send_service_reject(mme_ue, EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); - ogs_assert(rv == OGS_OK); OGS_FSM_TRAN(s, &emm_state_exception); return; } @@ -441,9 +435,8 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e) } else { ogs_warn(" Unknown CSFB Service Type[%d]", mme_ue->nas_eps.service.service_type); - rv = nas_send_service_reject(mme_ue, + nas_send_service_reject(mme_ue, EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); - ogs_assert(rv == OGS_OK); OGS_FSM_TRAN(s, &emm_state_exception); return; } @@ -468,9 +461,8 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e) } else { ogs_warn(" Unknown CSFB Service Type[%d]", mme_ue->nas_eps.service.service_type); - rv = nas_send_service_reject(mme_ue, + nas_send_service_reject(mme_ue, EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); - ogs_assert(rv == OGS_OK); OGS_FSM_TRAN(s, &emm_state_exception); return; } @@ -681,9 +673,8 @@ void emm_state_security_mode(ogs_fsm_t *s, mme_event_t *e) if (message->emm.h.security_header_type == OGS_NAS_SECURITY_HEADER_FOR_SERVICE_REQUEST_MESSAGE) { ogs_debug("[EMM] Service request"); - rv = nas_send_service_reject(mme_ue, + nas_send_service_reject(mme_ue, EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED); - ogs_assert(rv == OGS_OK); OGS_FSM_TRAN(s, &emm_state_exception); return; } diff --git a/src/mme/mme-fd-path.c b/src/mme/mme-fd-path.c index 2a5ff9ddb..3d25a51ef 100644 --- a/src/mme/mme-fd-path.c +++ b/src/mme/mme-fd-path.c @@ -245,6 +245,7 @@ static void mme_s6a_aia_cb(void *data, struct msg **msg) ret = fd_msg_avp_hdr(avp, &hdr); ogs_assert(ret == 0); s6a_message->result_code = hdr->avp_value->i32; + s6a_message->err = &s6a_message->result_code; ogs_debug(" Result Code: %d", hdr->avp_value->i32); } else { ret = fd_msg_search_avp(*msg, ogs_diam_experimental_result, &avp); @@ -256,6 +257,7 @@ static void mme_s6a_aia_cb(void *data, struct msg **msg) ret = fd_msg_avp_hdr(avpch, &hdr); ogs_assert(ret == 0); s6a_message->result_code = hdr->avp_value->i32; + s6a_message->exp_err = &s6a_message->result_code; ogs_debug(" Experimental Result Code: %d", s6a_message->result_code); } @@ -618,6 +620,7 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) ret = fd_msg_avp_hdr(avp, &hdr); ogs_assert(ret == 0); s6a_message->result_code = hdr->avp_value->i32; + s6a_message->err = &s6a_message->result_code; ogs_debug(" Result Code: %d", hdr->avp_value->i32); } else { ret = fd_msg_search_avp(*msg, ogs_diam_experimental_result, &avp); @@ -630,6 +633,7 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) ret = fd_msg_avp_hdr(avpch, &hdr); ogs_assert(ret == 0); s6a_message->result_code = hdr->avp_value->i32; + s6a_message->exp_err = &s6a_message->result_code; ogs_debug(" Experimental Result Code: %d", s6a_message->result_code); } diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index 85f3b5dba..50be30732 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -33,6 +33,51 @@ #include "mme-s6a-handler.h" #include "mme-path.h" +/* 3GPP TS 29.272 Annex A; Table !.a: + * Mapping from S6a error codes to NAS Cause Codes */ +static uint8_t emm_cause_from_diameter( + const uint32_t *dia_err, const uint32_t *dia_exp_err) +{ + if (dia_exp_err) { + switch (*dia_exp_err) { + case OGS_DIAM_S6A_ERROR_USER_UNKNOWN: /* 5001 */ + return EMM_CAUSE_EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED; + case OGS_DIAM_S6A_ERROR_UNKNOWN_EPS_SUBSCRIPTION: /* 5420 */ + /* FIXME: Error diagnostic? */ + return EMM_CAUSE_NO_SUITABLE_CELLS_IN_TRACKING_AREA; + case OGS_DIAM_S6A_ERROR_RAT_NOT_ALLOWED: /* 5421 */ + return EMM_CAUSE_ROAMING_NOT_ALLOWED_IN_THIS_TRACKING_AREA; + case OGS_DIAM_S6A_ERROR_ROAMING_NOT_ALLOWED: /* 5004 */ + return EMM_CAUSE_PLMN_NOT_ALLOWED; + //return EMM_CAUSE_EPS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN; (ODB_HPLMN_APN) + //return EMM_CAUSE_ESM_FAILURE; (ODB_ALL_APN) + case OGS_DIAM_S6A_AUTHENTICATION_DATA_UNAVAILABLE: /* 4181 */ + return EMM_CAUSE_NETWORK_FAILURE; + } + } + if (dia_err) { + switch (*dia_err) { + case ER_DIAMETER_AUTHORIZATION_REJECTED: /* 5003 */ + case ER_DIAMETER_UNABLE_TO_DELIVER: /* 3002 */ + case ER_DIAMETER_REALM_NOT_SERVED: /* 3003 */ + return EMM_CAUSE_NO_SUITABLE_CELLS_IN_TRACKING_AREA; + case ER_DIAMETER_UNABLE_TO_COMPLY: /* 5012 */ + case ER_DIAMETER_INVALID_AVP_VALUE: /* 5004 */ + case ER_DIAMETER_AVP_UNSUPPORTED: /* 5001 */ + case ER_DIAMETER_MISSING_AVP: /* 5005 */ + case ER_DIAMETER_RESOURCES_EXCEEDED: /* 5006 */ + case ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES: /* 5009 */ + default: /* FIXME: only permanent */ + return EMM_CAUSE_NETWORK_FAILURE; + } + } + + ogs_error("Unexpected Diameter Result Code %d/%d, defaulting to severe " + "network failure", + dia_err ? *dia_err : -1, dia_exp_err ? *dia_exp_err : -1); + return EMM_CAUSE_SEVERE_NETWORK_FAILURE; +} + void mme_state_initial(ogs_fsm_t *s, mme_event_t *e) { mme_sm_debug(e); @@ -352,11 +397,17 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) if (s6a_message->result_code != ER_DIAMETER_SUCCESS) { enb_ue_t *enb_ue = NULL; - rv = nas_send_attach_reject(mme_ue, - EMM_CAUSE_IMSI_UNKNOWN_IN_HSS, - ESM_CAUSE_PROTOCOL_ERROR_UNSPECIFIED); - ogs_assert(rv == OGS_OK); - ogs_warn("EMM_CAUSE : IMSI Unknown in HSS"); + /* 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( + s6a_message->err, s6a_message->exp_err); + + nas_send_attach_reject(mme_ue, + emm_cause, ESM_CAUSE_PROTOCOL_ERROR_UNSPECIFIED); + ogs_warn("EMM_CAUSE : %d", emm_cause); enb_ue = mme_ue->enb_ue; ogs_assert(enb_ue); diff --git a/src/mme/nas-path.c b/src/mme/nas-path.c index bfd2d6464..67a57e08f 100644 --- a/src/mme/nas-path.c +++ b/src/mme/nas-path.c @@ -114,7 +114,7 @@ int nas_send_attach_accept(mme_ue_t *mme_ue) return OGS_OK; } -int nas_send_attach_reject(mme_ue_t *mme_ue, +void nas_send_attach_reject(mme_ue_t *mme_ue, ogs_nas_emm_cause_t emm_cause, ogs_nas_esm_cause_t esm_cause) { int rv; @@ -136,8 +136,6 @@ int nas_send_attach_reject(mme_ue_t *mme_ue, ogs_assert(rv == OGS_OK && emmbuf); rv = nas_send_to_downlink_nas_transport(mme_ue, emmbuf); ogs_assert(rv == OGS_OK); - - return rv; } int nas_send_identity_request(mme_ue_t *mme_ue) @@ -289,9 +287,8 @@ int nas_send_pdn_connectivity_reject( } else { /* During the UE-attach process, we'll send Attach-Reject * with pyggybacking PDN-connectivity-Reject */ - rv = nas_send_attach_reject(mme_ue, + nas_send_attach_reject(mme_ue, EMM_CAUSE_EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED, esm_cause); - ogs_assert(rv == OGS_OK); } return OGS_OK; @@ -494,7 +491,7 @@ int nas_send_tau_reject(mme_ue_t *mme_ue, ogs_nas_emm_cause_t emm_cause) return OGS_OK; } -int nas_send_service_reject(mme_ue_t *mme_ue, +void nas_send_service_reject(mme_ue_t *mme_ue, ogs_nas_emm_cause_t emm_cause) { int rv; @@ -508,8 +505,6 @@ int nas_send_service_reject(mme_ue_t *mme_ue, rv = nas_send_to_downlink_nas_transport(mme_ue, emmbuf); ogs_assert(rv == OGS_OK); - - return OGS_OK; } int nas_send_cs_service_notification(mme_ue_t *mme_ue) diff --git a/src/mme/nas-path.h b/src/mme/nas-path.h index e88c300cb..5c5ae2cfb 100644 --- a/src/mme/nas-path.h +++ b/src/mme/nas-path.h @@ -32,7 +32,7 @@ int nas_send_emm_to_esm( int nas_send_to_downlink_nas_transport(mme_ue_t *mme_ue, ogs_pkbuf_t *pkbuf); int nas_send_attach_accept(mme_ue_t *mme_ue); -int nas_send_attach_reject(mme_ue_t *mme_ue, +void nas_send_attach_reject(mme_ue_t *mme_ue, ogs_nas_emm_cause_t emm_cause, ogs_nas_esm_cause_t esm_cause); int nas_send_identity_request(mme_ue_t *mme_ue); @@ -58,7 +58,7 @@ int nas_send_deactivate_bearer_context_request(mme_bearer_t *bearer); int nas_send_tau_accept(mme_ue_t *mme_ue, S1AP_ProcedureCode_t procedureCode); int nas_send_tau_reject(mme_ue_t *mme_ue, ogs_nas_esm_cause_t emm_cause); -int nas_send_service_reject(mme_ue_t *mme_ue, ogs_nas_emm_cause_t emm_cause); +void nas_send_service_reject(mme_ue_t *mme_ue, ogs_nas_emm_cause_t emm_cause); int nas_send_cs_service_notification(mme_ue_t *mme_ue); int nas_send_downlink_nas_transport(