From 775520d7a8302c42d514c83b47c25233911758da Mon Sep 17 00:00:00 2001 From: jmasterfunk84 <48972964+jmasterfunk84@users.noreply.github.com> Date: Tue, 13 Sep 2022 01:48:20 -0600 Subject: [PATCH] Introduce Cancel Location and Insert Subscriber Data features to HSS. (#1744) * Introduce Cancel Location and Insert Subscriber Data features to HSS. * HSS database will keep track of last known MME and Update Time * Purged UE flag is established in HSS for future PUR handling * HSS Thread will connect to database and watch change stream mongoDB must be configured with a Replica Set to use this * HSS will send IDR if subscription data changes * HSS will send CLR to old MME if MME host or realm changes * Function created to allow ULA and IDR to generate Subscription-Data AVP * MME Hostname and Realm shown in WebUI * Resolve freeDiameter errors During a ULR, if database does not contain a last known MME, a CLR is being sent to a Null destination. This will ensure that a destination is available in the database before sending the CLR. * Removed change streams. Added PUR handling. * newline needed at end of file. * Removed temp variable. * * Change WebUI to 2x2 display * Including UE Purged indicator * Using pointers in ogs_subscription_data_t * better memory mangement with pointers * Tweak to Destination used by hss_s6a_send_idr to use last known MME * Check for null mme_host and mme_realms Do this before trying to compare the strings. --- lib/dbi/subscription.c | 72 ++ lib/dbi/subscription.h | 2 + lib/diameter/s6a/message.c | 2 + lib/diameter/s6a/message.h | 10 + lib/proto/types.c | 7 + lib/proto/types.h | 6 + src/hss/hss-context.c | 20 + src/hss/hss-context.h | 2 + src/hss/hss-s6a-path.c | 867 ++++++++++++++++++++---- src/hss/hss-s6a-path.h | 50 ++ src/hss/meson.build | 1 + webui/server/models/profile.js | 3 + webui/server/models/subscriber.js | 3 + webui/src/components/Subscriber/View.js | 197 +++--- 14 files changed, 1022 insertions(+), 220 deletions(-) create mode 100644 src/hss/hss-s6a-path.h diff --git a/lib/dbi/subscription.c b/lib/dbi/subscription.c index dfd86214f..90b6be44a 100644 --- a/lib/dbi/subscription.c +++ b/lib/dbi/subscription.c @@ -189,6 +189,51 @@ int ogs_dbi_update_imeisv(char *supi, char *imeisv) return rv; } +int ogs_dbi_update_mme(char *supi, char *mme_host, char *mme_realm, + bool mme_ispurged) +{ + int rv = OGS_OK; + bson_t *query = NULL; + bson_t *update = NULL; + bson_error_t error; + + char *supi_type = NULL; + char *supi_id = NULL; + + ogs_assert(supi); + + supi_type = ogs_id_get_type(supi); + ogs_assert(supi_type); + supi_id = ogs_id_get_value(supi); + ogs_assert(supi_id); + + ogs_debug("SUPI type: %s, SUPI id: %s, mme_host: %s, mme_realm: %s", + supi_type, supi_id, mme_host, mme_realm); + + query = BCON_NEW(supi_type, BCON_UTF8(supi_id)); + update = BCON_NEW("$set", + "{", + "mme_host", BCON_UTF8(mme_host), + "mme_realm", BCON_UTF8(mme_realm), + "mme_timestamp", BCON_INT64(ogs_time_now()), + "mme_ispurged", BCON_BOOL(mme_ispurged), + "}"); + if (!mongoc_collection_update(ogs_mongoc()->collection.subscriber, + MONGOC_UPDATE_UPSERT, query, update, NULL, &error)) { + ogs_error("mongoc_collection_update() failure: %s", error.message); + + rv = OGS_ERROR; + } + + if (query) bson_destroy(query); + if (update) bson_destroy(update); + + ogs_free(supi_type); + ogs_free(supi_id); + + return rv; +} + int ogs_dbi_increment_sqn(char *supi) { int rv = OGS_OK; @@ -329,6 +374,14 @@ int ogs_dbi_subscription_data(char *supi, } subscription_data->num_of_msisdn = msisdn_index; + } else if (!strcmp(key, "imsi") && + BSON_ITER_HOLDS_UTF8(&iter)) { + utf8 = bson_iter_utf8(&iter, &length); + subscription_data->imsi = ogs_calloc(1, ogs_min(length, + OGS_MAX_IMSI_BCD_LEN)+1); + ogs_assert(subscription_data->imsi); + ogs_cpystrn((char*)subscription_data->imsi, + utf8, ogs_min(length, OGS_MAX_IMSI_BCD_LEN)+1); } else if (!strcmp(key, "access_restriction_data") && BSON_ITER_HOLDS_INT32(&iter)) { subscription_data->access_restriction_data = @@ -649,6 +702,25 @@ int ogs_dbi_subscription_data(char *supi, } subscription_data->num_of_slice++; } + } else if (!strcmp(key, "mme_host") && + BSON_ITER_HOLDS_UTF8(&iter)) { + utf8 = bson_iter_utf8(&iter, &length); + subscription_data->mme_host = ogs_calloc(1, ogs_min(length, + OGS_MAX_FQDN_LEN)+1); + ogs_assert(subscription_data->mme_host); + ogs_cpystrn((char*)subscription_data->mme_host, + utf8, ogs_min(length, OGS_MAX_FQDN_LEN)+1); + } else if (!strcmp(key, "mme_realm") && + BSON_ITER_HOLDS_UTF8(&iter)) { + utf8 = bson_iter_utf8(&iter, &length); + subscription_data->mme_realm = ogs_calloc(1, ogs_min(length, + OGS_MAX_FQDN_LEN)+1); + ogs_assert(subscription_data->mme_realm); + ogs_cpystrn((char*)subscription_data->mme_realm, + utf8, ogs_min(length, OGS_MAX_FQDN_LEN)+1); + } else if (!strcmp(key, "mme_ispurged") && + BSON_ITER_HOLDS_BOOL(&iter)) { + subscription_data->mme_ispurged = bson_iter_bool(&iter); } } diff --git a/lib/dbi/subscription.h b/lib/dbi/subscription.h index 2d118a7e5..aaca7578b 100644 --- a/lib/dbi/subscription.h +++ b/lib/dbi/subscription.h @@ -42,6 +42,8 @@ int ogs_dbi_auth_info(char *supi, ogs_dbi_auth_info_t *auth_info); int ogs_dbi_update_sqn(char *supi, uint64_t sqn); int ogs_dbi_increment_sqn(char *supi); int ogs_dbi_update_imeisv(char *supi, char *imeisv); +int ogs_dbi_update_mme(char *supi, char *mme_host, char *mme_realm, + bool mme_ispurged); int ogs_dbi_subscription_data(char *supi, ogs_subscription_data_t *subscription_data); diff --git a/lib/diameter/s6a/message.c b/lib/diameter/s6a/message.c index 187801aa4..e64379685 100644 --- a/lib/diameter/s6a/message.c +++ b/lib/diameter/s6a/message.c @@ -37,6 +37,7 @@ struct dict_object *ogs_diam_s6a_cmd_ida = 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_pua_flags = NULL; struct dict_object *ogs_diam_s6a_clr_flags = NULL; struct dict_object *ogs_diam_s6a_idr_flags = NULL; struct dict_object *ogs_diam_s6a_cancellation_type = NULL; @@ -110,6 +111,7 @@ int ogs_diam_s6a_init(void) 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, "PUA-Flags", &ogs_diam_s6a_pua_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, "IDR-Flags", &ogs_diam_s6a_idr_flags); CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "Cancellation-Type", &ogs_diam_s6a_cancellation_type); diff --git a/lib/diameter/s6a/message.h b/lib/diameter/s6a/message.h index 8144e61f3..ded7d31a0 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_PUA_FLAGS_FREEZE_MTMSI (1) +#define OGS_DIAM_S6A_PUA_FLAGS_FREEZE_PTMSI (1 << 1) + #define OGS_DIAM_S6A_UE_SRVCC_NOT_SUPPORTED (0) #define OGS_DIAM_S6A_UE_SRVCC_SUPPORTED (1) @@ -68,6 +71,12 @@ extern "C" { #define OGS_DIAM_S6A_VPLMN_DYNAMIC_ADDRESS_NOTALLOWED (0) #define OGS_DIAM_S6A_VPLMN_DYNAMIC_ADDRESS_ALLOWED (1) +#define OGS_DIAM_S6A_CT_MME_UPDATE_PROCEDURE (0) +#define OGS_DIAM_S6A_CT_SGSN_UPDATE_PROCEDURE (1) +#define OGS_DIAM_S6A_CT_SUBSCRIPTION_WITHDRAWL (2) +#define OGS_DIAM_S6A_CT_UPDATE_PROCEDURE_IWF (3) +#define OGS_DIAM_S6A_CT_INITIAL_ATTACH_PROCEDURE (4) + extern struct dict_object *ogs_diam_s6a_application; extern struct dict_object *ogs_diam_s6a_cmd_air; @@ -83,6 +92,7 @@ extern struct dict_object *ogs_diam_s6a_cmd_ida; 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_pua_flags; extern struct dict_object *ogs_diam_s6a_clr_flags; extern struct dict_object *ogs_diam_s6a_idr_flags; extern struct dict_object *ogs_diam_s6a_cancellation_type; diff --git a/lib/proto/types.c b/lib/proto/types.c index 06742cb8c..97f46d2a2 100644 --- a/lib/proto/types.c +++ b/lib/proto/types.c @@ -738,6 +738,13 @@ void ogs_subscription_data_free(ogs_subscription_data_t *subscription_data) ogs_assert(subscription_data); + if (subscription_data->imsi) + ogs_free(subscription_data->imsi); + if (subscription_data->mme_host) + ogs_free(subscription_data->mme_host); + if (subscription_data->mme_realm) + ogs_free(subscription_data->mme_realm); + for (i = 0; i < subscription_data->num_of_slice; i++) { ogs_slice_data_t *slice_data = &subscription_data->slice[i]; diff --git a/lib/proto/types.h b/lib/proto/types.h index 9afee3c24..f91b982e8 100644 --- a/lib/proto/types.h +++ b/lib/proto/types.h @@ -684,6 +684,8 @@ typedef struct ogs_subscription_data_s { int num_of_slice; ogs_slice_data_t slice[OGS_MAX_NUM_OF_SLICE]; + char *imsi; + #define OGS_MAX_NUM_OF_MSISDN 2 int num_of_msisdn; struct { @@ -691,6 +693,10 @@ typedef struct ogs_subscription_data_s { int len; char bcd[OGS_MAX_MSISDN_BCD_LEN+1]; } msisdn[OGS_MAX_NUM_OF_MSISDN]; + + char *mme_host; + char *mme_realm; + bool mme_ispurged; } ogs_subscription_data_t; void ogs_subscription_data_free(ogs_subscription_data_t *subscription_data); diff --git a/src/hss/hss-context.c b/src/hss/hss-context.c index 01fbace93..894d26ff3 100644 --- a/src/hss/hss-context.c +++ b/src/hss/hss-context.c @@ -396,6 +396,26 @@ int hss_db_update_imeisv(char *imsi_bcd, char *imeisv) return rv; } +int hss_db_update_mme(char *imsi_bcd, char *mme_host, char *mme_realm, + bool mme_ispurged) +{ + int rv; + char *supi = NULL; + + ogs_assert(imsi_bcd); + + ogs_thread_mutex_lock(&self.db_lock); + supi = ogs_msprintf("%s-%s", OGS_ID_SUPI_TYPE_IMSI, imsi_bcd); + ogs_assert(supi); + + rv = ogs_dbi_update_mme(supi, mme_host, mme_realm, mme_ispurged); + + ogs_free(supi); + ogs_thread_mutex_unlock(&self.db_lock); + + return rv; +} + int hss_db_increment_sqn(char *imsi_bcd) { int rv; diff --git a/src/hss/hss-context.h b/src/hss/hss-context.h index e9d71d708..26e237f37 100644 --- a/src/hss/hss-context.h +++ b/src/hss/hss-context.h @@ -63,6 +63,8 @@ int hss_db_auth_info(char *imsi_bcd, ogs_dbi_auth_info_t *auth_info); int hss_db_update_sqn(char *imsi_bcd, uint8_t *rand, uint64_t sqn); int hss_db_increment_sqn(char *imsi_bcd); int hss_db_update_imeisv(char *imsi_bcd, char *imeisv); +int hss_db_update_mme(char *imsi_bcd, char *mme_host, char *mme_realm, + bool mme_ispurged); int hss_db_subscription_data( char *imsi_bcd, ogs_subscription_data_t *subscription_data); diff --git a/src/hss/hss-s6a-path.c b/src/hss/hss-s6a-path.c index 73c111519..10744a1f2 100644 --- a/src/hss/hss-s6a-path.c +++ b/src/hss/hss-s6a-path.c @@ -21,6 +21,7 @@ #include "hss-context.h" #include "hss-fd-path.h" +#include "hss-s6a-path.h" /* handler for fallback cb */ static struct disp_hdl *hdl_s6a_fb = NULL; @@ -28,6 +29,28 @@ static struct disp_hdl *hdl_s6a_fb = NULL; static struct disp_hdl *hdl_s6a_air = NULL; /* handler for Update-Location-Request cb */ static struct disp_hdl *hdl_s6a_ulr = NULL; +/* handler for Purge-UE-Request cb */ +static struct disp_hdl *hdl_s6a_pur = NULL; +/* handler for Cancel-Location-Answer cb */ +static void hss_s6a_cla_cb(void *data, struct msg **msg); +/* handler for Insert-Subscriber-Data-Answer cb */ +static void hss_s6a_ida_cb(void *data, struct msg **msg); +/* handler for Sessions */ +static struct session_handler *hss_s6a_reg = NULL; + +/* s6a Subscription-Data builder */ +static int hss_s6a_avp_add_subscription_data( + ogs_subscription_data_t *subscription_data, struct avp *avp, + uint32_t subdatamask); + +struct sess_state { + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; +}; + +static void state_cleanup(struct sess_state *sess_data, os0_t sid, void *opaque) +{ + ogs_free(sess_data); +} /* Default callback for the application. */ static int hss_ogs_diam_s6a_fb_cb(struct msg **msg, struct avp *avp, @@ -211,7 +234,7 @@ static int hss_ogs_diam_s6a_air_cb( struct msg **msg, struct avp *avp, ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); ogs_assert(ret == 0); - /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ + /* Set the Origin-Host, Origin-Realm, and Result-Code AVPs */ ret = fd_msg_rescode_set(ans, (char*)"DIAMETER_SUCCESS", NULL, NULL, 1); ogs_assert(ret == 0); @@ -266,133 +289,32 @@ out: return 0; } -/* Callback for incoming Update-Location-Request messages */ -static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, - struct session *session, void *opaque, enum disp_action *act) +/* s6a Subscription-Data builder */ +static int hss_s6a_avp_add_subscription_data( + ogs_subscription_data_t *subscription_data, struct avp *avp, + uint32_t subdatamask) { int ret; - struct msg *ans, *qry; - - struct avp_hdr *hdr; - struct avp *avpch1; union avp_value val; - char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; - char imeisv_bcd[OGS_MAX_IMEISV_BCD_LEN+1]; - - int rv; - uint32_t result_code = 0; - ogs_subscription_data_t subscription_data; ogs_slice_data_t *slice_data = NULL; struct sockaddr_in sin; struct sockaddr_in6 sin6; - ogs_plmn_id_t visited_plmn_id; + struct avp *avp_msisdn, *avp_a_msisdn; + struct avp *avp_access_restriction_data; + struct avp *avp_subscriber_status, *avp_network_access_mode; + struct avp *avp_ambr, *avp_max_bandwidth_ul, *avp_max_bandwidth_dl; + struct avp *avp_rau_tau_timer; - ogs_assert(msg); + /* Set the APN Configuration Profile */ + struct avp *apn_configuration_profile; + struct avp *context_identifier; + struct avp *all_apn_configuration_included_indicator; - ogs_debug("Update-Location-Request"); - - memset(&subscription_data, 0, sizeof(ogs_subscription_data_t)); - - /* 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); - - rv = hss_db_subscription_data(imsi_bcd, &subscription_data); - if (rv != OGS_OK) { - ogs_error("Cannot get Subscription-Data for IMSI:'%s'", imsi_bcd); - result_code = OGS_DIAM_S6A_ERROR_USER_UNKNOWN; - goto out; - } - - ret = fd_msg_search_avp(qry, ogs_diam_s6a_terminal_information, &avp); - ogs_assert(ret == 0); - if (avp) { - char *p, *last; - - p = imeisv_bcd; - last = imeisv_bcd + OGS_MAX_IMEISV_BCD_LEN + 1; - - ret = fd_avp_search_avp(avp, ogs_diam_s6a_imei, &avpch1); - ogs_assert(ret == 0); - if (avpch1) { - ret = fd_msg_avp_hdr(avpch1, &hdr); - ogs_assert(ret == 0); - if (hdr->avp_value->os.len) { - char *s = NULL; - - ogs_assert(hdr->avp_value->os.data); - s = ogs_strndup( - (const char *)hdr->avp_value->os.data, - hdr->avp_value->os.len); - ogs_assert(s); - p = ogs_slprintf(p, last, "%s", s); - - ogs_free(s); - } - } - - ret = fd_avp_search_avp(avp, ogs_diam_s6a_software_version, &avpch1); - ogs_assert(ret == 0); - if (avpch1) { - ret = fd_msg_avp_hdr(avpch1, &hdr); - ogs_assert(ret == 0); - if (hdr->avp_value->os.len) { - char *s = NULL; - - ogs_assert(hdr->avp_value->os.data); - s = ogs_strndup( - (const char *)hdr->avp_value->os.data, - hdr->avp_value->os.len); - ogs_assert(s); - p = ogs_slprintf(p, last, "%s", s); - - ogs_free(s); - } - } - - ogs_assert(OGS_OK == hss_db_update_imeisv(imsi_bcd, imeisv_bcd)); - } - - ret = fd_msg_search_avp(qry, ogs_diam_visited_plmn_id, &avp); - ogs_assert(ret == 0); - ret = fd_msg_avp_hdr(avp, &hdr); - ogs_assert(ret == 0); - memcpy(&visited_plmn_id, hdr->avp_value->os.data, hdr->avp_value->os.len); - - ret = fd_msg_search_avp(qry, ogs_diam_s6a_ulr_flags, &avp); - ogs_assert(ret == 0); - ret = fd_msg_avp_hdr(avp, &hdr); - ogs_assert(ret == 0); - if (!(hdr->avp_value->u32 & OGS_DIAM_S6A_ULR_SKIP_SUBSCRIBER_DATA)) { - struct avp *avp_msisdn, *avp_a_msisdn; - struct avp *avp_access_restriction_data; - struct avp *avp_subscriber_status, *avp_network_access_mode; - struct avp *avp_ambr, *avp_max_bandwidth_ul, *avp_max_bandwidth_dl; - struct avp *avp_rau_tau_timer; - - /* Set the APN Configuration Profile */ - struct avp *apn_configuration_profile; - struct avp *context_identifier; - struct avp *all_apn_configuration_included_indicator; - - int i; - - /* Set the Subscription Data */ - - ret = fd_msg_avp_new(ogs_diam_s6a_subscription_data, 0, &avp); - ogs_assert(ret == 0); + int i; + if (subdatamask & OGS_HSS_SUBDATA_MSISDN) { /* * TS29.328 * 6.3.2 MSISDN AVP @@ -405,65 +327,73 @@ static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, * bits 8 to 5 of octet n encode digit 2n; * bits 4 to 1 of octet n encode digit 2(n-1)+1. */ - if (subscription_data.num_of_msisdn >= 1) { + if (subscription_data->num_of_msisdn >= 1) { ret = fd_msg_avp_new(ogs_diam_s6a_msisdn, 0, &avp_msisdn); ogs_assert(ret == 0); - val.os.data = subscription_data.msisdn[0].buf; - val.os.len = subscription_data.msisdn[0].len; + val.os.data = subscription_data->msisdn[0].buf; + val.os.len = subscription_data->msisdn[0].len; ret = fd_msg_avp_setvalue(avp_msisdn, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_msisdn); ogs_assert(ret == 0); } - if (subscription_data.num_of_msisdn >= 2) { + if (subscription_data->num_of_msisdn >= 2) { ret = fd_msg_avp_new(ogs_diam_s6a_a_msisdn, 0, &avp_a_msisdn); ogs_assert(ret == 0); - val.os.data = subscription_data.msisdn[1].buf; - val.os.len = subscription_data.msisdn[1].len; + val.os.data = subscription_data->msisdn[1].buf; + val.os.len = subscription_data->msisdn[1].len; ret = fd_msg_avp_setvalue(avp_a_msisdn, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_a_msisdn); ogs_assert(ret == 0); } + } - if (subscription_data.access_restriction_data) { + if (subdatamask & OGS_HSS_SUBDATA_ARD) { + if (subscription_data->access_restriction_data) { ret = fd_msg_avp_new(ogs_diam_s6a_access_restriction_data, 0, &avp_access_restriction_data); ogs_assert(ret == 0); - val.i32 = subscription_data.access_restriction_data; + val.i32 = subscription_data->access_restriction_data; ret = fd_msg_avp_setvalue( avp_access_restriction_data, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_access_restriction_data); ogs_assert(ret == 0); } + } + if (subdatamask & OGS_HSS_SUBDATA_SUB_STATUS) { ret = fd_msg_avp_new( ogs_diam_s6a_subscriber_status, 0, &avp_subscriber_status); ogs_assert(ret == 0); - val.i32 = subscription_data.subscriber_status; + val.i32 = subscription_data->subscriber_status; ret = fd_msg_avp_setvalue(avp_subscriber_status, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_subscriber_status); ogs_assert(ret == 0); + } + if (subdatamask & OGS_HSS_SUBDATA_NAM) { ret = fd_msg_avp_new(ogs_diam_s6a_network_access_mode, 0, &avp_network_access_mode); ogs_assert(ret == 0); - val.i32 = subscription_data.network_access_mode; + val.i32 = subscription_data->network_access_mode; ret = fd_msg_avp_setvalue(avp_network_access_mode, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_network_access_mode); ogs_assert(ret == 0); + } + if (subdatamask & OGS_HSS_SUBDATA_UEAMBR) { /* Set the AMBR */ ret = fd_msg_avp_new(ogs_diam_s6a_ambr, 0, &avp_ambr); ogs_assert(ret == 0); ret = fd_msg_avp_new( ogs_diam_s6a_max_bandwidth_ul, 0, &avp_max_bandwidth_ul); ogs_assert(ret == 0); - val.u32 = ogs_uint64_to_uint32(subscription_data.ambr.uplink); + val.u32 = ogs_uint64_to_uint32(subscription_data->ambr.uplink); ret = fd_msg_avp_setvalue(avp_max_bandwidth_ul, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add( @@ -472,7 +402,7 @@ static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, ret = fd_msg_avp_new( ogs_diam_s6a_max_bandwidth_dl, 0, &avp_max_bandwidth_dl); ogs_assert(ret == 0); - val.u32 = ogs_uint64_to_uint32(subscription_data.ambr.downlink); + val.u32 = ogs_uint64_to_uint32(subscription_data->ambr.downlink); ret = fd_msg_avp_setvalue(avp_max_bandwidth_dl, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add( @@ -480,31 +410,34 @@ static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, ogs_assert(ret == 0); ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_ambr); ogs_assert(ret == 0); + } + if (subdatamask & OGS_HSS_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); ogs_assert(ret == 0); - val.i32 = subscription_data.subscribed_rau_tau_timer * 60; /* seconds */ + /* seconds */ + val.i32 = subscription_data->subscribed_rau_tau_timer * 60; ret = fd_msg_avp_setvalue(avp_rau_tau_timer, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_rau_tau_timer); ogs_assert(ret == 0); + } + if (subdatamask & OGS_HSS_SUBDATA_SLICE) { /* For EPC, we'll use first Slice in Subscription */ - if (subscription_data.num_of_slice) - slice_data = &subscription_data.slice[0]; + if (subscription_data->num_of_slice) + slice_data = &subscription_data->slice[0]; if (!slice_data) { - ogs_error("[%s] Cannot find S-NSSAI", imsi_bcd); - result_code = OGS_DIAM_S6A_ERROR_UNKNOWN_EPS_SUBSCRIPTION; - goto out; + ogs_error("[%s] Cannot find S-NSSAI", subscription_data->imsi); + return OGS_ERROR; } if (!slice_data->num_of_session) { - ogs_error("[%s] No PDN", imsi_bcd); - result_code = OGS_DIAM_S6A_ERROR_UNKNOWN_EPS_SUBSCRIPTION; - goto out; + ogs_error("[%s] No PDN", subscription_data->imsi); + return OGS_ERROR; } ret = fd_msg_avp_new(ogs_diam_s6a_apn_configuration_profile, 0, @@ -583,7 +516,7 @@ static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, /* Set Served-Party-IP-Address */ if ((session->session_type == OGS_PDU_SESSION_TYPE_IPV4 || - session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) && + session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) && session->ue_ip.ipv4) { ret = fd_msg_avp_new(ogs_diam_s6a_served_party_ip_address, 0, &served_party_ip_address); @@ -598,7 +531,7 @@ static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, } if ((session->session_type == OGS_PDU_SESSION_TYPE_IPV6 || - session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) && + session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) && session->ue_ip.ipv6) { ret = fd_msg_avp_new(ogs_diam_s6a_served_party_ip_address, 0, &served_party_ip_address); @@ -786,16 +719,174 @@ static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, ret = fd_msg_avp_add(apn_configuration_profile, MSG_BRW_LAST_CHILD, apn_configuration); ogs_assert(ret == 0); + } ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, apn_configuration_profile); ogs_assert(ret == 0); + } + return OGS_OK; +} + +/* Callback for incoming Update-Location-Request messages */ +static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + int ret; + struct msg *ans, *qry; + + struct avp_hdr *hdr; + struct avp *avpch1; + union avp_value val; + + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + char imeisv_bcd[OGS_MAX_IMEISV_BCD_LEN+1]; + + char mme_host[OGS_MAX_FQDN_LEN+1]; + char mme_realm[OGS_MAX_FQDN_LEN+1]; + + int rv; + uint32_t result_code = 0; + ogs_subscription_data_t subscription_data; + + ogs_plmn_id_t visited_plmn_id; + + ogs_assert(msg); + + ogs_debug("Update-Location-Request"); + + memset(&subscription_data, 0, sizeof(ogs_subscription_data_t)); + + /* 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); + + rv = hss_db_subscription_data(imsi_bcd, &subscription_data); + if (rv != OGS_OK) { + ogs_error("Cannot get Subscription-Data for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_S6A_ERROR_USER_UNKNOWN; + goto out; + } + + ret = fd_msg_search_avp(qry, ogs_diam_origin_host, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + ogs_cpystrn(mme_host, (char*)hdr->avp_value->os.data, + ogs_min(hdr->avp_value->os.len, OGS_MAX_FQDN_LEN)+1); + + ret = fd_msg_search_avp(qry, ogs_diam_origin_realm, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + ogs_cpystrn(mme_realm, (char*)hdr->avp_value->os.data, + ogs_min(hdr->avp_value->os.len, OGS_MAX_FQDN_LEN)+1); + + /* If UE is not purged at MME, determine if the MME sending the ULR + * is different from the one that was last used. if so, send CLR. + */ + if (subscription_data.mme_host != NULL && + subscription_data.mme_realm != NULL) { + if (!subscription_data.mme_ispurged) { + if (strcmp(subscription_data.mme_host, mme_host) || + strcmp(subscription_data.mme_realm, mme_realm)) { + hss_s6a_send_clr(imsi_bcd, subscription_data.mme_host, + subscription_data.mme_realm, + OGS_DIAM_S6A_CT_MME_UPDATE_PROCEDURE); + ogs_info("[%s] Sending Cancel Location to previous MME", + imsi_bcd); + } + } + } + + /* Update database with current MME and timestamp */ + ogs_assert(OGS_OK == hss_db_update_mme(imsi_bcd, mme_host, mme_realm, + false)); + + ret = fd_msg_search_avp(qry, ogs_diam_s6a_terminal_information, &avp); + ogs_assert(ret == 0); + if (avp) { + char *p, *last; + + p = imeisv_bcd; + last = imeisv_bcd + OGS_MAX_IMEISV_BCD_LEN + 1; + + ret = fd_avp_search_avp(avp, ogs_diam_s6a_imei, &avpch1); + ogs_assert(ret == 0); + if (avpch1) { + ret = fd_msg_avp_hdr(avpch1, &hdr); + ogs_assert(ret == 0); + if (hdr->avp_value->os.len) { + char *s = NULL; + + ogs_assert(hdr->avp_value->os.data); + s = ogs_strndup( + (const char *)hdr->avp_value->os.data, + hdr->avp_value->os.len); + ogs_assert(s); + p = ogs_slprintf(p, last, "%s", s); + + ogs_free(s); + } + } + + ret = fd_avp_search_avp(avp, ogs_diam_s6a_software_version, &avpch1); + ogs_assert(ret == 0); + if (avpch1) { + ret = fd_msg_avp_hdr(avpch1, &hdr); + ogs_assert(ret == 0); + if (hdr->avp_value->os.len) { + char *s = NULL; + + ogs_assert(hdr->avp_value->os.data); + s = ogs_strndup( + (const char *)hdr->avp_value->os.data, + hdr->avp_value->os.len); + ogs_assert(s); + p = ogs_slprintf(p, last, "%s", s); + + ogs_free(s); + } + } + + ogs_assert(OGS_OK == hss_db_update_imeisv(imsi_bcd, imeisv_bcd)); + } + + ret = fd_msg_search_avp(qry, ogs_diam_visited_plmn_id, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + memcpy(&visited_plmn_id, hdr->avp_value->os.data, hdr->avp_value->os.len); + + ret = fd_msg_search_avp(qry, ogs_diam_s6a_ulr_flags, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + if (!(hdr->avp_value->u32 & OGS_DIAM_S6A_ULR_SKIP_SUBSCRIBER_DATA)) { + /* Set the Subscription Data */ + 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); + if (rv != OGS_OK) { + result_code = OGS_DIAM_S6A_ERROR_UNKNOWN_EPS_SUBSCRIPTION; + goto out; + } ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); ogs_assert(ret == 0); } - /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ + /* Set the Origin-Host, Origin-Realm, and Result-Code AVPs */ ret = fd_msg_rescode_set(ans, (char*)"DIAMETER_SUCCESS", NULL, NULL, 1); ogs_assert(ret == 0); @@ -863,6 +954,483 @@ out: return 0; } +/* Callback for incoming Purge-UE-Request messages */ +static int hss_ogs_diam_s6a_pur_cb( struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + int ret, rv; + + struct msg *ans, *qry; + struct avp_hdr *hdr; + union avp_value val; + + ogs_subscription_data_t subscription_data; + + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + char mme_host[OGS_MAX_FQDN_LEN+1]; + char mme_realm[OGS_MAX_FQDN_LEN+1]; + + uint32_t result_code = 0; + + ogs_assert(msg); + + ogs_debug("Purge-UE-Request"); + + memset(&subscription_data, 0, sizeof(ogs_subscription_data_t)); + + /* 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); + + rv = hss_db_subscription_data(imsi_bcd, &subscription_data); + if (rv != OGS_OK) { + ogs_error("Cannot get Subscription-Data for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_S6A_ERROR_USER_UNKNOWN; + goto out; + } + + ret = fd_msg_search_avp(qry, ogs_diam_origin_host, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + ogs_cpystrn(mme_host, (char*)hdr->avp_value->os.data, + ogs_min(hdr->avp_value->os.len, OGS_MAX_FQDN_LEN)+1); + + ret = fd_msg_search_avp(qry, ogs_diam_origin_realm, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + ogs_cpystrn(mme_realm, (char*)hdr->avp_value->os.data, + ogs_min(hdr->avp_value->os.len, OGS_MAX_FQDN_LEN)+1); + + if (!strcmp(subscription_data.mme_host, mme_host) && + !strcmp(subscription_data.mme_realm, mme_realm)) { + rv = hss_db_update_mme(imsi_bcd, mme_host, mme_realm, true); + if (rv != OGS_OK) { + ogs_error("Cannot update UE Purged at MME flag:'%s'", imsi_bcd); + ret = fd_msg_rescode_set(ans, + (char*)"DIAMETER_UNABLE_TO_COMPLY", NULL, NULL, 1); + ogs_assert(ret == 0); + goto outnoexp; + } + } + + /* Set the PUA Flags */ + ret = fd_msg_avp_new(ogs_diam_s6a_pua_flags, 0, &avp); + ogs_assert(ret == 0); + if (!strcmp(subscription_data.mme_host, mme_host) && + !strcmp(subscription_data.mme_realm, mme_realm)) { + val.i32 = OGS_DIAM_S6A_PUA_FLAGS_FREEZE_MTMSI; + } else { + val.i32 = 0; + } + 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); + + /* Set the Origin-Host, Origin-Realm, and Result-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); + + /* 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("Purge-UE-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); + + ogs_subscription_data_free(&subscription_data); + + return 0; + +out: + ret = ogs_diam_message_experimental_rescode_set(ans, result_code); + ogs_assert(ret == 0); +outnoexp: + /* 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); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_S6A_APPLICATION_ID); + ogs_assert(ret == 0); + + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_subscription_data_free(&subscription_data); + + return 0; +} + +/* 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) +{ + int ret; + + struct msg *req = NULL; + struct avp *avp; + union avp_value val; + struct sess_state *sess_data = NULL, *svg; + struct session *session = NULL; + + ogs_debug("[HSS] Cancel-Location-Request"); + + /* Create the random value to store with the session */ + sess_data = ogs_calloc(1, sizeof(*sess_data)); + ogs_assert(sess_data); + + /* Create the request */ + ret = fd_msg_new(ogs_diam_s6a_cmd_clr, MSGFL_ALLOC_ETEID, &req); + ogs_assert(ret == 0); + + /* Create a new session */ + #define OGS_DIAM_S6A_APP_SID_OPT "app_s6a" + ret = fd_msg_new_session(req, (os0_t)OGS_DIAM_S6A_APP_SID_OPT, + CONSTSTRLEN(OGS_DIAM_S6A_APP_SID_OPT)); + ogs_assert(ret == 0); + ret = fd_msg_sess_get(fd_g_config->cnf_dict, req, &session, NULL); + 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(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Origin-Host & Origin-Realm */ + ret = fd_msg_add_origin(req, 0); + ogs_assert(ret == 0); + + /* Set the Destination-Host AVP */ + if (mme_host != NULL) { + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)mme_host; + val.os.len = strlen(mme_host); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + } + + /* Set the Destination-Realm AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); + ogs_assert(ret == 0); + if (mme_realm == NULL) { + val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); + val.os.len = strlen(fd_g_config->cnf_diamrlm); + } else { + val.os.data = (unsigned char *)mme_realm; + val.os.len = strlen(mme_realm); + } + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the User-Name AVP */ + ret = fd_msg_avp_new(ogs_diam_user_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)imsi_bcd; + val.os.len = strlen(imsi_bcd); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the CLR-Flags */ + ret = fd_msg_avp_new(ogs_diam_s6a_clr_flags, 0, &avp); + ogs_assert(ret == 0); + if (cancellation_type == OGS_DIAM_S6A_CT_SUBSCRIPTION_WITHDRAWL) { + val.u32 = (OGS_DIAM_S6A_CLR_FLAGS_REATTACH_REQUIRED | + OGS_DIAM_S6A_CLR_FLAGS_S6A_S6D_INDICATOR); + } else { + val.u32 = OGS_DIAM_S6A_CLR_FLAGS_S6A_S6D_INDICATOR; + } + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Cancellation-Type */ + ret = fd_msg_avp_new(ogs_diam_s6a_cancellation_type, 0, &avp); + ogs_assert(ret == 0); + val.u32 = cancellation_type; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + req, OGS_DIAM_S6A_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Keep a pointer to the session data for debug purpose, + * in real life we would not need it */ + svg = sess_data; + + /* Store this value in the session */ + ret = fd_sess_state_store(hss_s6a_reg, session, &sess_data); + ogs_assert(ret == 0); + ogs_assert(sess_data == 0); + + /* Send the request */ + ret = fd_msg_send(&req, hss_s6a_cla_cb, svg); + ogs_assert(ret == 0); + + /* Increment the counter */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_sent++; + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); +} + +/* HSS received Cancel Location Answer from MME */ +static void hss_s6a_cla_cb(void *data, struct msg **msg) +{ + int ret; + + struct sess_state *sess_data = NULL; + struct session *session; + int new; + + ogs_debug("[HSS] Cancel-Location-Answer"); + + /* Search the session, retrieve its data */ + ret = fd_msg_sess_get(fd_g_config->cnf_dict, *msg, &session, &new); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(new == 0); + + ret = fd_sess_state_retrieve(hss_s6a_reg, session, &sess_data); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(sess_data); + ogs_expect_or_return((void *)sess_data == data); + + ret = fd_msg_free(*msg); + ogs_assert(ret == 0); + *msg = NULL; + + state_cleanup(sess_data, NULL, NULL); + return; +} + +/* HSS Sends Insert Subscriber Data Request to MME */ +/* arguments: flags, subscriber data, imsi */ +int hss_s6a_send_idr(char *imsi_bcd, uint32_t idr_flags, uint32_t subdatamask) +{ + int ret; + + struct msg *req = NULL; + struct avp *avp; + union avp_value val; + struct sess_state *sess_data = NULL, *svg; + struct session *session = NULL; + + ogs_subscription_data_t subscription_data; + + ogs_debug("[HSS] Insert-Subscriber-Data-Request"); + + memset(&subscription_data, 0, sizeof(ogs_subscription_data_t)); + + ret = hss_db_subscription_data(imsi_bcd, &subscription_data); + if (ret != OGS_OK) { + ogs_error("Cannot get Subscription-Data for IMSI:'%s'", imsi_bcd); + return OGS_ERROR; + } + + if (subscription_data.mme_ispurged) { + ogs_error(" [%s] UE Purged at MME. Cannot send IDR.", imsi_bcd); + return OGS_ERROR; + } + + /* Create the random value to store with the session */ + sess_data = ogs_calloc(1, sizeof(*sess_data)); + ogs_assert(sess_data); + + /* Create the request */ + ret = fd_msg_new(ogs_diam_s6a_cmd_idr, MSGFL_ALLOC_ETEID, &req); + ogs_assert(ret == 0); + + /* Create a new session */ + #define OGS_DIAM_S6A_APP_SID_OPT "app_s6a" + ret = fd_msg_new_session(req, (os0_t)OGS_DIAM_S6A_APP_SID_OPT, + CONSTSTRLEN(OGS_DIAM_S6A_APP_SID_OPT)); + ogs_assert(ret == 0); + ret = fd_msg_sess_get(fd_g_config->cnf_dict, req, &session, NULL); + 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(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Origin-Host & Origin-Realm */ + ret = fd_msg_add_origin(req, 0); + ogs_assert(ret == 0); + + /* Set the Destination-Host AVP */ + if (subscription_data.mme_host != NULL) { + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)subscription_data.mme_host; + val.os.len = strlen(subscription_data.mme_host); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + } + + /* Set the Destination-Realm AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); + ogs_assert(ret == 0); + if (subscription_data.mme_realm == NULL) { + val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); + val.os.len = strlen(fd_g_config->cnf_diamrlm); + } else { + val.os.data = (unsigned char *)subscription_data.mme_realm; + val.os.len = strlen(subscription_data.mme_realm); + } + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the User-Name AVP */ + ret = fd_msg_avp_new(ogs_diam_user_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)imsi_bcd; + val.os.len = strlen(imsi_bcd); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the IDR-Flags */ + if (idr_flags) { + ret = fd_msg_avp_new(ogs_diam_s6a_idr_flags, 0, &avp); + ogs_assert(ret == 0); + val.u32 = idr_flags; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + } + + /* Set the Subscription Data */ + ret = fd_msg_avp_new(ogs_diam_s6a_subscription_data, 0, &avp); + ogs_assert(ret == 0); + if (subdatamask) { + ret = hss_s6a_avp_add_subscription_data(&subscription_data, + avp, subdatamask); + if (ret != OGS_OK) { + ogs_error(" [%s] Could not build Subscription-Data.", + imsi_bcd); + return OGS_ERROR; + } + } + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + req, OGS_DIAM_S6A_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Keep a pointer to the session data for debug purpose, + * in real life we would not need it */ + svg = sess_data; + + /* Store this value in the session */ + ret = fd_sess_state_store(hss_s6a_reg, session, &sess_data); + ogs_assert(ret == 0); + ogs_assert(sess_data == 0); + + /* Send the request */ + ret = fd_msg_send(&req, hss_s6a_ida_cb, svg); + ogs_assert(ret == 0); + + /* Increment the counter */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_sent++; + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + ogs_subscription_data_free(&subscription_data); + + return OGS_OK; +} + +/* HSS received Insert Subscriber Data Answer from MME */ +static void hss_s6a_ida_cb(void *data, struct msg **msg) +{ + int ret; + + struct sess_state *sess_data = NULL; + struct session *session; + int new; + + ogs_debug("[HSS] Insert-Subscriber-Data-Answer"); + + /* Search the session, retrieve its data */ + ret = fd_msg_sess_get(fd_g_config->cnf_dict, *msg, &session, &new); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(new == 0); + + ret = fd_sess_state_retrieve(hss_s6a_reg, session, &sess_data); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(sess_data); + ogs_expect_or_return((void *)sess_data == data); + + ret = fd_msg_free(*msg); + ogs_assert(ret == 0); + *msg = NULL; + + state_cleanup(sess_data, NULL, NULL); + return; +} + int hss_s6a_init(void) { int ret; @@ -875,6 +1443,10 @@ int hss_s6a_init(void) memset(&data, 0, sizeof(data)); data.app = ogs_diam_s6a_application; + /* Create handler for sessions */ + ret = fd_sess_handler_create(&hss_s6a_reg, state_cleanup, NULL, NULL); + ogs_assert(ret == 0); + /* Fallback CB if command != unexpected message received */ ret = fd_disp_register(hss_ogs_diam_s6a_fb_cb, DISP_HOW_APPID, &data, NULL, &hdl_s6a_fb); @@ -892,6 +1464,12 @@ int hss_s6a_init(void) &hdl_s6a_ulr); ogs_assert(ret == 0); + /* Specific handler for Purge-UE-Request */ + data.command = ogs_diam_s6a_cmd_pur; + ret = fd_disp_register(hss_ogs_diam_s6a_pur_cb, DISP_HOW_CC, &data, NULL, + &hdl_s6a_pur); + 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); @@ -901,10 +1479,17 @@ int hss_s6a_init(void) void hss_s6a_final(void) { + int ret; + + ret = fd_sess_handler_destroy(&hss_s6a_reg, NULL); + ogs_assert(ret == OGS_OK); + if (hdl_s6a_fb) (void) fd_disp_unregister(&hdl_s6a_fb, NULL); if (hdl_s6a_air) (void) fd_disp_unregister(&hdl_s6a_air, NULL); if (hdl_s6a_ulr) (void) fd_disp_unregister(&hdl_s6a_ulr, NULL); + if (hdl_s6a_pur) + (void) fd_disp_unregister(&hdl_s6a_pur, NULL); } diff --git a/src/hss/hss-s6a-path.h b/src/hss/hss-s6a-path.h new file mode 100644 index 000000000..3e3b0381f --- /dev/null +++ b/src/hss/hss-s6a-path.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HSS_S6A_PATH_H +#define HSS_S6A_PATH_H + +#include "hss-context.h" + +#ifdef __cplusplus +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); + +/* HSS Sends Insert Subscriber Data Request to MME */ +int hss_s6a_send_idr(char *imsi_bcd, uint32_t idr_flags, uint32_t subdata_mask); + +#ifdef __cplusplus +} +#endif + +#endif /* HSS_S6A_PATH_H */ diff --git a/src/hss/meson.build b/src/hss/meson.build index 1cae23fcf..cd52f987c 100644 --- a/src/hss/meson.build +++ b/src/hss/meson.build @@ -18,6 +18,7 @@ libhss_sources = files(''' hss-context.h hss-fd-path.h + hss-s6a-path.h hss-init.c hss-context.c diff --git a/webui/server/models/profile.js b/webui/server/models/profile.js index 053567ed7..9e36a5bb3 100644 --- a/webui/server/models/profile.js +++ b/webui/server/models/profile.js @@ -13,6 +13,9 @@ const Profile = new Schema({ msisdn: [ String ], imeisv: [ String ], + mme_host: [ String ], + mme_realm: [ String ], + mme_ispurged: [ Boolean ], security: { k: String, diff --git a/webui/server/models/subscriber.js b/webui/server/models/subscriber.js index 81a4ec857..a08dcba07 100644 --- a/webui/server/models/subscriber.js +++ b/webui/server/models/subscriber.js @@ -13,6 +13,9 @@ const Subscriber = new Schema({ msisdn: [ String ], imeisv: [ String ], + mme_host: [ String ], + mme_realm: [ String ], + mme_ispurged: [ Boolean ], security: { k: String, diff --git a/webui/src/components/Subscriber/View.js b/webui/src/components/Subscriber/View.js index 30ba6147b..c7e71c0e3 100644 --- a/webui/src/components/Subscriber/View.js +++ b/webui/src/components/Subscriber/View.js @@ -10,6 +10,7 @@ import CloseIcon from 'react-icons/lib/md/close'; import PhoneIcon from 'react-icons/lib/md/phone'; import SecurityIcon from 'react-icons/lib/md/security'; +import MmeIcon from 'react-icons/lib/md/router'; import PdnIcon from 'react-icons/lib/md/cast'; import KeyboardControlIcon from 'react-icons/lib/md/keyboard-control'; @@ -99,6 +100,12 @@ const Subscriber = styled.div` margin: 12px; font-size: 16px; } + .sectionbody { + display: flex; + } + .sectioncolumn { + flex: 1; + } .body { display: flex; flex-direction: row; @@ -163,6 +170,9 @@ const View = ({ visible, disableOnClickOutside, subscriber, onEdit, onDelete, on const imsi = (subscriber || {}).imsi; const msisdn_list = ((subscriber || {}).msisdn || []); const imeisv = (subscriber || {}).imeisv; + const mme_host = (subscriber || {}).mme_host; + const mme_realm = (subscriber || {}).mme_realm; + const mme_ispurged = (subscriber || {}).mme_ispurged; const security = ((subscriber || {}).security || {}); const ambr = ((subscriber || {}).ambr || {}); const slice_list = ((subscriber || {}).slice || []); @@ -193,94 +203,123 @@ const View = ({ visible, disableOnClickOutside, subscriber, onEdit, onDelete, on
Subscriber Configuration
- {(msisdn_list.length !== 0 || (imeisv && imeisv.length !== 0)) && -
-
- -
-
- {msisdn_list.map((msisdn, index) => -
- {msisdn} - MSISDN +
+
+ {(msisdn_list.length !== 0 || (imeisv && imeisv.length !== 0)) && +
+
+
- )} - {imeisv && imeisv.length !== 0 && +
+ {msisdn_list.map((msisdn, index) => +
+ {msisdn} + MSISDN +
+ )} + {imeisv && imeisv.length !== 0 && +
+ {imeisv} + IMEISV +
+ } +
+
+ } +
+
+ +
+
- {imeisv} - IMEISV + {security.k} + K
- } + {security.opc && +
+ {security.opc} + OPc +
+ } + {security.op && +
+ {security.op} + OP +
+ } +
+ {security.amf} + AMF +
+ {security.sqn && +
+ {security.sqn} + SQN +
+ } +
- } -
-
- -
-
-
- {security.k} - K -
- {security.opc && -
- {security.opc} - OPc +
+ {mme_host && mme_host.length !== 0 && +
+
+ +
+
+
+ {mme_host} + MME Hostname +
+ {mme_realm && +
+ {mme_realm} + MME Realm +
+ } +
+ {mme_ispurged === true ? ( "Purged" ) : ( "Not Purged" )} + UE is Purged at MME +
+
} - {security.op && -
- {security.op} - OP +
+
+
- } -
- {security.amf} - AMF -
- {security.sqn && -
- {security.sqn} - SQN +
+
+ {ambr['downlink'] === undefined ? "unlimited" : + ambr.downlink['value'] === undefined ? "unlimited" : + ambr.downlink.value + } {ambr['downlink'] === undefined ? "unlimited" : + ambr.downlink['value'] === undefined ? "" : + ambr.downlink['unit'] === undefined ? "bps" : + ambr.downlink.unit === 0 ? "bps" : + ambr.downlink.unit === 1 ? "Kbps" : + ambr.downlink.unit === 2 ? "Mbps" : + ambr.downlink.unit === 3 ? "Gbps" : + ambr.downlink.unit === 4 ? "Tbps" : + "Unknown Unit" } + DL +
+
+ {ambr['uplink'] === undefined ? "unlimited" : + ambr.uplink['value'] === undefined ? "unlimited" : + ambr.uplink.value + } {ambr['uplink'] === undefined ? "unlimited" : + ambr.uplink['value'] === undefined ? "" : + ambr.uplink['unit'] === undefined ? "bps" : + ambr.uplink.unit === 0 ? "bps" : + ambr.uplink.unit === 1 ? "Kbps" : + ambr.uplink.unit === 2 ? "Mbps" : + ambr.uplink.unit === 3 ? "Gbps" : + ambr.uplink.unit === 4 ? "Tbps" : + "Unknown Unit" } + UL +
- } -
-
-
-
- -
-
-
- {ambr['downlink'] === undefined ? "unlimited" : - ambr.downlink['value'] === undefined ? "unlimited" : - ambr.downlink.value - } {ambr['downlink'] === undefined ? "unlimited" : - ambr.downlink['value'] === undefined ? "" : - ambr.downlink['unit'] === undefined ? "bps" : - ambr.downlink.unit === 0 ? "bps" : - ambr.downlink.unit === 1 ? "Kbps" : - ambr.downlink.unit === 2 ? "Mbps" : - ambr.downlink.unit === 3 ? "Gbps" : - ambr.downlink.unit === 4 ? "Tbps" : - "Unknown Unit" } - DL -
-
- {ambr['uplink'] === undefined ? "unlimited" : - ambr.uplink['value'] === undefined ? "unlimited" : - ambr.uplink.value - } {ambr['uplink'] === undefined ? "unlimited" : - ambr.uplink['value'] === undefined ? "" : - ambr.uplink['unit'] === undefined ? "bps" : - ambr.uplink.unit === 0 ? "bps" : - ambr.uplink.unit === 1 ? "Kbps" : - ambr.uplink.unit === 2 ? "Mbps" : - ambr.uplink.unit === 3 ? "Gbps" : - ambr.uplink.unit === 4 ? "Tbps" : - "Unknown Unit" } - UL