From 1f2a8678edeadc2721ed0bc57425fffe989a2156 Mon Sep 17 00:00:00 2001 From: jmasterfunk84 <48972964+jmasterfunk84@users.noreply.github.com> Date: Sun, 2 Oct 2022 04:36:24 -0600 Subject: [PATCH] [MME] Support for Insert Subscriber Data (#1794) * [MME] Support for Insert Subscriber Data * Supported AVPs in IDR will overwrite existing subscription information * Provide error on partial APN updates * IDR and ULA use same function to process AVPs * Move subdatamask values into s6a, so both HSS and MME can use them * Updates are not actioned at this time. A Re-attach is required for most changes to take effect * Memory issue on IDR exceptions * Remove of handling MSIDSN change until DSR is used --- lib/diameter/s6a/message.h | 14 + lib/proto/types.h | 1 + src/hss/hss-context.c | 30 +- src/hss/hss-s6a-path.c | 16 +- src/hss/hss-s6a-path.h | 10 - src/mme/mme-fd-path.c | 1305 +++++++++++++++++++----------------- src/mme/mme-s6a-handler.c | 153 +++-- src/mme/mme-s6a-handler.h | 2 + src/mme/mme-sm.c | 2 + 9 files changed, 822 insertions(+), 711 deletions(-) diff --git a/lib/diameter/s6a/message.h b/lib/diameter/s6a/message.h index ded7d31a0..cb5ae6108 100644 --- a/lib/diameter/s6a/message.h +++ b/lib/diameter/s6a/message.h @@ -77,6 +77,19 @@ extern "C" { #define OGS_DIAM_S6A_CT_UPDATE_PROCEDURE_IWF (3) #define OGS_DIAM_S6A_CT_INITIAL_ATTACH_PROCEDURE (4) +#define OGS_DIAM_S6A_SUBDATA_NO_UPDATE (0) +#define OGS_DIAM_S6A_SUBDATA_SUB_STATUS (1) +#define OGS_DIAM_S6A_SUBDATA_MSISDN (1 << 1) +#define OGS_DIAM_S6A_SUBDATA_A_MSISDN (1 << 2) +#define OGS_DIAM_S6A_SUBDATA_NAM (1 << 3) +#define OGS_DIAM_S6A_SUBDATA_ODB (1 << 4) +#define OGS_DIAM_S6A_SUBDATA_ARD (1 << 5) +#define OGS_DIAM_S6A_SUBDATA_CC (1 << 6) +#define OGS_DIAM_S6A_SUBDATA_UEAMBR (1 << 7) +#define OGS_DIAM_S6A_SUBDATA_APN_CONFIG (1 << 8) +#define OGS_DIAM_S6A_SUBDATA_RAU_TAU_TIMER (1 << 9) +#define OGS_DIAM_S6A_SUBDATA_ALL 0xFFFFFFFF + extern struct dict_object *ogs_diam_s6a_application; extern struct dict_object *ogs_diam_s6a_cmd_air; @@ -179,6 +192,7 @@ typedef struct ogs_diam_s6a_idr_message_s { #define OGS_DIAM_S6A_IDR_FLAGS_RAT_TYPE (1 << 7) #define OGS_DIAM_S6A_IDR_FLAGS_PCSCF_Restoration (1 << 8) uint32_t idr_flags; + uint32_t subdatamask; ogs_subscription_data_t subscription_data; } ogs_diam_s6a_idr_message_t; diff --git a/lib/proto/types.h b/lib/proto/types.h index b057591c0..16486cb88 100644 --- a/lib/proto/types.h +++ b/lib/proto/types.h @@ -651,6 +651,7 @@ typedef struct ogs_slice_data_s { bool default_indicator; uint32_t context_identifier; /* EPC for checking default APN */ + uint32_t all_apn_config_inc; int num_of_session; ogs_session_t session[OGS_MAX_NUM_OF_SESS]; diff --git a/src/hss/hss-context.c b/src/hss/hss-context.c index f11430112..a33961ce5 100644 --- a/src/hss/hss-context.c +++ b/src/hss/hss-context.c @@ -1238,50 +1238,36 @@ int hss_handle_change_event(const bson_t *document) "request_cancel_location") && BSON_ITER_HOLDS_BOOL(&child2_iter)) { send_clr_flag = (char *)bson_iter_bool(&child2_iter); - } else if (!strncmp(child2_key, "msisdn", - strlen("msisdn"))) { - int msisdn_count = 0; - bson_iter_recurse(&child2_iter, &child3_iter); - while (bson_iter_next(&child3_iter)) { - if (BSON_ITER_HOLDS_UTF8(&child3_iter)) { - msisdn_count++; - } - } - if (msisdn_count) { - send_idr_flag = true; - subdatamask = (subdatamask | - OGS_HSS_SUBDATA_MSISDN); - } else { - send_clr_flag = true; - } } else if (!strncmp(child2_key, "access_restriction_data", strlen("access_restriction_data"))) { send_idr_flag = true; - subdatamask = (subdatamask | OGS_HSS_SUBDATA_ARD); + subdatamask = (subdatamask | OGS_DIAM_S6A_SUBDATA_ARD); } else if (!strncmp(child2_key, "subscriber_status", strlen("subscriber_status"))) { send_idr_flag = true; subdatamask = (subdatamask | - OGS_HSS_SUBDATA_SUB_STATUS); + OGS_DIAM_S6A_SUBDATA_SUB_STATUS); } else if (!strncmp(child2_key, "network_access_mode", strlen("network_access_mode"))) { send_idr_flag = true; - subdatamask = (subdatamask | OGS_HSS_SUBDATA_NAM); + subdatamask = (subdatamask | OGS_DIAM_S6A_SUBDATA_NAM); } else if (!strncmp(child2_key, "ambr", strlen("ambr"))) { send_idr_flag = true; - subdatamask = (subdatamask | OGS_HSS_SUBDATA_UEAMBR); + subdatamask = (subdatamask | + OGS_DIAM_S6A_SUBDATA_UEAMBR); } else if (!strncmp(child2_key, "subscribed_rau_tau_timer", strlen("subscribed_rau_tau_timer"))) { send_idr_flag = true; subdatamask = (subdatamask | - OGS_HSS_SUBDATA_RAU_TAU_TIMER); + OGS_DIAM_S6A_SUBDATA_RAU_TAU_TIMER); } else if (!strncmp(child2_key, "slice", strlen("slice"))) { send_idr_flag = true; - subdatamask = (subdatamask | OGS_HSS_SUBDATA_SLICE); + subdatamask = (subdatamask | + OGS_DIAM_S6A_SUBDATA_APN_CONFIG); } } } diff --git a/src/hss/hss-s6a-path.c b/src/hss/hss-s6a-path.c index ff9abd6f3..6ca47fd27 100644 --- a/src/hss/hss-s6a-path.c +++ b/src/hss/hss-s6a-path.c @@ -314,7 +314,7 @@ static int hss_s6a_avp_add_subscription_data( int i; - if (subdatamask & OGS_HSS_SUBDATA_MSISDN) { + if (subdatamask & OGS_DIAM_S6A_SUBDATA_MSISDN) { /* * TS29.328 * 6.3.2 MSISDN AVP @@ -350,7 +350,7 @@ static int hss_s6a_avp_add_subscription_data( } } - if (subdatamask & OGS_HSS_SUBDATA_ARD) { + if (subdatamask & OGS_DIAM_S6A_SUBDATA_ARD) { if (subscription_data->access_restriction_data) { ret = fd_msg_avp_new(ogs_diam_s6a_access_restriction_data, 0, &avp_access_restriction_data); @@ -364,7 +364,7 @@ static int hss_s6a_avp_add_subscription_data( } } - if (subdatamask & OGS_HSS_SUBDATA_SUB_STATUS) { + if (subdatamask & OGS_DIAM_S6A_SUBDATA_SUB_STATUS) { ret = fd_msg_avp_new( ogs_diam_s6a_subscriber_status, 0, &avp_subscriber_status); ogs_assert(ret == 0); @@ -375,7 +375,7 @@ static int hss_s6a_avp_add_subscription_data( ogs_assert(ret == 0); } - if (subdatamask & OGS_HSS_SUBDATA_NAM) { + if (subdatamask & OGS_DIAM_S6A_SUBDATA_NAM) { ret = fd_msg_avp_new(ogs_diam_s6a_network_access_mode, 0, &avp_network_access_mode); ogs_assert(ret == 0); @@ -386,7 +386,7 @@ static int hss_s6a_avp_add_subscription_data( ogs_assert(ret == 0); } - if (subdatamask & OGS_HSS_SUBDATA_UEAMBR) { + if (subdatamask & OGS_DIAM_S6A_SUBDATA_UEAMBR) { /* Set the AMBR */ ret = fd_msg_avp_new(ogs_diam_s6a_ambr, 0, &avp_ambr); ogs_assert(ret == 0); @@ -412,7 +412,7 @@ static int hss_s6a_avp_add_subscription_data( ogs_assert(ret == 0); } - if (subdatamask & OGS_HSS_SUBDATA_RAU_TAU_TIMER) { + if (subdatamask & OGS_DIAM_S6A_SUBDATA_RAU_TAU_TIMER) { /* Set the Subscribed RAU TAU Timer */ ret = fd_msg_avp_new( ogs_diam_s6a_subscribed_rau_tau_timer, 0, &avp_rau_tau_timer); @@ -425,7 +425,7 @@ static int hss_s6a_avp_add_subscription_data( ogs_assert(ret == 0); } - if (subdatamask & OGS_HSS_SUBDATA_SLICE) { + if (subdatamask & OGS_DIAM_S6A_SUBDATA_APN_CONFIG) { /* For EPC, we'll use first Slice in Subscription */ if (subscription_data->num_of_slice) slice_data = &subscription_data->slice[0]; @@ -910,7 +910,7 @@ static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, ret = fd_msg_avp_new(ogs_diam_s6a_subscription_data, 0, &avp); ogs_assert(ret == 0); rv = hss_s6a_avp_add_subscription_data(&subscription_data, - avp, OGS_HSS_SUBDATA_ALL); + avp, OGS_DIAM_S6A_SUBDATA_ALL); if (rv != OGS_OK) { result_code = OGS_DIAM_S6A_ERROR_UNKNOWN_EPS_SUBSCRIPTION; goto out; diff --git a/src/hss/hss-s6a-path.h b/src/hss/hss-s6a-path.h index 3e3b0381f..5af40e487 100644 --- a/src/hss/hss-s6a-path.h +++ b/src/hss/hss-s6a-path.h @@ -26,16 +26,6 @@ extern "C" { #endif -#define OGS_HSS_SUBDATA_NO_UPDATE (0) -#define OGS_HSS_SUBDATA_MSISDN (1) -#define OGS_HSS_SUBDATA_ARD (1 << 1) -#define OGS_HSS_SUBDATA_SUB_STATUS (1 << 2) -#define OGS_HSS_SUBDATA_NAM (1 << 3) -#define OGS_HSS_SUBDATA_UEAMBR (1 << 4) -#define OGS_HSS_SUBDATA_RAU_TAU_TIMER (1 << 5) -#define OGS_HSS_SUBDATA_SLICE (1 << 6) -#define OGS_HSS_SUBDATA_ALL 0xFFFFFFFF - /* HSS Sends Cancel Location Request to MME */ void hss_s6a_send_clr(char *imsi_bcd, char *mme_host, char *mme_realm, uint32_t cancellation_type); diff --git a/src/mme/mme-fd-path.c b/src/mme/mme-fd-path.c index 024ef3ae3..281be44b5 100644 --- a/src/mme/mme-fd-path.c +++ b/src/mme/mme-fd-path.c @@ -28,6 +28,11 @@ static struct disp_hdl *hdl_s6a_idr = NULL; static struct session_handler *mme_s6a_reg = NULL; +/* s6a process Subscription-Data from avp */ +static int mme_s6a_subscription_data_from_avp(struct avp *avp, + ogs_subscription_data_t *subscription_data, + mme_ue_t *mme_ue, uint32_t *subdatamask); + struct sess_state { mme_ue_t *mme_ue; struct timespec ts; /* Time of sending the message */ @@ -41,6 +46,622 @@ static void state_cleanup(struct sess_state *sess_data, os0_t sid, void *opaque) ogs_free(sess_data); } +/* s6a process Subscription-Data from avp */ +static int mme_s6a_subscription_data_from_avp(struct avp *avp, + ogs_subscription_data_t *subscription_data, + mme_ue_t *mme_ue, uint32_t *subdatamask) +{ + int ret; + int error = 0; + char buf[OGS_CHRGCHARS_LEN]; + struct avp *avpch1, *avpch2, *avpch3, *avpch4, *avpch5; + struct avp_hdr *hdr; + ogs_sockaddr_t addr; + + ogs_assert(avp); + ogs_assert(subscription_data); + ogs_assert(mme_ue); + ogs_assert(subdatamask); + + /* AVP: 'MSISDN'( 701 ) + * The MSISDN AVP is of type OctetString. This AVP contains an MSISDN, + * in international number format as described in ITU-T Rec E.164 [8], + * encoded as a TBCD-string, i.e. digits from 0 through 9 are encoded + * 0000 to 1001; 1111 is used as a filler when there is an odd number + * of digits; bits 8 to 5 of octet n encode digit 2n; bits 4 to 1 of + * octet n encode digit 2(n-1)+1. + * Reference: 3GPP TS 29.329 + */ + ret = fd_avp_search_avp(avp, ogs_diam_s6a_msisdn, &avpch1); + ogs_assert(ret == 0); + if (avpch1) { + ret = fd_msg_avp_hdr(avpch1, &hdr); + ogs_assert(ret == 0); + if (hdr->avp_value->os.data && hdr->avp_value->os.len) { + mme_ue->msisdn_len = hdr->avp_value->os.len; + memcpy(mme_ue->msisdn, hdr->avp_value->os.data, + ogs_min(mme_ue->msisdn_len, OGS_MAX_MSISDN_LEN)); + ogs_buffer_to_bcd(mme_ue->msisdn, + mme_ue->msisdn_len, mme_ue->msisdn_bcd); + *subdatamask = (*subdatamask | OGS_DIAM_S6A_SUBDATA_MSISDN); + } + } + + /* AVP: 'A-MSISDN'(1643) + * The A-MSISDN AVP contains an A-MSISDN, in international number + * format as described in ITU-T Rec E.164, encoded as a TBCD-string. + * This AVP shall not include leading indicators for the nature of + * address and the numbering plan; it shall contain only the + * TBCD-encoded digits of the address. + * Reference: 3GPP TS 29.272 7.3.157 + */ + ret = fd_avp_search_avp(avp, ogs_diam_s6a_a_msisdn, &avpch1); + ogs_assert(ret == 0); + if (avpch1) { + ret = fd_msg_avp_hdr(avpch1, &hdr); + ogs_assert(ret == 0); + if (hdr->avp_value->os.data && hdr->avp_value->os.len) { + mme_ue->a_msisdn_len = hdr->avp_value->os.len; + memcpy(mme_ue->a_msisdn, hdr->avp_value->os.data, + ogs_min(mme_ue->a_msisdn_len, OGS_MAX_MSISDN_LEN)); + ogs_buffer_to_bcd(mme_ue->a_msisdn, + mme_ue->a_msisdn_len, mme_ue->a_msisdn_bcd); + *subdatamask = (*subdatamask | OGS_DIAM_S6A_SUBDATA_A_MSISDN); + } + } + + /* AVP: 'Network-Access-Mode'(1417) + * The Network-Access-Mode AVP shall indicate one of three options + * through its value. + * (EPS-IMSI-COMBINED/RESERVED/EPS-ONLY) + * Reference: 3GPP TS 29.272 7.3.21 + */ + ret = fd_avp_search_avp(avp, ogs_diam_s6a_network_access_mode, &avpch1); + ogs_assert(ret == 0); + if (avpch1) { + ret = fd_msg_avp_hdr(avpch1, &hdr); + ogs_assert(ret == 0); + mme_ue->network_access_mode = hdr->avp_value->i32; + *subdatamask = (*subdatamask | OGS_DIAM_S6A_SUBDATA_NAM); + } + + /* AVP: '3GPP-Charging-Characteristics'(13) + * For GGSN, it contains the charging characteristics for + * this PDP Context received in the Create PDP Context + * Request Message (only available in R99 and later releases). + * For PGW, it contains the charging characteristics for the + * IP-CAN bearer. + * Reference: 3GPP TS 29.061 16.4.7.2 13 + */ + ret = fd_avp_search_avp(avp, ogs_diam_s6a_3gpp_charging_characteristics, + &avpch1); + ogs_assert(ret == 0); + if (avpch1) { + ret = fd_msg_avp_hdr(avpch1, &hdr); + memcpy(mme_ue->charging_characteristics, + OGS_HEX(hdr->avp_value->os.data, (int)hdr->avp_value->os.len, buf), + OGS_CHRGCHARS_LEN); + mme_ue->charging_characteristics_presence = true; + *subdatamask = (*subdatamask | OGS_DIAM_S6A_SUBDATA_CC); + } + + /* AVP: 'AMBR'(1435) + * The Amber AVP contains the Max-Requested-Bandwidth-UL and + * Max-Requested-Bandwidth-DL AVPs. + * Reference: 3GPP TS 29.272 7.3.41 + */ + ret = fd_avp_search_avp(avp, ogs_diam_s6a_ambr, &avpch1); + ogs_assert(ret == 0); + if (avpch1) { + /* AVP: 'Max-Requested-Bandwidth-UL'(516) + * The Max -Bandwidth-UL AVP indicates the maximum requested + * bandwidth in bits per second for an uplink IP flow. + * Reference: 3GPP TS 29.212 7.3.41 + */ + ret = fd_avp_search_avp(avpch1, + ogs_diam_s6a_max_bandwidth_ul, &avpch2); + ogs_assert(ret == 0); + if (avpch2) { + ret = fd_msg_avp_hdr(avpch2, &hdr); + ogs_assert(ret == 0); + subscription_data->ambr.uplink = hdr->avp_value->u32; + } else { + ogs_error("no_Max-Bandwidth-UL"); + error++; + } + + /* AVP: 'Max-Requested-Bandwidth-DL'(515) + * The Max-Requested-Bandwidth-DL AVP indicates the maximum + * bandwidth in bits per second for a downlink IP flow. + * Reference: 3GPP TS 29.212 7.3.41 + */ + ret = fd_avp_search_avp(avpch1, + ogs_diam_s6a_max_bandwidth_dl, &avpch2); + ogs_assert(ret == 0); + if (avpch2) { + ret = fd_msg_avp_hdr(avpch2, &hdr); + ogs_assert(ret == 0); + subscription_data->ambr.downlink = hdr->avp_value->u32; + } else { + ogs_error("no_Max-Bandwidth-DL"); + error++; + } + *subdatamask = (*subdatamask | OGS_DIAM_S6A_SUBDATA_UEAMBR); + } + + /* AVP: 'Subscribed-Periodic-RAU-TAU-Timer'(1619) + * The Subscribed-Periodic-TAU-RAU-Timer AVP contains the subscribed + * periodic TAU/RAU timer value in seconds. + * Reference: 3GPP TS 29.272 7.3.134 + */ + ret = fd_avp_search_avp(avp, + ogs_diam_s6a_subscribed_rau_tau_timer, &avpch1); + ogs_assert(ret == 0); + if (avpch1) { + ret = fd_msg_avp_hdr(avpch1, &hdr); + ogs_assert(ret == 0); + subscription_data->subscribed_rau_tau_timer = hdr->avp_value->i32; + *subdatamask = (*subdatamask | OGS_DIAM_S6A_SUBDATA_RAU_TAU_TIMER); + } + + /* AVP: 'APN-Configuration-Profile'(1429) + * The APN-Configuration-Profile AVP shall contain the information + * related to the user's subscribed APN configurations for EPS. The + * Context-Identifier AVP within it shall identify the per subscriber's + * default APN configuration. The Subscription-Data AVP associated + * with an IMSI contains one APN-Configuration-Profile AVP. Each + * APN-Configuration-Profile AVP contains one or more APN-Configuration + * AVPs. Each APN-Configuration AVP describes the configuration for a + * single APN. Therefore, the cardinality of the relationship between + * IMSI and APN is one-to-many. + * Reference: 3GPP TS 29.272 7.3.34 + */ + ret = fd_avp_search_avp(avp, + ogs_diam_s6a_apn_configuration_profile, &avpch1); + ogs_assert(ret == 0); + if (avpch1) { + ogs_slice_data_t *slice_data = NULL; + + ret = fd_msg_browse(avpch1, MSG_BRW_FIRST_CHILD, &avpch2, NULL); + ogs_assert(ret == 0); + + ogs_assert(subscription_data->num_of_slice == 0); + slice_data = &subscription_data->slice[0]; + while (avpch2) { + ret = fd_msg_avp_hdr(avpch2, &hdr); + ogs_assert(ret == 0); + switch(hdr->avp_code) { + + /* AVP: 'Context-Identifier'(1423) + * The Context-Identifier in the APN-Configuration AVP shall + * identify that APN configuration, and it shall not have a + * value of zero. Furthermore, the Context-Identifier in the + * APN-Configuration AVP shall uniquely identify the EPS APN + * configuration per subscription. + * Reference: 3GPP TS 29.272 7.3.35 + */ + case OGS_DIAM_S6A_AVP_CODE_CONTEXT_IDENTIFIER: + slice_data->context_identifier = hdr->avp_value->i32; + break; + + /* AVP: 'All-APN-Configurations-Included-Indicator'(1428) + * Reference: 3GPP TS 29.272 7.3.33 + */ + case OGS_DIAM_S6A_AVP_CODE_ALL_APN_CONFIG_INC_IND: + slice_data->all_apn_config_inc = hdr->avp_value->i32; + break; + + /* AVP: 'APN-Configuration'(1430) + * The APN-Configuration AVP contains the information + * related to the user's subscribed APN configurations. + * Reference: 3GPP TS 29.272 7.3.35 + */ + case OGS_DIAM_S6A_AVP_CODE_APN_CONFIGURATION: + { + ogs_session_t *session = NULL; + + if (slice_data->num_of_session >= OGS_MAX_NUM_OF_SESS) { + ogs_warn("Ignore max session count overflow [%d>=%d]", + slice_data->num_of_session, OGS_MAX_NUM_OF_SESS); + break; + } + session = &slice_data->session[slice_data->num_of_session]; + ogs_assert(session); + + /* AVP: 'Service-Selection'(493) + * The Service-Selection AVP is of type of UTF8String. This + * AVP shall contain either the APN Network Identifier + * (i.e. an APN without the Operator Identifier) per 3GPP + * TS 23.003 [3], clauses 9.1 & 9.1.1, or this AVP shall + * contain the wild card value per 3GPP TS 23.003 [3], + * clause 9.2.1, and 3GPP TS 23.008 [30], clause 2.13.6). + * ((DNN/APN)) + * Reference: 3GPP TS 29.272 7.3.36 + */ + ret = fd_avp_search_avp( + avpch2, ogs_diam_service_selection, &avpch3); + ogs_assert(ret == 0); + if (avpch3) { + ret = fd_msg_avp_hdr(avpch3, &hdr); + session->name = ogs_strndup( + (char*)hdr->avp_value->os.data, + hdr->avp_value->os.len); + ogs_assert(session->name); + } else { + ogs_error("no_Service-Selection"); + error++; + } + + /* AVP: 'Context-Identifier'(1423) + * The Context-Identifier in the APN-Configuration AVP shall + * identify that APN configuration, and it shall not have a + * value of zero. Furthermore, the Context-Identifier in the + * APN-Configuration AVP shall uniquely identify the EPS APN + * configuration per subscription. + * Reference: 3GPP TS 29.272 7.3.27 + */ + ret = fd_avp_search_avp(avpch2, + ogs_diam_s6a_context_identifier, &avpch3); + ogs_assert(ret == 0); + if (avpch3) { + ret = fd_msg_avp_hdr(avpch3, &hdr); + session->context_identifier = hdr->avp_value->i32; + } else { + ogs_error("no_Context-Identifier"); + error++; + } + + /* AVP: 'PDN-Type'(1456) + * The PDN-Type AVP indicates the address type of PDN. + * ((IPv4/IPv6/IPv4v6)) + * Reference: 3GPP TS 29.272 7.3.62 + */ + ret = fd_avp_search_avp(avpch2, ogs_diam_s6a_pdn_type, + &avpch3); + ogs_assert(ret == 0); + if (avpch3) { + ret = fd_msg_avp_hdr(avpch3, &hdr); + session->session_type = + OGS_PDU_SESSION_TYPE_FROM_DIAMETER( + hdr->avp_value->i32); + } else { + ogs_error("no_PDN-Type"); + error++; + } + + /* AVP: '3GPP-Charging-Characteristics'(13) + * For GGSN, it contains the charging characteristics for + * this PDP Context received in the Create PDP Context + * Request Message (only available in R99 and later releases). + * For PGW, it contains the charging characteristics for the + * IP-CAN bearer. + * Reference: 3GPP TS 29.061 16.4.7.2 13 + */ + ret = fd_avp_search_avp(avpch2, + ogs_diam_s6a_3gpp_charging_characteristics, &avpch3); + ogs_assert(ret == 0); + if (avpch3) { + ret = fd_msg_avp_hdr(avpch3, &hdr); + memcpy(session->charging_characteristics, + OGS_HEX(hdr->avp_value->os.data, + (int)hdr->avp_value->os.len, buf), + OGS_CHRGCHARS_LEN); + session->charging_characteristics_presence = true; + } else { + memcpy(session->charging_characteristics, + (uint8_t *)"\x00\x00", OGS_CHRGCHARS_LEN); + session->charging_characteristics_presence = false; + } + + /* AVP: 'Served-Party-IP-Address'(848) + * The Served-Party-IP-Address AVP holds the IP address of + * either the calling or called party, depending on whether + * the P-CSCF is in touch with the calling or the called + * party. + * ((UE IP STATIC ADDRESS)) + * Reference: 32-299-f10 + */ + ret = fd_msg_browse(avpch2, MSG_BRW_FIRST_CHILD, + &avpch3, NULL); + ogs_assert(ret == 0); + while (avpch3) { + ret = fd_msg_avp_hdr(avpch3, &hdr); + ogs_assert(ret == 0); + switch(hdr->avp_code) { + case OGS_DIAM_S6A_AVP_CODE_SERVED_PARTY_IP_ADDRESS: + ret = fd_msg_avp_value_interpret(avpch3, &addr.sa); + ogs_assert(ret == 0); + + if (addr.ogs_sa_family == AF_INET) { + if (session->session_type == + OGS_PDU_SESSION_TYPE_IPV4) { + session->paa.addr = + addr.sin.sin_addr.s_addr; + } else if (session->session_type == + OGS_PDU_SESSION_TYPE_IPV4V6) { + session->paa.both.addr = + addr.sin.sin_addr.s_addr; + } else { + ogs_error("Warning: Received a static IPv4 " + "address but PDN-Type does not include " + "IPv4. Ignoring..."); + } + } else if (addr.ogs_sa_family == AF_INET6) { + if (session->session_type == + OGS_PDU_SESSION_TYPE_IPV6) { + memcpy(session->paa.addr6, + addr.sin6.sin6_addr.s6_addr, + OGS_IPV6_LEN); + } else if (session->session_type == + OGS_PDU_SESSION_TYPE_IPV4V6) { + memcpy(session->paa.both.addr6, + addr.sin6.sin6_addr.s6_addr, + OGS_IPV6_LEN); + } else { + ogs_error("Warning: Received a static IPv6 " + "address but PDN-Type does not include " + "IPv6. Ignoring..."); + } + } else { + ogs_error("Invalid family[%d]", + addr.ogs_sa_family); + } + break; + default: + break; + } + fd_msg_browse(avpch3, MSG_BRW_NEXT, &avpch3, NULL); + } + + /* AVP: 'EPS-Subscribed-QoS-Profile'(1431) + * The EPS-Subscribed-QoS-Profile AVP shall contain the + * bearer-level QoS parameters (QoS Class Identifier and + * Allocation Retention Priority) associated to the + * default bearer for an APN. + * Reference: 3GPP TS 29.272 7.3.37 + */ + ret = fd_avp_search_avp(avpch2, + ogs_diam_s6a_eps_subscribed_qos_profile, &avpch3); + ogs_assert(ret == 0); + if (avpch3) { + + /* AVP: 'QoS-Class-Identifier'(1028) + * The QoS-Class-Identifier AVP identifies a set of + * IP-CAN specific QoS parameters that define the + * authorized QoS, excluding the applicable bitrates + * and ARP for the IP-CAN bearer or service flow. + * Reference: 3GPP TS 29.212 7.3.37 + */ + ret = fd_avp_search_avp(avpch3, + ogs_diam_s6a_qos_class_identifier, &avpch4); + ogs_assert(ret == 0); + if (avpch4) { + ret = fd_msg_avp_hdr(avpch4, &hdr); + ogs_assert(ret == 0); + session->qos.index = hdr->avp_value->i32; + } else { + ogs_error("no_QoS-Class-Identifier"); + error++; + } + + /* AVP: 'Allocation-Retention-Priority'(1034) + * The Allocation-Retention-Priority AVP is used to + * indicate the priority of allocation and retention, + * the pre-emption capability and pre-emption + * vulnerability for the SDF if provided within the + * QoS-Information-AVP or for the EPS default bearer if + * provided within the Default-EPS-Bearer-QoS AVP. + * Reference: 3GPP TS 29.212 7.3.40 + */ + ret = fd_avp_search_avp(avpch3, + ogs_diam_s6a_allocation_retention_priority, + &avpch4); + ogs_assert(ret == 0); + if (avpch4) { + + /* AVP: 'Priority-Level'(1046) + * The Priority-Level AVP is used for deciding + * whether a bearer establishment or modification + * request can be accepted or needs to be rejected + * in case of resource limitations. + * Reference: 3GPP TS 29.212 7.3.40 + */ + ret = fd_avp_search_avp(avpch4, + ogs_diam_s6a_priority_level, &avpch5); + ogs_assert(ret == 0); + if (avpch5) { + ret = fd_msg_avp_hdr(avpch5, &hdr); + ogs_assert(ret == 0); + session->qos.arp.priority_level = + hdr->avp_value->i32; + } else { + ogs_error("no_ARP"); + error++; + } + + /* AVP: 'Pre-emption-Capability'(1047) + * The Pre-emption-Capability AVP defines whether a + * service data flow can get resources that were + * already assigned to another service data flow + * with a lower priority level. + * Reference: 3GPP TS 29.212 7.3.40 + */ + ret = fd_avp_search_avp(avpch4, + ogs_diam_s6a_pre_emption_capability, &avpch5); + ogs_assert(ret == 0); + if (avpch5) { + ret = fd_msg_avp_hdr(avpch5, &hdr); + ogs_assert(ret == 0); + session->qos.arp.pre_emption_capability = + hdr->avp_value->i32; + } else { + session->qos.arp.pre_emption_capability = + OGS_EPC_PRE_EMPTION_DISABLED; + } + + /* AVP: 'Pre-emption-Vulnerability'(1048) + * The Pre-emption-Vulnerability AVP defines whether + * a service data flow can lose the resources + * assigned to it in order to admit a service data + * flow with higher priority level. + * Reference: 3GPP TS 29.212 7.3.40 + */ + ret = fd_avp_search_avp(avpch4, + ogs_diam_s6a_pre_emption_vulnerability, + &avpch5); + ogs_assert(ret == 0); + if (avpch5) { + ret = fd_msg_avp_hdr(avpch5, &hdr); + ogs_assert(ret == 0); + session->qos.arp.pre_emption_vulnerability = + hdr->avp_value->i32; + } else { + session->qos.arp.pre_emption_vulnerability = + OGS_EPC_PRE_EMPTION_ENABLED; + } + + } else { + ogs_error("no_QCI"); + error++; + } + } else { + ogs_error("no_EPS-Subscribed-QoS-Profile"); + error++; + } + + /* AVP: 'MIP6-Agent-Info'(486) + * The MIP6-Agent-Info AVP contains necessary information + * to assign an HA to the MN. When the MIP6-Agent-Info AVP + * is present in a message, it MUST contain either the + * MIP-Home-Agent-Address AVP, the MIP-Home-Agent-Host AVP, + * or both AVPs. + * Reference: 3GPP TS 29.212 7.3.45 + */ + ret = fd_avp_search_avp(avpch2, + ogs_diam_mip6_agent_info, &avpch3); + ogs_assert(ret == 0); + if (avpch3) { + + /* AVP: 'MIP-Home-Agent-Address'(334) + * The MIP-Home-Agent-Host AVP contains the identity of + * the assigned MIPv6 HA. Both the Destination-Realm and + * the Destination-Host AVPs of the HA are included in + * the grouped AVP. The usage of the MIP-Home-Agent-Host + * AVP is equivalent to the MIP-Home-Agent-Address AVP + * but offers an additional level of indirection by + * using the DNS infrastructure. The Destination-Host + * AVP is used to identify an HA, and the Destination- + * Realm AVP is used to identify the realm where the HA + * is located. + * ((SMF IP STATIC ADDRESS)) + * Reference: 3GPP TS 29.212 7.3.42 + */ + ret = fd_msg_browse(avpch3, + MSG_BRW_FIRST_CHILD, &avpch4, NULL); + ogs_assert(ret == 0); + while (avpch4) { + ret = fd_msg_avp_hdr(avpch4, &hdr); + switch(hdr->avp_code) { + case OGS_DIAM_S6A_AVP_CODE_MIP_HOME_AGENT_ADDRESS: + ret = fd_msg_avp_value_interpret(avpch4, + &addr.sa); + ogs_assert(ret == 0); + if (addr.ogs_sa_family == AF_INET) + { + session->smf_ip.ipv4 = 1; + session->smf_ip.addr = + addr.sin.sin_addr.s_addr; + } + else if (addr.ogs_sa_family == AF_INET6) + { + session->smf_ip.ipv6 = 1; + memcpy(session->smf_ip.addr6, + addr.sin6.sin6_addr.s6_addr, + OGS_IPV6_LEN); + } + else + { + ogs_error("Invald family:%d", + addr.ogs_sa_family); + error++; + } + break; + default: + ogs_error("Unknown AVP-Code:%d", + hdr->avp_code); + error++; + break; + } + fd_msg_browse(avpch4, MSG_BRW_NEXT, + &avpch4, NULL); + } + } + + /* AVP: 'AMBR'(1435) + * The Amber AVP contains the Max-Requested-Bandwidth-UL + * and Max-Requested-Bandwidth-DL AVPs. + * Reference: 3GPP TS 29.272 7.3.41 + */ + ret = fd_avp_search_avp(avpch2, ogs_diam_s6a_ambr, &avpch3); + ogs_assert(ret == 0); + if (avpch3) { + + /* AVP: 'Max-Requested-Bandwidth-UL'(516) + * The Max -Bandwidth-UL AVP indicates the maximum + * requested bandwidth in bits per second for an uplink + * IP flow. + * Reference: 3GPP TS 29.214 7.3.41 + */ + ret = fd_avp_search_avp(avpch3, + ogs_diam_s6a_max_bandwidth_ul, &avpch4); + ogs_assert(ret == 0); + if (avpch4) { + ret = fd_msg_avp_hdr(avpch4, &hdr); + ogs_assert(ret == 0); + session->ambr.uplink = hdr->avp_value->u32; + } else { + ogs_error("no_Max-Bandwidth-UL"); + error++; + } + + /* AVP: 'Max-Requested-Bandwidth-DL'(515) + * The Max-Requested-Bandwidth-DL AVP indicates the + * maximum bandwidth in bits per second for a downlink + * IP flow. + * Reference: 3GPP TS 29.214 7.3.41 + */ + ret = fd_avp_search_avp(avpch3, + ogs_diam_s6a_max_bandwidth_dl, &avpch4); + ogs_assert(ret == 0); + if (avpch4) { + ret = fd_msg_avp_hdr(avpch4, &hdr); + ogs_assert(ret == 0); + session->ambr.downlink = hdr->avp_value->u32; + } else { + ogs_error("no_Max-Bandwidth-DL"); + error++; + } + } + + slice_data->num_of_session++; + break; + } + default: + ogs_warn("Unknown AVP-code:%d", hdr->avp_code); + break; + } + + fd_msg_browse(avpch2, MSG_BRW_NEXT, &avpch2, NULL); + } + + if (slice_data->num_of_session) + subscription_data->num_of_slice = 1; + *subdatamask = (*subdatamask | OGS_DIAM_S6A_SUBDATA_APN_CONFIG); + } + + return error; +} + /* MME Sends Authentication Information Request to HSS */ void mme_s6a_send_air(mme_ue_t *mme_ue, ogs_nas_authentication_failure_parameter_t @@ -599,18 +1220,14 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) { int ret; - char buf[OGS_CHRGCHARS_LEN]; - struct sess_state *sess_data = NULL; struct timespec ts; struct session *session; struct avp *avp, *avpch; - struct avp *avpch1, *avpch2, *avpch3, *avpch4, *avpch5; struct avp_hdr *hdr; unsigned long dur; int error = 0; int new; - ogs_sockaddr_t addr; mme_event_t *e = NULL; mme_ue_t *mme_ue = NULL; @@ -636,7 +1253,7 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) mme_ue = sess_data->mme_ue; ogs_assert(mme_ue); - /* Set Authentication-Information Command */ + /* Set Update-Location Command */ 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; @@ -740,603 +1357,29 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) ret = fd_msg_search_avp(*msg, ogs_diam_s6a_subscription_data, &avp); ogs_assert(ret == 0); if (avp) { + uint32_t subdatamask = 0; + ret = mme_s6a_subscription_data_from_avp(avp, subscription_data, mme_ue, + &subdatamask); - /* AVP: 'MSISDN'( 701 ) - * The MSISDN AVP is of type OctetString. This AVP contains an MSISDN, - * in international number format as described in ITU-T Rec E.164 [8], - * encoded as a TBCD-string, i.e. digits from 0 through 9 are encoded - * 0000 to 1001; 1111 is used as a filler when there is an odd number - * of digits; bits 8 to 5 of octet n encode digit 2n; bits 4 to 1 of - * octet n encode digit 2(n-1)+1. - * Reference: 3GPP TS 29.329 - */ - ret = fd_avp_search_avp(avp, ogs_diam_s6a_msisdn, &avpch1); - ogs_assert(ret == 0); - if (avpch1) { - ret = fd_msg_avp_hdr(avpch1, &hdr); - ogs_assert(ret == 0); - if (hdr->avp_value->os.data && hdr->avp_value->os.len) { - mme_ue->msisdn_len = hdr->avp_value->os.len; - memcpy(mme_ue->msisdn, hdr->avp_value->os.data, - ogs_min(mme_ue->msisdn_len, OGS_MAX_MSISDN_LEN)); - ogs_buffer_to_bcd(mme_ue->msisdn, - mme_ue->msisdn_len, mme_ue->msisdn_bcd); - } - } - - /* AVP: 'A-MSISDN'(1643) - * The A-MSISDN AVP contains an A-MSISDN, in international number - * format as described in ITU-T Rec E.164, encoded as a TBCD-string. - * This AVP shall not include leading indicators for the nature of - * address and the numbering plan; it shall contain only the - * TBCD-encoded digits of the address. - * Reference: 3GPP TS 29.272 7.3.157 - */ - ret = fd_avp_search_avp(avp, ogs_diam_s6a_a_msisdn, &avpch1); - ogs_assert(ret == 0); - if (avpch1) { - ret = fd_msg_avp_hdr(avpch1, &hdr); - ogs_assert(ret == 0); - if (hdr->avp_value->os.data && hdr->avp_value->os.len) { - mme_ue->a_msisdn_len = hdr->avp_value->os.len; - memcpy(mme_ue->a_msisdn, hdr->avp_value->os.data, - ogs_min(mme_ue->a_msisdn_len, OGS_MAX_MSISDN_LEN)); - ogs_buffer_to_bcd(mme_ue->a_msisdn, - mme_ue->a_msisdn_len, mme_ue->a_msisdn_bcd); - } - } - - /* AVP: 'Network-Access-Mode'(1417) - * The Network-Access-Mode AVP shall indicate one of three options - * through its value. - * (EPS-IMSI-COMBINED/RESERVED/EPS-ONLY) - * Reference: 3GPP TS 29.272 7.3.21 - */ - ret = fd_avp_search_avp(avp, ogs_diam_s6a_network_access_mode, &avpch1); - ogs_assert(ret == 0); - if (avpch1) { - ret = fd_msg_avp_hdr(avpch1, &hdr); - ogs_assert(ret == 0); - mme_ue->network_access_mode = hdr->avp_value->i32; - } else { + if (!(subdatamask & OGS_DIAM_S6A_SUBDATA_NAM)) { mme_ue->network_access_mode = 0; - ogs_warn("no subscribed Network-Access-Mode, defaulting to PACKET_AND_CIRCUIT (0)"); + ogs_warn("no subscribed Network-Access-Mode, defaulting to " + "PACKET_AND_CIRCUIT (0)"); } - - /* AVP: '3GPP-Charging-Characteristics'(13) - * For GGSN, it contains the charging characteristics for - * this PDP Context received in the Create PDP Context - * Request Message (only available in R99 and later releases). - * For PGW, it contains the charging characteristics for the - * IP-CAN bearer. - * Reference: 3GPP TS 29.061 16.4.7.2 13 - */ - ret = fd_avp_search_avp(avp, ogs_diam_s6a_3gpp_charging_characteristics, &avpch1); - ogs_assert(ret == 0); - if (avpch1) { - ret = fd_msg_avp_hdr(avpch1, &hdr); - memcpy(mme_ue->charging_characteristics, - OGS_HEX(hdr->avp_value->os.data, (int)hdr->avp_value->os.len, buf), OGS_CHRGCHARS_LEN); - mme_ue->charging_characteristics_presence = true; - } else { - memcpy(mme_ue->charging_characteristics, (uint8_t *)"\x00\x00", OGS_CHRGCHARS_LEN); + if (!(subdatamask & OGS_DIAM_S6A_SUBDATA_CC)) { + memcpy(mme_ue->charging_characteristics, (uint8_t *)"\x00\x00", + OGS_CHRGCHARS_LEN); mme_ue->charging_characteristics_presence = false; - } - - /* AVP: 'AMBR'(1435) - * The Amber AVP contains the Max-Requested-Bandwidth-UL and - * Max-Requested-Bandwidth-DL AVPs. - * Reference: 3GPP TS 29.272 7.3.41 - */ - ret = fd_avp_search_avp(avp, ogs_diam_s6a_ambr, &avpch1); - ogs_assert(ret == 0); - if (avpch1) { - - /* AVP: 'Max-Requested-Bandwidth-UL'(516) - * The Max -Bandwidth-UL AVP indicates the maximum requested - * bandwidth in bits per second for an uplink IP flow. - * Reference: 3GPP TS 29.212 7.3.41 - */ - ret = fd_avp_search_avp(avpch1, - ogs_diam_s6a_max_bandwidth_ul, &avpch2); - ogs_assert(ret == 0); - if (avpch2) { - ret = fd_msg_avp_hdr(avpch2, &hdr); - ogs_assert(ret == 0); - subscription_data->ambr.uplink = hdr->avp_value->u32; - } else { - ogs_error("no_Max-Bandwidth-UL"); - error++; - } - - /* AVP: 'Max-Requested-Bandwidth-DL'(515) - * The Max-Requested-Bandwidth-DL AVP indicates the maximum - * bandwidth in bits per second for a downlink IP flow. - * Reference: 3GPP TS 29.212 7.3.41 - */ - ret = fd_avp_search_avp(avpch1, - ogs_diam_s6a_max_bandwidth_dl, &avpch2); - ogs_assert(ret == 0); - if (avpch2) { - ret = fd_msg_avp_hdr(avpch2, &hdr); - ogs_assert(ret == 0); - subscription_data->ambr.downlink = hdr->avp_value->u32; - } else { - ogs_error("no_Max-Bandwidth-DL"); - error++; - } - - } else { + } + if (!(subdatamask & OGS_DIAM_S6A_SUBDATA_UEAMBR)) { ogs_error("no_AMBR"); error++; } - - /* AVP: 'Subscribed-Periodic-RAU-TAU-Timer'(1619) - * The Subscribed-Periodic-TAU-RAU-Timer AVP contains the subscribed - * periodic TAU/RAU timer value in seconds. - * Reference: 3GPP TS 29.272 7.3.134 - */ - ret = fd_avp_search_avp(avp, - ogs_diam_s6a_subscribed_rau_tau_timer, &avpch1); - ogs_assert(ret == 0); - if (avpch1) { - ret = fd_msg_avp_hdr(avpch1, &hdr); - ogs_assert(ret == 0); - subscription_data->subscribed_rau_tau_timer = hdr->avp_value->i32; - } else { + if (!(subdatamask & OGS_DIAM_S6A_SUBDATA_RAU_TAU_TIMER)) { subscription_data->subscribed_rau_tau_timer = OGS_RAU_TAU_DEFAULT_TIME; } - - /* AVP: 'APN-Configuration-Profile'(1429) - * The APN-Configuration-Profile AVP shall contain the information - * related to the user's subscribed APN configurations for EPS. The - * Context-Identifier AVP within it shall identify the per subscriber's - * default APN configuration. The Subscription-Data AVP associated - * with an IMSI contains one APN-Configuration-Profile AVP. Each - * APN-Configuration-Profile AVP contains one or more APN-Configuration - * AVPs. Each APN-Configuration AVP describes the configuration for a - * single APN. Therefore, the cardinality of the relationship between - * IMSI and APN is one-to-many. - * Reference: 3GPP TS 29.272 7.3.34 - */ - ret = fd_avp_search_avp(avp, - ogs_diam_s6a_apn_configuration_profile, &avpch1); - ogs_assert(ret == 0); - if (avpch1) { - ogs_slice_data_t *slice_data = NULL; - - ret = fd_msg_browse(avpch1, MSG_BRW_FIRST_CHILD, &avpch2, NULL); - ogs_assert(ret == 0); - - ogs_assert(subscription_data->num_of_slice == 0); - slice_data = &subscription_data->slice[0]; - while (avpch2) { - ret = fd_msg_avp_hdr(avpch2, &hdr); - ogs_assert(ret == 0); - switch(hdr->avp_code) { - - /* AVP: 'Context-Identifier'(1423) - * The Context-Identifier in the APN-Configuration AVP shall - * identify that APN configuration, and it shall not have a - * value of zero. Furthermore, the Context-Identifier in the - * APN-Configuration AVP shall uniquely identify the EPS APN - * configuration per subscription. - * Reference: 3GPP TS 29.272 7.3.35 - */ - case OGS_DIAM_S6A_AVP_CODE_CONTEXT_IDENTIFIER: - slice_data->context_identifier = hdr->avp_value->i32; - break; - - /* AVP: 'All-APN-Configurations-Included-Indicator'(1428) - * Reference: 3GPP TS 29.272 7.3.33 - */ - case OGS_DIAM_S6A_AVP_CODE_ALL_APN_CONFIG_INC_IND: - break; - - /* AVP: 'APN-Configuration'(1430) - * The APN-Configuration AVP contains the information - * related to the user's subscribed APN configurations. - * Reference: 3GPP TS 29.272 7.3.35 - */ - case OGS_DIAM_S6A_AVP_CODE_APN_CONFIGURATION: - { - ogs_session_t *session = NULL; - - if (slice_data->num_of_session >= OGS_MAX_NUM_OF_SESS) { - ogs_warn("Ignore max session count overflow [%d>=%d]", - slice_data->num_of_session, OGS_MAX_NUM_OF_SESS); - break; - } - session = &slice_data->session[slice_data->num_of_session]; - ogs_assert(session); - - /* AVP: 'Service-Selection'(493) - * The Service-Selection AVP is of type of UTF8String. This - * AVP shall contain either the APN Network Identifier - * (i.e. an APN without the Operator Identifier) per 3GPP - * TS 23.003 [3], clauses 9.1 & 9.1.1, or this AVP shall - * contain the wild card value per 3GPP TS 23.003 [3], - * clause 9.2.1, and 3GPP TS 23.008 [30], clause 2.13.6). - * ((DNN/APN)) - * Reference: 3GPP TS 29.272 7.3.36 - */ - ret = fd_avp_search_avp( - avpch2, ogs_diam_service_selection, &avpch3); - ogs_assert(ret == 0); - if (avpch3) { - ret = fd_msg_avp_hdr(avpch3, &hdr); - session->name = ogs_strndup( - (char*)hdr->avp_value->os.data, - hdr->avp_value->os.len); - ogs_assert(session->name); - } else { - ogs_error("no_Service-Selection"); - error++; - } - - /* AVP: 'Context-Identifier'(1423) - * The Context-Identifier in the APN-Configuration AVP shall - * identify that APN configuration, and it shall not have a - * value of zero. Furthermore, the Context-Identifier in the - * APN-Configuration AVP shall uniquely identify the EPS APN - * configuration per subscription. - * Reference: 3GPP TS 29.272 7.3.27 - */ - ret = fd_avp_search_avp(avpch2, - ogs_diam_s6a_context_identifier, &avpch3); - ogs_assert(ret == 0); - if (avpch3) { - ret = fd_msg_avp_hdr(avpch3, &hdr); - session->context_identifier = hdr->avp_value->i32; - } else { - ogs_error("no_Context-Identifier"); - error++; - } - - /* AVP: 'PDN-Type'(1456) - * The PDN-Type AVP indicates the address type of PDN. - * ((IPv4/IPv6/IPv4v6)) - * Reference: 3GPP TS 29.272 7.3.62 - */ - ret = fd_avp_search_avp(avpch2, ogs_diam_s6a_pdn_type, - &avpch3); - ogs_assert(ret == 0); - if (avpch3) { - ret = fd_msg_avp_hdr(avpch3, &hdr); - session->session_type = - OGS_PDU_SESSION_TYPE_FROM_DIAMETER( - hdr->avp_value->i32); - } else { - ogs_error("no_PDN-Type"); - error++; - } - - /* AVP: '3GPP-Charging-Characteristics'(13) - * For GGSN, it contains the charging characteristics for - * this PDP Context received in the Create PDP Context - * Request Message (only available in R99 and later releases). - * For PGW, it contains the charging characteristics for the - * IP-CAN bearer. - * Reference: 3GPP TS 29.061 16.4.7.2 13 - */ - ret = fd_avp_search_avp(avpch2, ogs_diam_s6a_3gpp_charging_characteristics, - &avpch3); - ogs_assert(ret == 0); - if (avpch3) { - ret = fd_msg_avp_hdr(avpch3, &hdr); - memcpy(session->charging_characteristics, - OGS_HEX(hdr->avp_value->os.data, (int)hdr->avp_value->os.len, buf), OGS_CHRGCHARS_LEN); - session->charging_characteristics_presence = true; - } else { - memcpy(session->charging_characteristics, (uint8_t *)"\x00\x00", OGS_CHRGCHARS_LEN); - session->charging_characteristics_presence = false; - } - - /* AVP: 'Served-Party-IP-Address'(848) - * The Served-Party-IP-Address AVP holds the IP address of - * either the calling or called party, depending on whether - * the P-CSCF is in touch with the calling or the called - * party. - * ((UE IP STATIC ADDRESS)) - * Reference: 32-299-f10 - */ - ret = fd_msg_browse(avpch2, MSG_BRW_FIRST_CHILD, - &avpch3, NULL); - ogs_assert(ret == 0); - while (avpch3) { - ret = fd_msg_avp_hdr(avpch3, &hdr); - ogs_assert(ret == 0); - switch(hdr->avp_code) { - case OGS_DIAM_S6A_AVP_CODE_SERVED_PARTY_IP_ADDRESS: - ret = fd_msg_avp_value_interpret(avpch3, &addr.sa); - ogs_assert(ret == 0); - - if (addr.ogs_sa_family == AF_INET) { - if (session->session_type == - OGS_PDU_SESSION_TYPE_IPV4) { - session->paa.addr = - addr.sin.sin_addr.s_addr; - } else if (session->session_type == - OGS_PDU_SESSION_TYPE_IPV4V6) { - session->paa.both.addr = - addr.sin.sin_addr.s_addr; - } else { - ogs_error("Warning: Received a static IPv4 " - "address but PDN-Type does not include " - "IPv4. Ignoring..."); - } - } else if (addr.ogs_sa_family == AF_INET6) { - if (session->session_type == - OGS_PDU_SESSION_TYPE_IPV6) { - memcpy(session->paa.addr6, - addr.sin6.sin6_addr.s6_addr, - OGS_IPV6_LEN); - } else if (session->session_type == - OGS_PDU_SESSION_TYPE_IPV4V6) { - memcpy(session->paa.both.addr6, - addr.sin6.sin6_addr.s6_addr, - OGS_IPV6_LEN); - } else { - ogs_error("Warning: Received a static IPv6 " - "address but PDN-Type does not include " - "IPv6. Ignoring..."); - } - } else { - ogs_error("Invalid family[%d]", - addr.ogs_sa_family); - } - break; - default: - break; - } - fd_msg_browse(avpch3, MSG_BRW_NEXT, &avpch3, NULL); - } - - /* AVP: 'EPS-Subscribed-QoS-Profile'(1431) - * The EPS-Subscribed-QoS-Profile AVP shall contain the - * bearer-level QoS parameters (QoS Class Identifier and - * Allocation Retention Priority) associated to the - * default bearer for an APN. - * Reference: 3GPP TS 29.272 7.3.37 - */ - ret = fd_avp_search_avp(avpch2, - ogs_diam_s6a_eps_subscribed_qos_profile, &avpch3); - ogs_assert(ret == 0); - if (avpch3) { - - /* AVP: 'QoS-Class-Identifier'(1028) - * The QoS-Class-Identifier AVP identifies a set of - * IP-CAN specific QoS parameters that define the - * authorized QoS, excluding the applicable bitrates - * and ARP for the IP-CAN bearer or service flow. - * Reference: 3GPP TS 29.212 7.3.37 - */ - ret = fd_avp_search_avp(avpch3, - ogs_diam_s6a_qos_class_identifier, &avpch4); - ogs_assert(ret == 0); - if (avpch4) { - ret = fd_msg_avp_hdr(avpch4, &hdr); - ogs_assert(ret == 0); - session->qos.index = hdr->avp_value->i32; - } else { - ogs_error("no_QoS-Class-Identifier"); - error++; - } - - /* AVP: 'Allocation-Retention-Priority'(1034) - * The Allocation-Retention-Priority AVP is used to - * indicate the priority of allocation and retention, - * the pre-emption capability and pre-emption - * vulnerability for the SDF if provided within the - * QoS-Information-AVP or for the EPS default bearer if - * provided within the Default-EPS-Bearer-QoS AVP. - * Reference: 3GPP TS 29.212 7.3.40 - */ - ret = fd_avp_search_avp(avpch3, - ogs_diam_s6a_allocation_retention_priority, - &avpch4); - ogs_assert(ret == 0); - if (avpch4) { - - /* AVP: 'Priority-Level'(1046) - * The Priority-Level AVP is used for deciding - * whether a bearer establishment or modification - * request can be accepted or needs to be rejected - * in case of resource limitations. - * Reference: 3GPP TS 29.212 7.3.40 - */ - ret = fd_avp_search_avp(avpch4, - ogs_diam_s6a_priority_level, &avpch5); - ogs_assert(ret == 0); - if (avpch5) { - ret = fd_msg_avp_hdr(avpch5, &hdr); - ogs_assert(ret == 0); - session->qos.arp.priority_level = - hdr->avp_value->i32; - } else { - ogs_error("no_ARP"); - error++; - } - - /* AVP: 'Pre-emption-Capability'(1047) - * The Pre-emption-Capability AVP defines whether a - * service data flow can get resources that were - * already assigned to another service data flow - * with a lower priority level. - * Reference: 3GPP TS 29.212 7.3.40 - */ - ret = fd_avp_search_avp(avpch4, - ogs_diam_s6a_pre_emption_capability, &avpch5); - ogs_assert(ret == 0); - if (avpch5) { - ret = fd_msg_avp_hdr(avpch5, &hdr); - ogs_assert(ret == 0); - session->qos.arp.pre_emption_capability = - hdr->avp_value->i32; - } else { - session->qos.arp.pre_emption_capability = - OGS_EPC_PRE_EMPTION_DISABLED; - } - - /* AVP: 'Pre-emption-Vulnerability'(1048) - * The Pre-emption-Vulnerability AVP defines whether - * a service data flow can lose the resources - * assigned to it in order to admit a service data - * flow with higher priority level. - * Reference: 3GPP TS 29.212 7.3.40 - */ - ret = fd_avp_search_avp(avpch4, - ogs_diam_s6a_pre_emption_vulnerability, - &avpch5); - ogs_assert(ret == 0); - if (avpch5) { - ret = fd_msg_avp_hdr(avpch5, &hdr); - ogs_assert(ret == 0); - session->qos.arp.pre_emption_vulnerability = - hdr->avp_value->i32; - } else { - session->qos.arp.pre_emption_vulnerability = - OGS_EPC_PRE_EMPTION_ENABLED; - } - - } else { - ogs_error("no_QCI"); - error++; - } - } else { - ogs_error("no_EPS-Subscribed-QoS-Profile"); - error++; - } - - /* AVP: 'MIP6-Agent-Info'(486) - * The MIP6-Agent-Info AVP contains necessary information - * to assign an HA to the MN. When the MIP6-Agent-Info AVP - * is present in a message, it MUST contain either the - * MIP-Home-Agent-Address AVP, the MIP-Home-Agent-Host AVP, - * or both AVPs. - * Reference: 3GPP TS 29.212 7.3.45 - */ - ret = fd_avp_search_avp(avpch2, - ogs_diam_mip6_agent_info, &avpch3); - ogs_assert(ret == 0); - if (avpch3) { - - /* AVP: 'MIP-Home-Agent-Address'(334) - * The MIP-Home-Agent-Host AVP contains the identity of - * the assigned MIPv6 HA. Both the Destination-Realm and - * the Destination-Host AVPs of the HA are included in - * the grouped AVP. The usage of the MIP-Home-Agent-Host - * AVP is equivalent to the MIP-Home-Agent-Address AVP - * but offers an additional level of indirection by - * using the DNS infrastructure. The Destination-Host - * AVP is used to identify an HA, and the Destination- - * Realm AVP is used to identify the realm where the HA - * is located. - * ((SMF IP STATIC ADDRESS)) - * Reference: 3GPP TS 29.212 7.3.42 - */ - ret = fd_msg_browse(avpch3, - MSG_BRW_FIRST_CHILD, &avpch4, NULL); - ogs_assert(ret == 0); - while (avpch4) { - ret = fd_msg_avp_hdr(avpch4, &hdr); - switch(hdr->avp_code) { - case OGS_DIAM_S6A_AVP_CODE_MIP_HOME_AGENT_ADDRESS: - ret = fd_msg_avp_value_interpret(avpch4, - &addr.sa); - ogs_assert(ret == 0); - if (addr.ogs_sa_family == AF_INET) - { - session->smf_ip.ipv4 = 1; - session->smf_ip.addr = - addr.sin.sin_addr.s_addr; - } - else if (addr.ogs_sa_family == AF_INET6) - { - session->smf_ip.ipv6 = 1; - memcpy(session->smf_ip.addr6, - addr.sin6.sin6_addr.s6_addr, - OGS_IPV6_LEN); - } - else - { - ogs_error("Invald family:%d", - addr.ogs_sa_family); - error++; - } - break; - default: - ogs_error("Unknown AVP-Code:%d", - hdr->avp_code); - error++; - break; - } - fd_msg_browse(avpch4, MSG_BRW_NEXT, - &avpch4, NULL); - } - } - - /* AVP: 'AMBR'(1435) - * The Amber AVP contains the Max-Requested-Bandwidth-UL - * and Max-Requested-Bandwidth-DL AVPs. - * Reference: 3GPP TS 29.272 7.3.41 - */ - ret = fd_avp_search_avp(avpch2, ogs_diam_s6a_ambr, &avpch3); - ogs_assert(ret == 0); - if (avpch3) { - - /* AVP: 'Max-Requested-Bandwidth-UL'(516) - * The Max -Bandwidth-UL AVP indicates the maximum - * requested bandwidth in bits per second for an uplink - * IP flow. - * Reference: 3GPP TS 29.214 7.3.41 - */ - ret = fd_avp_search_avp(avpch3, - ogs_diam_s6a_max_bandwidth_ul, &avpch4); - ogs_assert(ret == 0); - if (avpch4) { - ret = fd_msg_avp_hdr(avpch4, &hdr); - ogs_assert(ret == 0); - session->ambr.uplink = hdr->avp_value->u32; - } else { - ogs_error("no_Max-Bandwidth-UL"); - error++; - } - - /* AVP: 'Max-Requested-Bandwidth-DL'(515) - * The Max-Requested-Bandwidth-DL AVP indicates the - * maximum bandwidth in bits per second for a downlink - * IP flow. - * Reference: 3GPP TS 29.214 7.3.41 - */ - ret = fd_avp_search_avp(avpch3, - ogs_diam_s6a_max_bandwidth_dl, &avpch4); - ogs_assert(ret == 0); - if (avpch4) { - ret = fd_msg_avp_hdr(avpch4, &hdr); - ogs_assert(ret == 0); - session->ambr.downlink = hdr->avp_value->u32; - } else { - ogs_error("no_Max-Bandwidth-DL"); - error++; - } - } - - slice_data->num_of_session++; - break; - } - default: - ogs_warn("Unknown AVP-code:%d", hdr->avp_code); - break; - } - - fd_msg_browse(avpch2, MSG_BRW_NEXT, &avpch2, NULL); - } - - if (slice_data->num_of_session) - subscription_data->num_of_slice = 1; - } else { + if (!(subdatamask & OGS_DIAM_S6A_SUBDATA_APN_CONFIG)) { ogs_error("no_APN-Configuration-Profile"); error++; } @@ -1551,23 +1594,23 @@ static int mme_ogs_diam_s6a_idr_cb( struct msg **msg, struct avp *avp, struct session *session, void *opaque, enum disp_action *act) { int ret; + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + uint32_t result_code = 0; + bool has_subscriber_data; - mme_ue_t *mme_ue = NULL; - struct msg *ans, *qry; - ogs_diam_s6a_idr_message_t *idr_message = NULL; + + mme_event_t *e = NULL; + mme_ue_t *mme_ue = NULL; + ogs_diam_s6a_message_t *s6a_message = NULL; + ogs_diam_s6a_idr_message_t *idr_message = NULL; + ogs_subscription_data_t *subscription_data = 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("Insert-Subscriber-Data-Request"); s6a_message = ogs_calloc(1, sizeof(ogs_diam_s6a_message_t)); @@ -1575,6 +1618,8 @@ static int mme_ogs_diam_s6a_idr_cb( struct msg **msg, struct avp *avp, s6a_message->cmd_code = OGS_DIAM_S6A_CMD_CODE_INSERT_SUBSCRIBER_DATA; idr_message = &s6a_message->idr_message; ogs_assert(idr_message); + subscription_data = &idr_message->subscription_data; + ogs_assert(subscription_data); /* Create answer header */ qry = *msg; @@ -1603,15 +1648,21 @@ static int mme_ogs_diam_s6a_idr_cb( struct msg **msg, struct avp *avp, * profile relevant for EPS and GERAN/UTRAN. * Reference: 3GPP TS 29.272-f70 */ - ret = fd_msg_search_avp(*msg, ogs_diam_s6a_subscription_data, &avp); + ret = fd_msg_search_avp(qry, ogs_diam_s6a_subscription_data, &avp); ogs_assert(ret == 0); if (avp) { ret = fd_msg_avp_hdr(avp, &hdr); ogs_assert(ret == 0); - if (hdr->avp_value->os.len) { - ogs_debug("WIP: Process New Subscription Data"); + ret = fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, NULL, NULL); + if (ret) { + ogs_info("[%s] Subscription-Data is Empty.", imsi_bcd); } else { - ogs_debug("No Sub Data, ok to check IDR Flags"); + has_subscriber_data = true; + uint32_t subdatamask = 0; + ret = mme_s6a_subscription_data_from_avp(avp, subscription_data, + mme_ue, &subdatamask); + idr_message->subdatamask = subdatamask; + ogs_info("[%s] Subscription-Data Processed.", imsi_bcd); } } @@ -1621,14 +1672,6 @@ static int mme_ogs_diam_s6a_idr_cb( struct msg **msg, struct avp *avp, ret = fd_msg_avp_hdr(avp, &hdr); ogs_assert(ret == 0); idr_message->idr_flags = hdr->avp_value->i32; - } else { - ogs_error("Insert Subscriber Data does not contain any IDR Flags " - "for IMSI[%s]", imsi_bcd); - /* Set the Origin-Host, Origin-Realm, and Result-Code AVPs */ - ret = fd_msg_rescode_set(ans, - (char*)"DIAMETER_UNABLE_TO_COMPLY", NULL, NULL, 1); - ogs_assert(ret == 0); - goto outnoexp; } if (idr_message->idr_flags & OGS_DIAM_S6A_IDR_FLAGS_EPS_LOCATION_INFO) { @@ -1711,13 +1754,16 @@ static int mme_ogs_diam_s6a_idr_cb( struct msg **msg, struct avp *avp, ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); ogs_assert(ret == 0); } else { - ogs_error("Insert Subscriber Data " - "with unsupported IDR Flags for IMSI[%s]", imsi_bcd); - /* Set the Origin-Host, Origin-Realm, and Result-Code AVPs */ - ret = fd_msg_rescode_set( - ans, (char*)"DIAMETER_UNABLE_TO_COMPLY", NULL, NULL, 1); - ogs_assert(ret == 0); - goto outnoexp; + if (!has_subscriber_data) { + ogs_error("Insert Subscriber Data " + "with unsupported IDR Flags " + "or no Subscriber-Data for IMSI[%s]", imsi_bcd); + /* Set the Origin-Host, Origin-Realm, and Result-Code AVPs */ + ret = fd_msg_rescode_set( + ans, (char*)"DIAMETER_UNABLE_TO_COMPLY", NULL, NULL, 1); + ogs_assert(ret == 0); + goto outnoexp; + } } /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ @@ -1749,6 +1795,21 @@ static int mme_ogs_diam_s6a_idr_cb( struct msg **msg, struct avp *avp, ogs_diam_logger_self()->stats.nb_echoed++; ogs_assert( pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + int rv; + e = mme_event_new(MME_EVENT_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_subscription_data_free(subscription_data); + ogs_free(s6a_message); + mme_event_free(e); + } else { + ogs_pollset_notify(ogs_app()->pollset); + } + return 0; out: @@ -1771,7 +1832,9 @@ outnoexp: /* Send the answer */ ret = fd_msg_send(msg, NULL, NULL); - ogs_assert(ret == 0); + ogs_assert(ret == 0); + + ogs_free(s6a_message); return 0; } diff --git a/src/mme/mme-s6a-handler.c b/src/mme/mme-s6a-handler.c index b4199f945..aabfb0dc0 100644 --- a/src/mme/mme-s6a-handler.c +++ b/src/mme/mme-s6a-handler.c @@ -33,6 +33,9 @@ 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, + ogs_slice_data_t *slice_data); + uint8_t mme_s6a_handle_aia( mme_ue_t *mme_ue, ogs_diam_s6a_message_t *s6a_message) { @@ -74,7 +77,7 @@ uint8_t mme_s6a_handle_ula( ogs_diam_s6a_ula_message_t *ula_message = NULL; ogs_subscription_data_t *subscription_data = NULL; ogs_slice_data_t *slice_data = NULL; - int i, rv; + int rv; ogs_assert(mme_ue); ogs_assert(s6a_message); @@ -95,58 +98,13 @@ uint8_t mme_s6a_handle_ula( mme_session_remove_all(mme_ue); - for (i = 0; i < slice_data->num_of_session; i++) { - if (i >= OGS_MAX_NUM_OF_SESS) { - ogs_warn("Ignore max session count overflow [%d>=%d]", - slice_data->num_of_session, OGS_MAX_NUM_OF_SESS); - break; - } - - if (slice_data->session[i].name) { - mme_ue->session[i].name = ogs_strdup(slice_data->session[i].name); - ogs_assert(mme_ue->session[i].name); - } - - mme_ue->session[i].context_identifier = - slice_data->session[i].context_identifier; - - if (slice_data->session[i].session_type == OGS_PDU_SESSION_TYPE_IPV4 || - slice_data->session[i].session_type == OGS_PDU_SESSION_TYPE_IPV6 || - slice_data->session[i].session_type == - OGS_PDU_SESSION_TYPE_IPV4V6) { - mme_ue->session[i].session_type = - slice_data->session[i].session_type; - } else { - ogs_error("Invalid PDN_TYPE[%d]", - slice_data->session[i].session_type); - if (mme_ue->session[i].name) - ogs_free(mme_ue->session[i].name); - break; - } - memcpy(&mme_ue->session[i].paa, &slice_data->session[i].paa, - sizeof(mme_ue->session[i].paa)); - - memcpy(&mme_ue->session[i].qos, &slice_data->session[i].qos, - sizeof(mme_ue->session[i].qos)); - memcpy(&mme_ue->session[i].ambr, &slice_data->session[i].ambr, - sizeof(mme_ue->session[i].ambr)); - - memcpy(&mme_ue->session[i].smf_ip, &slice_data->session[i].smf_ip, - sizeof(mme_ue->session[i].smf_ip)); - - memcpy(&mme_ue->session[i].charging_characteristics, - &slice_data->session[i].charging_characteristics, - sizeof(mme_ue->session[i].charging_characteristics)); - mme_ue->session[i].charging_characteristics_presence = - slice_data->session[i].charging_characteristics_presence; - } - - if (i == 0) { + rv = mme_ue_session_from_slice_data(mme_ue, slice_data); + if (rv == 0) { ogs_error("No Session"); return OGS_NAS_EMM_CAUSE_SEVERE_NETWORK_FAILURE; } + mme_ue->num_of_session = rv; - mme_ue->num_of_session = i; mme_ue->context_identifier = slice_data->context_identifier; if (mme_ue->nas_eps.type == MME_EPS_TYPE_ATTACH_REQUEST) { @@ -168,6 +126,48 @@ uint8_t mme_s6a_handle_ula( return OGS_NAS_EMM_CAUSE_REQUEST_ACCEPTED; } +uint8_t mme_s6a_handle_idr( + mme_ue_t *mme_ue, ogs_diam_s6a_message_t *s6a_message) +{ + ogs_diam_s6a_idr_message_t *idr_message = NULL; + ogs_subscription_data_t *subscription_data = NULL; + ogs_slice_data_t *slice_data = NULL; + int rv; + + ogs_assert(mme_ue); + ogs_assert(s6a_message); + idr_message = &s6a_message->idr_message; + ogs_assert(idr_message); + subscription_data = &idr_message->subscription_data; + ogs_assert(subscription_data); + + if (idr_message->subdatamask & OGS_DIAM_S6A_SUBDATA_UEAMBR) { + memcpy(&mme_ue->ambr, &subscription_data->ambr, sizeof(ogs_bitrate_t)); + } + + if (idr_message->subdatamask & OGS_DIAM_S6A_SUBDATA_APN_CONFIG) { + ogs_assert(subscription_data->num_of_slice == 1); + slice_data = &subscription_data->slice[0]; + + if (!slice_data->all_apn_config_inc) { + mme_session_remove_all(mme_ue); + rv = mme_ue_session_from_slice_data(mme_ue, slice_data); + if (rv == 0) { + ogs_error("No Session"); + return OGS_ERROR; + } + mme_ue->num_of_session = rv; + } else { + ogs_error ("Partial APN-Configuration Not Supported in IDR."); + return OGS_ERROR; + } + + mme_ue->context_identifier = slice_data->context_identifier; + } + + return OGS_OK; +} + void mme_s6a_handle_clr( mme_ue_t *mme_ue, ogs_diam_s6a_clr_message_t *clr_message) { @@ -215,7 +215,60 @@ void mme_s6a_handle_clr( } } -/* 3GPP TS 29.272 Annex A; Table !.a: +static uint8_t mme_ue_session_from_slice_data(mme_ue_t *mme_ue, + ogs_slice_data_t *slice_data) +{ + int i; + for (i = 0; i < slice_data->num_of_session; i++) { + if (i >= OGS_MAX_NUM_OF_SESS) { + ogs_warn("Ignore max session count overflow [%d>=%d]", + slice_data->num_of_session, OGS_MAX_NUM_OF_SESS); + break; + } + + if (slice_data->session[i].name) { + mme_ue->session[i].name = ogs_strdup(slice_data->session[i].name); + ogs_assert(mme_ue->session[i].name); + } + + mme_ue->session[i].context_identifier = + slice_data->session[i].context_identifier; + + if (slice_data->session[i].session_type == OGS_PDU_SESSION_TYPE_IPV4 || + slice_data->session[i].session_type == OGS_PDU_SESSION_TYPE_IPV6 || + slice_data->session[i].session_type == + OGS_PDU_SESSION_TYPE_IPV4V6) { + mme_ue->session[i].session_type = + slice_data->session[i].session_type; + } else { + ogs_error("Invalid PDN_TYPE[%d]", + slice_data->session[i].session_type); + if (mme_ue->session[i].name) + ogs_free(mme_ue->session[i].name); + break; + } + memcpy(&mme_ue->session[i].paa, &slice_data->session[i].paa, + sizeof(mme_ue->session[i].paa)); + + memcpy(&mme_ue->session[i].qos, &slice_data->session[i].qos, + sizeof(mme_ue->session[i].qos)); + memcpy(&mme_ue->session[i].ambr, &slice_data->session[i].ambr, + sizeof(mme_ue->session[i].ambr)); + + memcpy(&mme_ue->session[i].smf_ip, &slice_data->session[i].smf_ip, + sizeof(mme_ue->session[i].smf_ip)); + + memcpy(&mme_ue->session[i].charging_characteristics, + &slice_data->session[i].charging_characteristics, + sizeof(mme_ue->session[i].charging_characteristics)); + mme_ue->session[i].charging_characteristics_presence = + slice_data->session[i].charging_characteristics_presence; + } + + return i; +} + +/* 3GPP TS 29.272 Annex A; Table A.1: * 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) diff --git a/src/mme/mme-s6a-handler.h b/src/mme/mme-s6a-handler.h index 5a0dc53db..33094ea8a 100644 --- a/src/mme/mme-s6a-handler.h +++ b/src/mme/mme-s6a-handler.h @@ -30,6 +30,8 @@ uint8_t mme_s6a_handle_aia( mme_ue_t *mme_ue, ogs_diam_s6a_message_t *s6a_message); uint8_t mme_s6a_handle_ula( mme_ue_t *mme_ue, ogs_diam_s6a_message_t *s6a_message); +uint8_t mme_s6a_handle_idr( + mme_ue_t *mme_ue, ogs_diam_s6a_message_t *s6a_message); void mme_s6a_handle_clr( mme_ue_t *mme_ue, ogs_diam_s6a_clr_message_t *clr_message); diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index cd93deefa..22aeca369 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -435,11 +435,13 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) mme_s6a_handle_clr(mme_ue, &s6a_message->clr_message); break; case OGS_DIAM_S6A_CMD_CODE_INSERT_SUBSCRIBER_DATA: + mme_s6a_handle_idr(mme_ue, s6a_message); break; default: ogs_error("Invalid Type[%d]", s6a_message->cmd_code); break; } + ogs_subscription_data_free(&s6a_message->idr_message.subscription_data); ogs_subscription_data_free(&s6a_message->ula_message.subscription_data); ogs_free(s6a_message); break;