diff --git a/lib/dbi/ims.c b/lib/dbi/ims.c index ce4dea3d7..07d3c04e7 100644 --- a/lib/dbi/ims.c +++ b/lib/dbi/ims.c @@ -120,7 +120,8 @@ int ogs_dbi_ims_data(char *supi, ogs_ims_data_t *ims_data) bson_error_t error; const bson_t *document; bson_iter_t iter; - bson_iter_t child1_iter; + bson_iter_t child1_iter, child2_iter, child3_iter, child4_iter, child5_iter; + bson_iter_t child6_iter, child7_iter, child8_iter, child9_iter; const char *utf8 = NULL; uint32_t length = 0; @@ -190,6 +191,222 @@ int ogs_dbi_ims_data(char *supi, ogs_ims_data_t *ims_data) } } ims_data->num_of_msisdn = msisdn_index; + } else if (!strcmp(key, "ifc") && + BSON_ITER_HOLDS_ARRAY(&iter)) { + int ifc_index = 0; + bson_iter_recurse(&iter, &child2_iter); + while (bson_iter_next(&child2_iter)) { + ogs_assert(ifc_index < OGS_MAX_NUM_OF_IFC); + bson_iter_recurse(&child2_iter, &child3_iter); + while (bson_iter_next(&child3_iter)) { + const char *child3_key = bson_iter_key(&child3_iter); + if (!strcmp(child3_key, "priority") && + BSON_ITER_HOLDS_INT32(&child3_iter)) { + ims_data->ifc[ifc_index].priority = + bson_iter_int32(&child3_iter); + } else if (!strcmp(child3_key, "application_server") && + BSON_ITER_HOLDS_DOCUMENT(&child3_iter)) { + bson_iter_recurse(&child3_iter, &child4_iter); + while (bson_iter_next(&child4_iter)) { + const char *child4_key = + bson_iter_key(&child4_iter); + if (!strcmp(child4_key, "server_name") && + BSON_ITER_HOLDS_UTF8(&child4_iter)) { + utf8 = bson_iter_utf8(&child4_iter, &length); + ims_data->ifc[ifc_index] + .application_server.server_name = + ogs_strndup(utf8, length); + } else if (!strcmp(child4_key, "default_handling") + && BSON_ITER_HOLDS_INT32(&child4_iter)) { + ims_data->ifc[ifc_index] + .application_server.default_handling = + bson_iter_int32(&child4_iter); + } + } + } else if (!strcmp(child3_key, "trigger_point") && + BSON_ITER_HOLDS_DOCUMENT(&child3_iter)) { + bson_iter_recurse(&child3_iter, &child5_iter); + while (bson_iter_next(&child5_iter)) { + const char *child5_key = + bson_iter_key(&child5_iter); + if (!strcmp(child5_key, "condition_type_cnf") && + BSON_ITER_HOLDS_INT32(&child5_iter)) { + ims_data->ifc[ifc_index] + .trigger_point.condition_type_cnf = + bson_iter_int32(&child5_iter); + } else if (!strcmp(child5_key, "spt") && + BSON_ITER_HOLDS_ARRAY(&child5_iter)) { + int spt_index = 0; + bson_iter_recurse(&child5_iter, &child6_iter); + while (bson_iter_next(&child6_iter)) { + ogs_assert(spt_index < OGS_MAX_NUM_OF_SPT); + bson_iter_recurse(&child6_iter, + &child7_iter); + while (bson_iter_next(&child7_iter)) { + const char *child7_key = + bson_iter_key(&child7_iter); + if (!strcmp(child7_key, + "condition_negated") && + BSON_ITER_HOLDS_INT32( + &child7_iter)) { + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .condition_negated = + bson_iter_int32( + &child7_iter); + } else if (!strcmp(child7_key, "group") + && BSON_ITER_HOLDS_INT32( + &child7_iter)) { + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .group = bson_iter_int32( + &child7_iter); + } else if (!strcmp(child7_key, + "method") && + BSON_ITER_HOLDS_UTF8( + &child7_iter)) { + utf8 = bson_iter_utf8(&child7_iter, + &length); + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .method = + ogs_strndup(utf8, length); + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .type = OGS_SPT_HAS_METHOD; + } else if (!strcmp(child7_key, + "session_case") && + BSON_ITER_HOLDS_INT32( + &child7_iter)) { + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .session_case = + bson_iter_int32( + &child7_iter); + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .type = + OGS_SPT_HAS_SESSION_CASE; + } else if (!strcmp(child7_key, + "sip_header") && + BSON_ITER_HOLDS_DOCUMENT( + &child7_iter)) { + bson_iter_recurse(&child7_iter, + &child8_iter); + while (bson_iter_next( + &child8_iter)) { + const char *child8_key = + bson_iter_key( + &child8_iter); + if (!strcmp(child8_key, + "header") && + BSON_ITER_HOLDS_UTF8( + &child8_iter)) { + utf8 = bson_iter_utf8( + &child8_iter, + &length); + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .header = + ogs_strndup(utf8, + length); + } else if (!strcmp(child8_key, + "content") && + BSON_ITER_HOLDS_UTF8( + &child8_iter)) { + utf8 = bson_iter_utf8( + &child8_iter, + &length); + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .header_content = + ogs_strndup(utf8, + length); + } + } + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .type = OGS_SPT_HAS_SIP_HEADER; + } else if (!strcmp(child7_key, + "sdp_line") && + BSON_ITER_HOLDS_DOCUMENT( + &child7_iter)) { + bson_iter_recurse(&child7_iter, + &child9_iter); + while (bson_iter_next( + &child9_iter)) { + const char *child9_key = + bson_iter_key( + &child9_iter); + if (!strcmp(child9_key, + "line") && + BSON_ITER_HOLDS_UTF8( + &child9_iter)) { + utf8 = bson_iter_utf8( + &child9_iter, + &length); + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .sdp_line = + ogs_strndup(utf8, + length); + } else if (!strcmp(child9_key, + "content") && + BSON_ITER_HOLDS_UTF8( + &child9_iter)) { + utf8 = bson_iter_utf8( + &child9_iter, + &length); + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .sdp_line_content = + ogs_strndup(utf8, + length); + } + } + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .type = OGS_SPT_HAS_SDP_LINE; + } else if (!strcmp(child7_key, + "request_uri") && + BSON_ITER_HOLDS_UTF8( + &child7_iter)) { + utf8 = bson_iter_utf8(&child7_iter, + &length); + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .request_uri = + ogs_strndup(utf8, length); + ims_data->ifc[ifc_index] + .trigger_point + .spt[spt_index] + .type = OGS_SPT_HAS_REQUEST_URI; + } + } + spt_index++; + } + ims_data->ifc->trigger_point.num_of_spt = + spt_index; + } + } + } + } + ifc_index++; + } + ims_data->num_of_ifc = ifc_index; } } diff --git a/lib/diameter/s6a/message.c b/lib/diameter/s6a/message.c index c498f3b95..4902b6899 100644 --- a/lib/diameter/s6a/message.c +++ b/lib/diameter/s6a/message.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -92,6 +92,10 @@ struct dict_object *ogs_diam_s6a_software_version = NULL; struct dict_object *ogs_diam_s6a_msisdn = NULL; struct dict_object *ogs_diam_s6a_a_msisdn = NULL; +struct dict_object *ogs_diam_s6a_supported_features = NULL; +struct dict_object *ogs_diam_s6a_feature_list_id = NULL; +struct dict_object *ogs_diam_s6a_feature_list = NULL; + extern int ogs_dict_s6a_entry(char *conffile); int ogs_diam_s6a_init(void) @@ -176,5 +180,9 @@ int ogs_diam_s6a_init(void) CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "MSISDN", &ogs_diam_s6a_msisdn); CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "A-MSISDN", &ogs_diam_s6a_a_msisdn); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "Supported-Features", &ogs_diam_s6a_supported_features); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "Feature-List-ID", &ogs_diam_s6a_feature_list_id); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, "Feature-List", &ogs_diam_s6a_feature_list); + return 0; } diff --git a/lib/diameter/s6a/message.h b/lib/diameter/s6a/message.h index 56258f74c..f7322c1d8 100644 --- a/lib/diameter/s6a/message.h +++ b/lib/diameter/s6a/message.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -159,6 +159,10 @@ extern struct dict_object *ogs_diam_s6a_software_version; extern struct dict_object *ogs_diam_s6a_msisdn; extern struct dict_object *ogs_diam_s6a_a_msisdn; +extern struct dict_object *ogs_diam_s6a_supported_features; +extern struct dict_object *ogs_diam_s6a_feature_list_id; +extern struct dict_object *ogs_diam_s6a_feature_list; + typedef struct ogs_diam_e_utran_vector_s { uint8_t xres[OGS_MAX_RES_LEN]; uint8_t xres_len; diff --git a/lib/proto/types.h b/lib/proto/types.h index fcf3a8706..03690537d 100644 --- a/lib/proto/types.h +++ b/lib/proto/types.h @@ -958,6 +958,97 @@ typedef struct ogs_media_component_s { int num_of_sub; } ogs_media_component_t; +#define OGS_MAX_NUM_OF_SPT 20 +#define OGS_MAX_NUM_OF_IFC 20 + +/* + * Defines matching mechanism type of SPT + */ +typedef enum { + OGS_SPT_INVALID_TYPE, + OGS_SPT_HAS_METHOD, + OGS_SPT_HAS_SESSION_CASE, + OGS_SPT_HAS_SIP_HEADER, + OGS_SPT_HAS_SDP_LINE, + OGS_SPT_HAS_REQUEST_URI, +} ogs_spt_type_e; + +/************************************************** + * Service Point Trigger Structure (SPT) */ +typedef struct ogs_spt_s { + /* Matching mechanism type of SPT */ + ogs_spt_type_e type; + /* Indicates if the Service Point Trigger instance is negated */ + int condition_negated; + /* The SPT group or list of SPT groups assigned to the SPT */ + int group; + /* The method of the SIP request */ + const char *method; + /* The direction of the SIP request as evaluated by the S-CSCF */ + int session_case; + /* A header in the SIP request*/ + const char *header; + /* Optionally the value of the header in the SIP request */ + const char *header_content; + /* A SDP line within the body (if any) of a SIP request */ + const char *sdp_line; + /* Optionally the value in the SDP line of a SIP request */ + const char *sdp_line_content; + /* The request-URI of the SIP request */ + const char *request_uri; +} ogs_spt_t; + +/* + * Defines what logical operators should be used between SPTs belonging to + * different groups + */ +typedef enum { + OGS_DISJUNCTIVE_NORMAL_FORMAT, /* an ORed set of ANDed subsets */ + OGS_CONJUNCTIVE_NORMAL_FORMAT /* an ANDed set of ORed subsets */ +} ogs_condition_type_cnf_e; + +/************************************************** + * Trigger Point Structure + * Each TriggerPoint is made up of Service Point Trigger (SPTs) which are + * individual rules that are matched or not matched, that are either combined + * as logical AND or logical OR statements when evaluated. + */ +typedef struct ogs_trigger_point_s { + int num_of_spt; + ogs_condition_type_cnf_e condition_type_cnf; + ogs_spt_t spt[OGS_MAX_NUM_OF_SPT]; +} ogs_trigger_point_t; + +/************************************************** + * Application Server Structure */ +typedef struct ogs_application_server_s { + const char *server_name; + int default_handling; +} ogs_application_server_t; + +/************************************************** + * IFC Structure + * 3GPP TS 29.562 + */ +typedef struct ogs_ifc_s { + /* + * The priority of the IFC. + * The higher the Priority Number the lower the priority of the Filter + * Criteria is + */ + int priority; + /* + * The conditions that should be checked to find out + * if the indicated Application Server should be contacted or not + */ + ogs_trigger_point_t trigger_point; + /* + * the Application Server which shall be triggered + * if the conditions are met + */ + ogs_application_server_t application_server; +} ogs_ifc_t; + typedef struct ogs_ims_data_s { int num_of_msisdn; struct { @@ -969,6 +1060,9 @@ typedef struct ogs_ims_data_s { #define OGS_MAX_NUM_OF_MEDIA_COMPONENT 16 ogs_media_component_t media_component[OGS_MAX_NUM_OF_MEDIA_COMPONENT]; int num_of_media_component; + + int num_of_ifc; + ogs_ifc_t ifc[OGS_MAX_NUM_OF_IFC]; } ogs_ims_data_t; void ogs_ims_data_free(ogs_ims_data_t *ims_data); diff --git a/lib/sbi/message.c b/lib/sbi/message.c index 51c6e8fad..65a22aab7 100644 --- a/lib/sbi/message.c +++ b/lib/sbi/message.c @@ -738,6 +738,26 @@ ogs_sbi_request_t *ogs_sbi_build_request(ogs_sbi_message_t *message) ogs_free(fields); } + } + if (message->param.num_of_fields) { + char *fields; + + fields = ogs_strdup(message->param.fields[0]); + if (!fields) { + ogs_error("ogs_strdup() failed"); + return NULL; + } + + for (i = 1; i < message->param.num_of_fields; i++) + fields = ogs_mstrcatf( + fields, ",%s", message->param.fields[i]); + + if (fields) { + ogs_sbi_header_set(request->http.params, + OGS_SBI_PARAM_FIELDS, fields); + ogs_free(fields); + } + } if (message->param.ipv4addr) { ogs_sbi_header_set(request->http.params, diff --git a/lib/sbi/message.h b/lib/sbi/message.h index 9d286aaac..49fe847fe 100644 --- a/lib/sbi/message.h +++ b/lib/sbi/message.h @@ -364,6 +364,11 @@ extern "C" { #define OGS_SBI_PARAM_FIELDS_NSSAI "nssai" #define OGS_SBI_MAX_NUM_OF_FIELDS 8 +#define OGS_SBI_PARAM_FIELDS_GPSIS "gpsis" +#define OGS_SBI_PARAM_FIELDS_SUBSCRIBED_UE_AMBR "subscribedUeAmbr" +#define OGS_SBI_PARAM_FIELDS_NSSAI "nssai" +#define OGS_SBI_MAX_NUM_OF_FIELDS 8 + #define OGS_SBI_CONTENT_JSON_TYPE \ OGS_SBI_APPLICATION_TYPE "/" OGS_SBI_APPLICATION_JSON_TYPE #define OGS_SBI_CONTENT_PROBLEM_TYPE \ diff --git a/src/hss/hss-context.c b/src/hss/hss-context.c index d42c26655..918fa4820 100644 --- a/src/hss/hss-context.c +++ b/src/hss/hss-context.c @@ -923,7 +923,7 @@ char *hss_cx_download_user_data( hss_impu_t *impu = NULL; bool barring_indication_presence = true; - int i; + int i, n, m; ogs_assert(user_name); ogs_assert(visited_network_identifier); @@ -1011,7 +1011,160 @@ char *hss_cx_download_user_data( ogs_assert(user_data); } - if(self.sms_over_ims) { + /* IFC data */ + for (n = 0; n < ims_data->num_of_ifc; n++) { + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_ifc_s); + ogs_assert(user_data); + + /* priority */ + user_data = ogs_mstrcatf(user_data, "%s%d%s", + ogs_diam_cx_xml_priority_s, + ims_data->ifc[n].priority, + ogs_diam_cx_xml_priority_e); + ogs_assert(user_data); + + /* trigger point */ + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_tp_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s%d%s", + ogs_diam_cx_xml_cnf_s, + ims_data->ifc[n].trigger_point.condition_type_cnf, + ogs_diam_cx_xml_cnf_e); + ogs_assert(user_data); + + /* SPTs */ + for (m = 0; m < ims_data->ifc[n].trigger_point.num_of_spt; + m++) { + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_spt_s); + ogs_assert(user_data); + + /* condition negated */ + user_data = ogs_mstrcatf(user_data, "%s%d%s", + ogs_diam_cx_xml_condition_negated_s, + ims_data->ifc[n].trigger_point.spt[m]. + condition_negated, + ogs_diam_cx_xml_condition_negated_e); + ogs_assert(user_data); + + /* group */ + user_data = ogs_mstrcatf(user_data, "%s%d%s", + ogs_diam_cx_xml_group_s, + ims_data->ifc[n].trigger_point.spt[m].group, + ogs_diam_cx_xml_group_e); + ogs_assert(user_data); + + /* method */ + if (ims_data->ifc[n].trigger_point.spt[m].type == + OGS_SPT_HAS_METHOD) { + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_method_s, + ims_data->ifc[n].trigger_point.spt[m]. + method, + ogs_diam_cx_xml_method_e); + ogs_assert(user_data); + } + + /* session case */ + if (ims_data->ifc[n].trigger_point.spt[m].type == + OGS_SPT_HAS_SESSION_CASE) { + user_data = ogs_mstrcatf(user_data, "%s%d%s", + ogs_diam_cx_xml_session_case_s, + ims_data->ifc[n].trigger_point.spt[m]. + session_case, + ogs_diam_cx_xml_session_case_e); + ogs_assert(user_data); + } + + /* sip header */ + if (ims_data->ifc[n].trigger_point.spt[m].type == + OGS_SPT_HAS_SIP_HEADER) { + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_sip_hdr_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_header_s, + ims_data->ifc[n].trigger_point.spt[m]. + header, + ogs_diam_cx_xml_header_e); + ogs_assert(user_data); + + if (ims_data->ifc[n].trigger_point.spt[m]. + header_content) { + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_content_s, + ims_data->ifc[n].trigger_point. + spt[m].header_content, + ogs_diam_cx_xml_content_e); + ogs_assert(user_data); + } + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_sip_hdr_e); + ogs_assert(user_data); + } + + /* request uri */ + if (ims_data->ifc[n].trigger_point.spt[m].type == + OGS_SPT_HAS_REQUEST_URI) { + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_req_uri_s, + ims_data->ifc[n].trigger_point.spt[m]. + request_uri, + ogs_diam_cx_xml_req_uri_e); + ogs_assert(user_data); + } + + /* extension */ + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_extension_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_extension_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_spt_e); + ogs_assert(user_data); + } + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_tp_e); + ogs_assert(user_data); + + /* application server */ + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_app_server_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_server_name_s, + ims_data->ifc[n].application_server.server_name, + ogs_diam_cx_xml_server_name_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s%d%s", + ogs_diam_cx_xml_default_handling_s, + ims_data->ifc[n].application_server. + default_handling, + ogs_diam_cx_xml_default_handling_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_app_server_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_ifc_e); + ogs_assert(user_data); + } + + if (self.sms_over_ims) { user_data = ogs_mstrcatf(user_data, "%s", ogs_diam_cx_xml_ifc_s); ogs_assert(user_data); diff --git a/src/hss/hss-s6a-path.c b/src/hss/hss-s6a-path.c index e078f943a..a055c9a9c 100644 --- a/src/hss/hss-s6a-path.c +++ b/src/hss/hss-s6a-path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -974,6 +974,76 @@ static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, ans, OGS_DIAM_S6A_APPLICATION_ID); ogs_assert(ret == 0); + /* + * AVP 628 Supported-Features + * AVP 629 Feature-List-ID: 1 + * AVP 630 Feature-List: (misc subscriber restrictions) + */ + ret = fd_msg_avp_new(ogs_diam_s6a_supported_features, 0, &avp); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_vendor_id, 0, &avpch1); + ogs_assert(ret == 0); + val.i32 = OGS_3GPP_VENDOR_ID; + ret = fd_msg_avp_setvalue (avpch1, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_feature_list_id, 0, &avpch1); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue (avpch1, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_feature_list, 0, &avpch1); + ogs_assert(ret == 0); + val.u32 = 0x0000000b; + ret = fd_msg_avp_setvalue (avpch1, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1); + ogs_assert(ret == 0); + + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* + * AVP 628 Supported-Features + * AVP 629 Feature-List-ID: 2 + * AVP 630 Feature-List: (“NR as Secondary RAT: Supported”) + */ + ret = fd_msg_avp_new(ogs_diam_s6a_supported_features, 0, &avp); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_vendor_id, 0, &avpch1); + ogs_assert(ret == 0); + val.i32 = OGS_3GPP_VENDOR_ID; + ret = fd_msg_avp_setvalue (avpch1, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_feature_list_id, 0, &avpch1); + ogs_assert(ret == 0); + val.i32 = 2; + ret = fd_msg_avp_setvalue (avpch1, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_feature_list, 0, &avpch1); + ogs_assert(ret == 0); + val.u32 = 0x08000001; + ret = fd_msg_avp_setvalue (avpch1, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1); + ogs_assert(ret == 0); + + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + /* Send the answer */ ret = fd_msg_send(msg, NULL, NULL); ogs_assert(ret == 0); diff --git a/src/smf/npcf-build.c b/src/smf/npcf-build.c index 5ccde4fc8..6110b735e 100644 --- a/src/smf/npcf-build.c +++ b/src/smf/npcf-build.c @@ -34,6 +34,7 @@ ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_create( OpenAPI_subscribed_default_qos_t SubsDefQos; OpenAPI_arp_t Arp; OpenAPI_snssai_t sNssai; + OpenAPI_user_location_t ueLocation; ogs_assert(sess); ogs_assert(sess->sm_context_ref); @@ -50,6 +51,7 @@ ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_create( memset(&SmPolicyContextData, 0, sizeof(SmPolicyContextData)); memset(&sNssai, 0, sizeof(sNssai)); memset(&SubsSessAmbr, 0, sizeof(SubsSessAmbr)); + memset(&ueLocation, 0, sizeof(ueLocation)); SmPolicyContextData.supi = smf_ue->supi; if (!SmPolicyContextData.supi) { @@ -67,6 +69,12 @@ ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_create( goto end; } + /* + * TODO: Currently hard-coded to "00000800"; + * replace with dynamic value if needed in future + */ + SmPolicyContextData.chargingcharacteristics = (char *)"00000800"; + /* * Use ogs_sbi_supi_in_vplmn() instead of ogs_sbi_plmn_id_in_vplmn(). * This is because some vendors might not use the full DNN in LBO and @@ -139,6 +147,8 @@ ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_create( goto end; } + SmPolicyContextData.rat_type = OpenAPI_rat_type_NR; + SmPolicyContextData.serving_network = ogs_sbi_build_plmn_id_nid(&sess->serving_plmn_id); if (!SmPolicyContextData.serving_network) { @@ -146,6 +156,28 @@ ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_create( goto end; } + ueLocation.nr_location = ogs_sbi_build_nr_location( + &sess->nr_tai, &sess->nr_cgi); + if (!ueLocation.nr_location) { + ogs_error("ueLocation.nr_location"); + goto end; + } + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(sess->ue_location_timestamp); + if (!ueLocation.nr_location->ue_location_timestamp) { + ogs_error("ueLocation.nr_location->ue_location_timestamp"); + goto end; + } + + SmPolicyContextData.user_location_info = &ueLocation; + + SmPolicyContextData.ue_time_zone = + ogs_sbi_timezone_string(ogs_timezone()); + if (!SmPolicyContextData.ue_time_zone) { + ogs_error("SmPolicyContextData.ue_time_zone"); + goto end; + } + if (sess->ipv4) { SmPolicyContextData.ipv4_address = ogs_ipv4_to_string(sess->ipv4->addr[0]); @@ -263,6 +295,14 @@ end: if (SmPolicyContextData.serving_network) ogs_sbi_free_plmn_id_nid(SmPolicyContextData.serving_network); + if (ueLocation.nr_location) { + if (ueLocation.nr_location->ue_location_timestamp) + ogs_free(ueLocation.nr_location->ue_location_timestamp); + ogs_sbi_free_nr_location(ueLocation.nr_location); + } + if (SmPolicyContextData.ue_time_zone) + ogs_free(SmPolicyContextData.ue_time_zone); + if (sNssai.sd) ogs_free(sNssai.sd);