From e1820e4e54647720db7a9ed04dffb554ed7ca793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Gradi=C5=A1ar?= <120629867+matejGradisar@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:07:25 +0100 Subject: [PATCH] UE context transfer (#3052) * [SBI] Handle and store AMF info * [SBI] Add "target GUAMI" discovery option * [SBI] Handle UeContextTransfer request and response messages * [AMF] Handle NF discovery from AMF to AMF * [AMF] Add UE Context Transfer Request/Response from AMF to AMF * [SCP] Handle UeContextTransfer * Follow-up on #3052 * [AMF] force authentication after 'Ue context transfer' for now * [AMF] force authentication after 'Ue context transfer' for now --------- Co-authored-by: Sukchan Lee --- lib/nas/5gs/types.c | 18 ++ lib/nas/5gs/types.h | 2 + lib/proto/types.h | 5 +- lib/sbi/context.c | 38 +++- lib/sbi/context.h | 6 +- lib/sbi/message.c | 120 ++++++++++++- lib/sbi/message.h | 13 ++ lib/sbi/nnrf-handler.c | 103 +++++++++++ lib/sbi/ogs-sbi.h | 2 + lib/sbi/path.c | 27 +++ src/amf/amf-sm.c | 17 ++ src/amf/context.c | 100 +++++++++++ src/amf/context.h | 1 + src/amf/gmm-handler.c | 237 +++++++++++++++++++++++++ src/amf/gmm-handler.h | 1 + src/amf/gmm-sm.c | 121 +++++++++++++ src/amf/meson.build | 1 + src/amf/namf-build.c | 100 +++++++++++ src/amf/namf-build.h | 36 ++++ src/amf/namf-handler.c | 393 +++++++++++++++++++++++++++++++++++++++++ src/amf/namf-handler.h | 4 + src/amf/sbi-path.c | 2 + src/nrf/nnrf-handler.c | 7 + src/scp/sbi-path.c | 3 + 24 files changed, 1352 insertions(+), 5 deletions(-) create mode 100644 src/amf/namf-build.c create mode 100644 src/amf/namf-build.h diff --git a/lib/nas/5gs/types.c b/lib/nas/5gs/types.c index 18d739e7e..ce0d65f51 100644 --- a/lib/nas/5gs/types.c +++ b/lib/nas/5gs/types.c @@ -968,3 +968,21 @@ int ogs_nas_parse_qos_rules( return (int)(rule-first); } + +bool ogs_nas_5gs_guti_is_valid(ogs_nas_5gs_guti_t *guti) +{ + if ((guti->amf_id.region !=0) && + (guti->amf_id.set2 !=0) && + (guti->m_tmsi != 0) && + ((guti->nas_plmn_id.mcc1) !=0 || + (guti->nas_plmn_id.mcc2) !=0 || + (guti->nas_plmn_id.mcc3) !=0) && + ((guti->nas_plmn_id.mnc1) !=0 || + (guti->nas_plmn_id.mnc2) !=0 || + (guti->nas_plmn_id.mnc3) !=0)) { + + return true; + } + + return false; +} diff --git a/lib/nas/5gs/types.h b/lib/nas/5gs/types.h index cc35611db..8262f616b 100644 --- a/lib/nas/5gs/types.h +++ b/lib/nas/5gs/types.h @@ -1192,6 +1192,8 @@ typedef struct ogs_nas_rsn_s { uint8_t value; } __attribute__ ((packed)) ogs_nas_rsn_t; +bool ogs_nas_5gs_guti_is_valid(ogs_nas_5gs_guti_t *guti); + #ifdef __cplusplus } #endif diff --git a/lib/proto/types.h b/lib/proto/types.h index e081bea74..1c186d7f2 100644 --- a/lib/proto/types.h +++ b/lib/proto/types.h @@ -85,6 +85,8 @@ extern "C" { #define OGS_MAX_NUM_OF_ALGORITHM 8 +#define OGS_MAX_5G_GUTI_LEN 28 + #define OGS_MAX_NUM_OF_SERVED_GUMMEI 8 /* maxnoofRATs: 8 */ #define OGS_MAX_NUM_OF_SERVED_GUAMI 256 /* maxnoofServedGUAMIs: 256 */ #define OGS_MAX_NUM_OF_SUPPORTED_TA 256 /* maxnoofTACs: 256 */ @@ -271,10 +273,11 @@ ogs_amf_id_t *ogs_amf_id_build(ogs_amf_id_t *amf_id, #define OGS_PROTECTION_SCHEME_PROFILE_B 2 /************************************ - * SUPI/GPSI */ + * SUPI/GPSI/GUTI */ #define OGS_ID_SUPI_TYPE_IMSI "imsi" #define OGS_ID_GPSI_TYPE_MSISDN "msisdn" #define OGS_ID_SUPI_TYPE_IMEISV "imeisv" +#define OGS_ID_5G_GUTI_TYPE "5g-guti" char *ogs_id_get_type(const char *str); char *ogs_id_get_value(const char *str); diff --git a/lib/sbi/context.c b/lib/sbi/context.c index 87c47a574..daa84d60b 100644 --- a/lib/sbi/context.c +++ b/lib/sbi/context.c @@ -29,6 +29,7 @@ static OGS_POOL(xact_pool, ogs_sbi_xact_t); static OGS_POOL(subscription_spec_pool, ogs_sbi_subscription_spec_t); static OGS_POOL(subscription_data_pool, ogs_sbi_subscription_data_t); static OGS_POOL(smf_info_pool, ogs_sbi_smf_info_t); +static OGS_POOL(amf_info_pool, ogs_sbi_amf_info_t); static OGS_POOL(nf_info_pool, ogs_sbi_nf_info_t); void ogs_sbi_context_init(OpenAPI_nf_type_e nf_type) @@ -61,6 +62,7 @@ void ogs_sbi_context_init(OpenAPI_nf_type_e nf_type) ogs_pool_init(&subscription_data_pool, ogs_app()->pool.subscription); ogs_pool_init(&smf_info_pool, ogs_app()->pool.nf); + ogs_pool_init(&amf_info_pool, ogs_app()->pool.nf); ogs_pool_init(&nf_info_pool, ogs_app()->pool.nf * OGS_MAX_NUM_OF_NF_INFO); @@ -107,6 +109,7 @@ void ogs_sbi_context_final(void) ogs_pool_final(&nf_instance_pool); ogs_pool_final(&nf_service_pool); ogs_pool_final(&smf_info_pool); + ogs_pool_final(&amf_info_pool); ogs_pool_final(&nf_info_pool); @@ -1530,7 +1533,13 @@ ogs_sbi_nf_info_t *ogs_sbi_nf_info_add( static void amf_info_free(ogs_sbi_amf_info_t *amf_info) { - /* Nothing */ + ogs_assert(amf_info); + + amf_info->num_of_guami = 0; + amf_info->num_of_nr_tai = 0; + amf_info->num_of_nr_tai_range = 0; + + ogs_pool_free(&amf_info_pool, amf_info); } static void smf_info_free(ogs_sbi_smf_info_t *smf_info) @@ -1621,6 +1630,26 @@ ogs_sbi_nf_info_t *ogs_sbi_nf_info_find( return NULL; } +bool ogs_sbi_check_amf_info_guami( + ogs_sbi_amf_info_t *amf_info, ogs_guami_t *guami) +{ + int i; + + ogs_assert(amf_info); + ogs_assert(guami); + + for (i = 0; i < amf_info->num_of_guami; i++) { + if ((memcmp(&amf_info->guami[i].amf_id, &guami->amf_id, + sizeof(ogs_amf_id_t)) == 0) && + (memcmp(&amf_info->guami[i].plmn_id, &guami->plmn_id, + OGS_PLMN_ID_LEN) == 0)) { + return true; + } + } + + return false; +} + bool ogs_sbi_check_smf_info_slice( ogs_sbi_smf_info_t *smf_info, ogs_s_nssai_t *s_nssai, char *dnn) { @@ -1964,6 +1993,13 @@ bool ogs_sbi_discovery_option_is_matched( } switch (nf_info->nf_type) { + case OpenAPI_nf_type_AMF: + if (requester_nf_type == OpenAPI_nf_type_AMF && + discovery_option->target_guami && + ogs_sbi_check_amf_info_guami(&nf_info->amf, + discovery_option->target_guami) == false) + return false; + break; case OpenAPI_nf_type_SMF: if (discovery_option->num_of_snssais && discovery_option->dnn && ogs_sbi_check_smf_info_slice(&nf_info->smf, diff --git a/lib/sbi/context.h b/lib/sbi/context.h index 2537403ca..1c22f7c47 100644 --- a/lib/sbi/context.h +++ b/lib/sbi/context.h @@ -341,8 +341,8 @@ typedef struct ogs_sbi_sepp_info_s { } ogs_sbi_sepp_info_t; typedef struct ogs_sbi_amf_info_s { - int amf_set_id; - int amf_region_id; + uint8_t amf_set_id; + uint16_t amf_region_id; int num_of_guami; ogs_guami_t guami[OGS_MAX_NUM_OF_SERVED_GUAMI]; @@ -437,6 +437,8 @@ void ogs_sbi_nf_info_remove_all(ogs_list_t *list); ogs_sbi_nf_info_t *ogs_sbi_nf_info_find( ogs_list_t *list, OpenAPI_nf_type_e nf_type); +bool ogs_sbi_check_amf_info_guami( + ogs_sbi_amf_info_t *amf_info, ogs_guami_t *guami); bool ogs_sbi_check_smf_info_slice( ogs_sbi_smf_info_t *smf_info, ogs_s_nssai_t *s_nssai, char *dnn); bool ogs_sbi_check_smf_info_tai( diff --git a/lib/sbi/message.c b/lib/sbi/message.c index 3e663a144..07f5ce309 100644 --- a/lib/sbi/message.c +++ b/lib/sbi/message.c @@ -189,6 +189,10 @@ void ogs_sbi_message_free(ogs_sbi_message_t *message) OpenAPI_sec_negotiate_req_data_free(message->SecNegotiateReqData); if (message->SecNegotiateRspData) OpenAPI_sec_negotiate_rsp_data_free(message->SecNegotiateRspData); + if (message->UeContextTransferReqData) + OpenAPI_ue_context_transfer_req_data_free(message->UeContextTransferReqData); + if (message->UeContextTransferRspData) + OpenAPI_ue_context_transfer_rsp_data_free(message->UeContextTransferRspData); /* HTTP Part */ for (i = 0; i < message->num_of_part; i++) { @@ -282,6 +286,7 @@ ogs_sbi_request_t *ogs_sbi_build_request(ogs_sbi_message_t *message) int i; ogs_sbi_request_t *request = NULL; OpenAPI_nf_type_e nf_type = OpenAPI_nf_type_NULL; + char sender_timestamp[OGS_SBI_RFC7231_DATE_LEN]; char *max_rsp_time = NULL; @@ -389,6 +394,18 @@ ogs_sbi_request_t *ogs_sbi_build_request(ogs_sbi_message_t *message) OGS_SBI_PARAM_REQUESTER_NF_INSTANCE_ID, discovery_option->requester_nf_instance_id); } + if (discovery_option->target_guami) { + char *v = ogs_sbi_discovery_option_build_guami(discovery_option); + if (v) { + ogs_sbi_header_set(request->http.params, + OGS_SBI_PARAM_GUAMI, v); + ogs_free(v); + } else { + ogs_warn("build failed: service-names[%d:%s]", + discovery_option->num_of_service_names, + discovery_option->service_names[0]); + } + } if (ogs_sbi_self()->discovery_config.no_service_names == false && discovery_option->num_of_service_names) { @@ -817,9 +834,14 @@ int ogs_sbi_parse_request( ogs_sbi_discovery_option_parse_snssais(discovery_option, v); discovery_option_presence = true; } + } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_GUAMI)) { + char *v = ogs_hash_this_val(hi); + if (v) { + ogs_sbi_discovery_option_parse_guami(discovery_option, v); + discovery_option_presence = true; + } } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_DNN)) { char *v = ogs_hash_this_val(hi); - if (v) { ogs_sbi_discovery_option_set_dnn(discovery_option, v); discovery_option_presence = true; @@ -1340,6 +1362,14 @@ static char *build_json(ogs_sbi_message_t *message) item = OpenAPI_sec_negotiate_rsp_data_convertToJSON( message->SecNegotiateRspData); ogs_assert(item); + } else if (message->UeContextTransferReqData) { + item = OpenAPI_ue_context_transfer_req_data_convertToJSON( + message->UeContextTransferReqData); + ogs_assert(item); + } else if (message->UeContextTransferRspData) { + item = OpenAPI_ue_context_transfer_rsp_data_convertToJSON( + message->UeContextTransferRspData); + ogs_assert(item); } if (item) { @@ -2082,6 +2112,27 @@ static int parse_json(ogs_sbi_message_t *message, } break; + CASE(OGS_SBI_RESOURCE_NAME_TRANSFER) + if (message->res_status == 0) { + message->UeContextTransferReqData = + OpenAPI_ue_context_transfer_req_data_parseFromJSON(item); + if (!message->UeContextTransferReqData) { + rv = OGS_ERROR; + ogs_error("JSON parse error"); + } + } else if (message->res_status == OGS_SBI_HTTP_STATUS_OK) { + message->UeContextTransferRspData = + OpenAPI_ue_context_transfer_rsp_data_parseFromJSON(item); + if (!message->UeContextTransferRspData) { + rv = OGS_ERROR; + ogs_error("JSON parse error"); + } + } else { + ogs_error("HTTP ERROR Status : %d", + message->res_status); + } + break; + DEFAULT rv = OGS_ERROR; ogs_error("Unknown resource name [%s]", @@ -2889,6 +2940,8 @@ void ogs_sbi_discovery_option_free( ogs_free(discovery_option->requester_nf_instance_id); if (discovery_option->dnn) ogs_free(discovery_option->dnn); + if (discovery_option->target_guami) + ogs_free(discovery_option->target_guami); for (i = 0; i < discovery_option->num_of_service_names; i++) ogs_free(discovery_option->service_names[i]); @@ -3129,6 +3182,71 @@ void ogs_sbi_discovery_option_parse_snssais( ogs_free(v); } +char *ogs_sbi_discovery_option_build_guami( + ogs_sbi_discovery_option_t *discovery_option) +{ + OpenAPI_guami_t *Guami = NULL; + cJSON *guamiItem = NULL; + char *v = NULL; + + ogs_assert(discovery_option); + ogs_assert(discovery_option->target_guami); + + Guami = ogs_sbi_build_guami(discovery_option->target_guami); + ogs_assert(Guami); + guamiItem = OpenAPI_guami_convertToJSON(Guami); + ogs_assert(guamiItem); + ogs_sbi_free_guami(Guami); + + v = cJSON_PrintUnformatted(guamiItem); + ogs_expect(v); + cJSON_Delete(guamiItem); + + return v; +} + +void ogs_sbi_discovery_option_parse_guami( + ogs_sbi_discovery_option_t *discovery_option, char *guami) +{ + OpenAPI_guami_t *Guami = NULL; + cJSON *guamItem = NULL; + char *v = NULL; + + ogs_assert(discovery_option); + ogs_assert(guami); + + v = ogs_sbi_url_decode(guami); + if (!v) { + ogs_error("ogs_sbi_url_decode() failed : guami[%s]", guami); + return; + } + + guamItem = cJSON_Parse(v); + if (!guamItem) { + ogs_error("Cannot parse guami[%s]", guami); + ogs_free(v); + return; + } + + Guami = OpenAPI_guami_parseFromJSON(guamItem); + + if (Guami) { + ogs_guami_t *ogs_guami = NULL; + + discovery_option->target_guami = ogs_malloc(sizeof(*ogs_guami)); + ogs_assert(discovery_option->target_guami); + + ogs_sbi_parse_guami(discovery_option->target_guami, Guami); + OpenAPI_guami_free(Guami); + } else { + ogs_error("OpenAPI_guami_parseFromJSON() failed : guami[%s]", + guami); + } + cJSON_Delete(guamItem); + + ogs_free(v); +} + void ogs_sbi_discovery_option_set_tai( ogs_sbi_discovery_option_t *discovery_option, ogs_5gs_tai_t *tai) { diff --git a/lib/sbi/message.h b/lib/sbi/message.h index 5ea06e697..b295f3195 100644 --- a/lib/sbi/message.h +++ b/lib/sbi/message.h @@ -124,6 +124,7 @@ extern "C" { #define OGS_SBI_RESOURCE_NAME_UE_CONTEXTS "ue-contexts" #define OGS_SBI_RESOURCE_NAME_N1_N2_MESSAGES "n1-n2-messages" +#define OGS_SBI_RESOURCE_NAME_TRANSFER "transfer" #define OGS_SBI_RESOURCE_NAME_SM_CONTEXT_STATUS "sm-context-status" #define OGS_SBI_RESOURCE_NAME_AM_POLICY_NOTIFY "am-policy-notify" @@ -295,6 +296,8 @@ extern "C" { OGS_SBI_CUSTOM_DISCOVERY_COMMON OGS_SBI_PARAM_REQUESTER_PLMN_LIST #define OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_FEATURES \ OGS_SBI_CUSTOM_DISCOVERY_COMMON OGS_SBI_PARAM_REQUESTER_FEATURES +#define OGS_SBI_CUSTOM_DISCOVERY_GUAMI \ + OGS_SBI_CUSTOM_DISCOVERY_COMMON OGS_SBI_PARAM_GUAMI #define OGS_SBI_CUSTOM_PRODUCER_ID \ OGS_SBI_CUSTOM_3GPP_COMMON "Producer-Id" #define OGS_SBI_CUSTOM_OCI \ @@ -330,6 +333,7 @@ extern "C" { #define OGS_SBI_PARAM_PLMN_ID "plmn-id" #define OGS_SBI_PARAM_SINGLE_NSSAI "single-nssai" #define OGS_SBI_PARAM_SNSSAI "snssai" +#define OGS_SBI_PARAM_GUAMI "guami" #define OGS_SBI_PARAM_SNSSAIS "snssais" #define OGS_SBI_PARAM_TAI "tai" #define OGS_SBI_PARAM_SLICE_INFO_REQUEST_FOR_PDU_SESSION \ @@ -431,6 +435,8 @@ typedef struct ogs_sbi_discovery_option_s { bool tai_presence; ogs_5gs_tai_t tai; + ogs_guami_t *target_guami; + int num_of_target_plmn_list; ogs_plmn_id_t target_plmn_list[OGS_MAX_NUM_OF_PLMN]; int num_of_requester_plmn_list; @@ -542,6 +548,8 @@ typedef struct ogs_sbi_message_s { OpenAPI_smf_registration_t *SmfRegistration; OpenAPI_sec_negotiate_req_data_t *SecNegotiateReqData; OpenAPI_sec_negotiate_rsp_data_t *SecNegotiateRspData; + OpenAPI_ue_context_transfer_req_data_t *UeContextTransferReqData; + OpenAPI_ue_context_transfer_rsp_data_t *UeContextTransferRspData; ogs_sbi_links_t *links; @@ -641,6 +649,11 @@ char *ogs_sbi_discovery_option_build_snssais( void ogs_sbi_discovery_option_parse_snssais( ogs_sbi_discovery_option_t *discovery_option, char *snssais); +char *ogs_sbi_discovery_option_build_guami( + ogs_sbi_discovery_option_t *discovery_option); +void ogs_sbi_discovery_option_parse_guami( + ogs_sbi_discovery_option_t *discovery_option, char *guami); + void ogs_sbi_discovery_option_set_tai( ogs_sbi_discovery_option_t *discovery_option, ogs_5gs_tai_t *tai); char *ogs_sbi_discovery_option_build_tai( diff --git a/lib/sbi/nnrf-handler.c b/lib/sbi/nnrf-handler.c index 36f2b6975..3279cd4af 100644 --- a/lib/sbi/nnrf-handler.c +++ b/lib/sbi/nnrf-handler.c @@ -27,6 +27,8 @@ static void handle_scp_info( ogs_sbi_nf_instance_t *nf_instance, OpenAPI_scp_info_t *ScpInfo); static void handle_sepp_info( ogs_sbi_nf_instance_t *nf_instance, OpenAPI_sepp_info_t *SeppInfo); +static void handle_amf_info( + ogs_sbi_nf_instance_t *nf_instance, OpenAPI_amf_info_t *AmfInfo); void ogs_nnrf_nfm_handle_nf_register( ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_message_t *recvmsg) @@ -256,7 +258,14 @@ void ogs_nnrf_nfm_handle_nf_profile( if (SmfInfoMap && SmfInfoMap->value) handle_smf_info(nf_instance, SmfInfoMap->value); } + if (NFProfile->amf_info) + handle_amf_info(nf_instance, NFProfile->amf_info); + OpenAPI_list_for_each(NFProfile->amf_info_list, node) { + OpenAPI_map_t *AmfInfoMap = node->data; + if (AmfInfoMap && AmfInfoMap->value) + handle_amf_info(nf_instance, AmfInfoMap->value); + } if (NFProfile->scp_info) handle_scp_info(nf_instance, NFProfile->scp_info); if (NFProfile->sepp_info) @@ -653,6 +662,100 @@ static void handle_sepp_info( } } +static void handle_amf_info( + ogs_sbi_nf_instance_t *nf_instance, OpenAPI_amf_info_t *AmfInfo) +{ + ogs_sbi_nf_info_t *nf_info = NULL; + OpenAPI_list_t *GuamiList = NULL; + OpenAPI_guami_t *GuamiAmfInfoItem = NULL; + OpenAPI_list_t *TaiList = NULL; + OpenAPI_tai_t *TaiItem = NULL; + OpenAPI_list_t *TaiRangeList = NULL; + OpenAPI_tai_range_t *TaiRangeItem = NULL; + OpenAPI_list_t *TacRangeList = NULL; + OpenAPI_tac_range_t *TacRangeItem = NULL; + OpenAPI_lnode_t *node = NULL, *node2 = NULL; + + ogs_assert(nf_instance); + ogs_assert(AmfInfo); + + nf_info = ogs_sbi_nf_info_add( + &nf_instance->nf_info_list, OpenAPI_nf_type_AMF); + ogs_assert(nf_info); + + nf_info->amf.amf_set_id = ogs_uint64_from_string(AmfInfo->amf_set_id); + nf_info->amf.amf_region_id = ogs_uint64_from_string(AmfInfo->amf_region_id); + GuamiList = AmfInfo->guami_list; + + OpenAPI_list_for_each(GuamiList, node) { + GuamiAmfInfoItem = node->data; + if (GuamiAmfInfoItem) { + ogs_assert(nf_info->amf.num_of_guami < OGS_MAX_NUM_OF_SERVED_GUAMI); + + if (GuamiAmfInfoItem->amf_id && GuamiAmfInfoItem->plmn_id && + GuamiAmfInfoItem->plmn_id->mnc && + GuamiAmfInfoItem->plmn_id->mcc) { + + ogs_sbi_parse_guami( + &nf_info->amf.guami[nf_info->amf.num_of_guami], + GuamiAmfInfoItem); + nf_info->amf.num_of_guami++; + } + } + } + + TaiList = AmfInfo->tai_list; + OpenAPI_list_for_each(TaiList, node) { + TaiItem = node->data; + if (TaiItem && TaiItem->plmn_id && TaiItem->tac) { + ogs_5gs_tai_t *nr_tai = NULL; + ogs_assert(nf_info->amf.num_of_nr_tai < OGS_MAX_NUM_OF_TAI); + nr_tai = &nf_info->amf.nr_tai[nf_info->amf.num_of_nr_tai]; + ogs_assert(nr_tai); + ogs_sbi_parse_plmn_id(&nr_tai->plmn_id, TaiItem->plmn_id); + nr_tai->tac = ogs_uint24_from_string(TaiItem->tac); + nf_info->amf.num_of_nr_tai++; + } + } + + TaiRangeList = AmfInfo->tai_range_list; + OpenAPI_list_for_each(TaiRangeList, node) { + TaiRangeItem = node->data; + if (TaiRangeItem && TaiRangeItem->plmn_id && + TaiRangeItem->tac_range_list) { + ogs_assert(nf_info->amf.num_of_nr_tai_range < + OGS_MAX_NUM_OF_TAI); + + ogs_sbi_parse_plmn_id( + &nf_info->amf.nr_tai_range + [nf_info->amf.num_of_nr_tai_range].plmn_id, + TaiRangeItem->plmn_id); + + TacRangeList = TaiRangeItem->tac_range_list; + OpenAPI_list_for_each(TacRangeList, node2) { + TacRangeItem = node2->data; + if (TacRangeItem && + TacRangeItem->start && TacRangeItem->end) { + int tac_index = nf_info->amf.nr_tai_range + [nf_info->amf.num_of_nr_tai_range].num_of_tac_range; + ogs_assert(tac_index < OGS_MAX_NUM_OF_TAI); + + nf_info->amf.nr_tai_range + [nf_info->amf.num_of_nr_tai_range].start[tac_index] = + ogs_uint24_from_string(TacRangeItem->start); + nf_info->amf.nr_tai_range + [nf_info->amf.num_of_nr_tai_range].end[tac_index] = + ogs_uint24_from_string(TacRangeItem->end); + + nf_info->amf.nr_tai_range + [nf_info->amf.num_of_nr_tai_range].num_of_tac_range++; + } + } + nf_info->amf.num_of_nr_tai_range++; + } + } +} + static void handle_validity_time( ogs_sbi_subscription_data_t *subscription_data, char *validity_time, const char *action) diff --git a/lib/sbi/ogs-sbi.h b/lib/sbi/ogs-sbi.h index cafa2b728..23cc63e79 100644 --- a/lib/sbi/ogs-sbi.h +++ b/lib/sbi/ogs-sbi.h @@ -84,6 +84,8 @@ #include "model/sec_negotiate_rsp_data.h" #include "model/patch_item.h" #include "model/ue_authentication_ctx.h" +#include "model/ue_context_transfer_req_data.h" +#include "model/ue_context_transfer_rsp_data.h" #include "custom/links.h" diff --git a/lib/sbi/path.c b/lib/sbi/path.c index b4451110b..66dd8ef81 100644 --- a/lib/sbi/path.c +++ b/lib/sbi/path.c @@ -422,6 +422,33 @@ int ogs_sbi_discover_and_send(ogs_sbi_xact_t *xact) discovery_option->tai.tac.v); } + if (discovery_option && discovery_option->target_guami) { + bool rc = false; + char *v = ogs_sbi_discovery_option_build_guami(discovery_option); + ogs_expect(v); + + if (v) { + char *encoded = ogs_sbi_url_encode(v); + ogs_expect(encoded); + + if (encoded) { + ogs_sbi_header_set(request->http.headers, + OGS_SBI_CUSTOM_DISCOVERY_GUAMI, encoded); + ogs_free(encoded); + + rc = true; + } + ogs_free(v); + } + + if (rc == false) + ogs_error("build failed: guami[PLMN_ID:%06x,AMF_ID:%x]", + ogs_plmn_id_hexdump( + &discovery_option->target_guami->plmn_id), + ogs_amf_id_hexdump( + &discovery_option->target_guami->amf_id)); + } + if (discovery_option && discovery_option->requester_features) { char *v = ogs_uint64_to_string( diff --git a/src/amf/amf-sm.c b/src/amf/amf-sm.c index ebd59e6ba..22de3f852 100644 --- a/src/amf/amf-sm.c +++ b/src/amf/amf-sm.c @@ -180,6 +180,22 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) END break; + CASE(OGS_SBI_RESOURCE_NAME_TRANSFER) + SWITCH(sbi_message.h.method) + CASE(OGS_SBI_HTTP_METHOD_POST) + amf_namf_comm_handle_ue_context_transfer_request( + stream, &sbi_message); + break; + DEFAULT + ogs_error("Invalid HTTP method [%s]", + sbi_message.h.method); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_FORBIDDEN, &sbi_message, + "Invalid HTTP method", sbi_message.h.method)); + END + break; + DEFAULT ogs_error("Invalid resource name [%s]", sbi_message.h.resource.component[2]); @@ -375,6 +391,7 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) CASE(OGS_SBI_SERVICE_NAME_NUDM_UECM) CASE(OGS_SBI_SERVICE_NAME_NUDM_SDM) CASE(OGS_SBI_SERVICE_NAME_NPCF_AM_POLICY_CONTROL) + CASE(OGS_SBI_SERVICE_NAME_NAMF_COMM) sbi_xact = e->h.sbi.data; ogs_assert(sbi_xact); diff --git a/src/amf/context.c b/src/amf/context.c index 12db2a8fc..b8e7713b6 100644 --- a/src/amf/context.c +++ b/src/amf/context.c @@ -40,6 +40,7 @@ static void stats_add_ran_ue(void); static void stats_remove_ran_ue(void); static void stats_add_amf_session(void); static void stats_remove_amf_session(void); +static bool amf_namf_comm_parse_guti(ogs_nas_5gs_guti_t *guti, char *ue_context_id); void amf_context_init(void) { @@ -1955,6 +1956,105 @@ amf_ue_t *amf_ue_find_by_message(ogs_nas_5gs_message_t *message) return amf_ue; } +static bool amf_namf_comm_parse_guti(ogs_nas_5gs_guti_t *guti, char *ue_context_id) +{ +#define MIN_LENGTH_OF_MNC 2 +#define MAX_LENGTH_OF_MNC 3 +#define LENGTH_OF_MCC 3 +#define LENGTH_OF_AMF_ID 6 +#define LENGTH_OF_TMSI 8 + + char amf_id_string[LENGTH_OF_AMF_ID + 1]; + char tmsi_string[LENGTH_OF_TMSI + 1]; + char mcc_string[LENGTH_OF_MCC + 1]; + char mnc_string[MAX_LENGTH_OF_MNC + 1]; + OpenAPI_plmn_id_t Plmn_id; + ogs_plmn_id_t plmn_id; + + /* TS29.518 6.1.3.2.2 Guti pattern (27 or 28 characters): + "5g-guti-[0-9]{5,6}[0-9a-fA-F]{14}" */ + + short index = 8; /* start parsing guti after "5g-guti-" */ + + strncpy(mcc_string, &ue_context_id[index], LENGTH_OF_MCC); + mcc_string[LENGTH_OF_MCC] = '\0'; + index += LENGTH_OF_MCC; + + if (strlen(ue_context_id) == OGS_MAX_5G_GUTI_LEN - 1) { + /* mnc is 2 characters long */ + mnc_string[MIN_LENGTH_OF_MNC] = '\0'; + strncpy(mnc_string, &ue_context_id[index], MIN_LENGTH_OF_MNC); + index += MIN_LENGTH_OF_MNC; + } else if (strlen(ue_context_id) == OGS_MAX_5G_GUTI_LEN) { + /* mnc is 3 characters long */ + mnc_string[MAX_LENGTH_OF_MNC] = '\0'; + strncpy(mnc_string, &ue_context_id[index], MAX_LENGTH_OF_MNC); + index += MAX_LENGTH_OF_MNC; + } else { + ogs_error("Invalid Ue context id"); + return false; + } + + strncpy(amf_id_string, &ue_context_id[index], LENGTH_OF_AMF_ID); + amf_id_string[LENGTH_OF_AMF_ID] = '\0'; + index += LENGTH_OF_AMF_ID; + + strncpy(tmsi_string, &ue_context_id[index], LENGTH_OF_TMSI); + tmsi_string[LENGTH_OF_TMSI] = '\0'; + + memset(&Plmn_id, 0, sizeof(Plmn_id)); + Plmn_id.mcc = mcc_string; + Plmn_id.mnc = mnc_string; + + memset(&plmn_id, 0, sizeof(plmn_id)); + ogs_sbi_parse_plmn_id(&plmn_id, &Plmn_id); + ogs_nas_from_plmn_id(&guti->nas_plmn_id, &plmn_id); + ogs_amf_id_from_string(&guti->amf_id, amf_id_string); + + guti->m_tmsi = (u_int32_t)strtol(tmsi_string, NULL, 16); + return true; +} + +amf_ue_t *amf_ue_find_by_ue_context_id(char *ue_context_id) +{ + amf_ue_t *amf_ue = NULL; + + ogs_assert(ue_context_id); + + if (strncmp(ue_context_id, OGS_ID_SUPI_TYPE_IMSI, + strlen(OGS_ID_SUPI_TYPE_IMSI)) == 0) { + + amf_ue = amf_ue_find_by_supi(ue_context_id); + if (!amf_ue) { + ogs_info("[%s] Unknown UE by SUPI", ue_context_id); + return NULL; + } + + } else if (strncmp(ue_context_id, OGS_ID_5G_GUTI_TYPE, + strlen(OGS_ID_5G_GUTI_TYPE)) == 0) { + + ogs_nas_5gs_guti_t guti; + memset(&guti, 0, sizeof(guti)); + + if (amf_namf_comm_parse_guti(&guti, ue_context_id) == false) { + ogs_error("amf_namf_comm_parse_guti() failed"); + return NULL; + } + + amf_ue = amf_ue_find_by_guti(&guti); + if (!amf_ue) { + ogs_info("[%s] Unknown UE by GUTI", ue_context_id); + return NULL; + } + + } else { + ogs_error("Unsupported UE context ID type"); + return NULL; + } + + return amf_ue; +} + void amf_ue_set_suci(amf_ue_t *amf_ue, ogs_nas_5gs_mobile_identity_t *mobile_identity) { diff --git a/src/amf/context.h b/src/amf/context.h index 83ab14b4a..33bcd5d02 100644 --- a/src/amf/context.h +++ b/src/amf/context.h @@ -749,6 +749,7 @@ void amf_ue_fsm_fini(amf_ue_t *amf_ue); amf_ue_t *amf_ue_find_by_guti(ogs_nas_5gs_guti_t *nas_guti); amf_ue_t *amf_ue_find_by_suci(char *suci); amf_ue_t *amf_ue_find_by_supi(char *supi); +amf_ue_t *amf_ue_find_by_ue_context_id(char *ue_context_id); amf_ue_t *amf_ue_find_by_message(ogs_nas_5gs_message_t *message); void amf_ue_set_suci(amf_ue_t *amf_ue, diff --git a/src/amf/gmm-handler.c b/src/amf/gmm-handler.c index 4a57a6aa8..ba2cd86be 100644 --- a/src/amf/gmm-handler.c +++ b/src/amf/gmm-handler.c @@ -1520,6 +1520,243 @@ static ogs_nas_5gmm_cause_t gmm_handle_nas_message_container( return gmm_cause; } +static ogs_nas_5gmm_capability_t + amf_namf_comm_base64_decode_5gmm_capability(char *encoded) +{ + ogs_nas_5gmm_capability_t gmm_capability; + char *gmm_capability_octets_string = NULL; + uint8_t gmm_capability_iei = 0; + + memset(&gmm_capability, 0, sizeof(gmm_capability)); + gmm_capability_octets_string = + (char*) ogs_calloc(sizeof(gmm_capability) + 1, sizeof(char)); + ogs_assert(gmm_capability_octets_string); + + int len = ogs_base64_decode(gmm_capability_octets_string, encoded); + + if (len == 0) + ogs_error("Gmm capability not decoded"); + + ogs_assert(sizeof(gmm_capability_octets_string) <= + sizeof(gmm_capability) + 1); + + gmm_capability_iei = // not copied anywhere for now + gmm_capability_octets_string[0]; + if (gmm_capability_iei != + OGS_NAS_5GS_REGISTRATION_REQUEST_5GMM_CAPABILITY_TYPE) { + ogs_error("Type of 5GMM capability IEI is incorrect"); + } + memcpy(&gmm_capability, + gmm_capability_octets_string + 1, + sizeof(gmm_capability)); + if (gmm_capability_octets_string) { + ogs_free(gmm_capability_octets_string); + } + + return gmm_capability; +} + +static ogs_nas_ue_security_capability_t + amf_namf_comm_base64_decode_ue_security_capability(char *encoded) +{ + ogs_nas_ue_security_capability_t ue_security_capability; + char *ue_security_capability_octets_string = NULL; + uint8_t ue_security_capability_iei = 0; + + memset(&ue_security_capability, 0, sizeof(ue_security_capability)); + ue_security_capability_octets_string = + (char*) ogs_calloc(sizeof(ue_security_capability), sizeof(char)); + ogs_assert(ue_security_capability_octets_string); + + ogs_base64_decode(ue_security_capability_octets_string, encoded); + + ogs_assert(sizeof(ue_security_capability_octets_string) <= + sizeof(ogs_nas_ue_security_capability_t) + 1); + + ue_security_capability_iei = // not copied anywhere for now + ue_security_capability_octets_string[0]; + if (ue_security_capability_iei != + OGS_NAS_5GS_REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_TYPE) { + ogs_error("UE security capability IEI is incorrect"); + } + + memcpy(&ue_security_capability, ue_security_capability_octets_string + 1, + sizeof(ue_security_capability)); + + if (ue_security_capability_octets_string) { + ogs_free(ue_security_capability_octets_string); + } + + return ue_security_capability; +} + +static void amf_namf_comm_decode_ue_mm_context_list( + amf_ue_t *amf_ue, OpenAPI_list_t *MmContextList) { + + OpenAPI_lnode_t *node = NULL; + + OpenAPI_list_for_each(MmContextList, node) { + + OpenAPI_mm_context_t *MmContext = NULL; + OpenAPI_list_t *AllowedNssaiList = NULL; + OpenAPI_lnode_t *node1 = NULL; + OpenAPI_list_t *NssaiMappingList = NULL; + int num_of_s_nssai = 0; + int num_of_nssai_mapping = 0; + + MmContext = node->data; + + AllowedNssaiList = MmContext->allowed_nssai; + NssaiMappingList = MmContext->nssai_mapping_list; + + OpenAPI_list_for_each(AllowedNssaiList, node1) { + OpenAPI_snssai_t *AllowedNssai = node1->data; + + ogs_assert(num_of_s_nssai < OGS_MAX_NUM_OF_SLICE); + + amf_ue->allowed_nssai.s_nssai[num_of_s_nssai].sst = + (uint8_t)AllowedNssai->sst; + amf_ue->allowed_nssai.s_nssai[num_of_s_nssai].sd = + ogs_s_nssai_sd_from_string(AllowedNssai->sd); + + num_of_s_nssai++; + amf_ue->allowed_nssai.num_of_s_nssai = num_of_s_nssai; + } + + OpenAPI_list_for_each(NssaiMappingList, node1) { + OpenAPI_nssai_mapping_t *NssaiMapping = node1->data; + OpenAPI_snssai_t *HSnssai = NssaiMapping->h_snssai; + + ogs_assert(num_of_nssai_mapping < OGS_MAX_NUM_OF_SLICE); + + amf_ue->allowed_nssai.s_nssai[num_of_nssai_mapping]. + mapped_hplmn_sst = HSnssai->sst; + amf_ue->allowed_nssai.s_nssai[num_of_nssai_mapping]. + mapped_hplmn_sd = ogs_s_nssai_sd_from_string(HSnssai->sd); + + num_of_nssai_mapping++; + } + + if (MmContext->ue_security_capability) { + amf_ue->ue_security_capability = + amf_namf_comm_base64_decode_ue_security_capability( + MmContext->ue_security_capability); + } + } +} + +static void amf_namf_comm_decode_ue_session_context_list( + amf_ue_t *amf_ue, OpenAPI_list_t *SessionContextList) +{ + OpenAPI_lnode_t *node = NULL; + + OpenAPI_list_for_each(SessionContextList, node) { + OpenAPI_pdu_session_context_t *PduSessionContext; + PduSessionContext = node->data; + amf_sess_t *sess = NULL; + + sess = amf_sess_add(amf_ue, PduSessionContext->pdu_session_id); + ogs_assert(sess); + + sess->sm_context_ref = PduSessionContext->sm_context_ref; + + if (PduSessionContext->s_nssai) { + memset(&sess->s_nssai, 0, sizeof(sess->s_nssai)); + + sess->s_nssai.sst = PduSessionContext->s_nssai->sst; + sess->s_nssai.sd = ogs_s_nssai_sd_from_string( + PduSessionContext->s_nssai->sd); + } + + if (PduSessionContext->dnn) + sess->dnn = ogs_strdup(PduSessionContext->dnn); + if (PduSessionContext->access_type) + amf_ue->nas.access_type = (int)PduSessionContext->access_type; + } +} + +int amf_namf_comm_handle_ue_context_transfer_response( + ogs_sbi_message_t *recvmsg, amf_ue_t *amf_ue) +{ + OpenAPI_ue_context_t *UeContext = NULL; + +ogs_error("V funkciji amf_namf_comm_handle_ue_context_transfer_response"); + + if (!recvmsg->UeContextTransferRspData) { + ogs_error("No UeContextTransferRspData"); + return OGS_ERROR; + } + + if (!recvmsg->UeContextTransferRspData->ue_context) { + ogs_error("No UE context"); + return OGS_ERROR; + } + + UeContext = recvmsg->UeContextTransferRspData->ue_context; + + if (UeContext->supi) { + amf_ue_set_supi(amf_ue, UeContext->supi); + if (!UeContext->supi_unauth_ind){ + amf_ue->auth_result = OpenAPI_auth_result_AUTHENTICATION_SUCCESS; + } + } + + if (UeContext->pei) { + if (amf_ue->pei) + ogs_free(amf_ue->pei); + amf_ue->pei = ogs_strdup(UeContext->pei); + } + + if (UeContext->sub_ue_ambr) { + amf_ue->ue_ambr.downlink = + ogs_sbi_bitrate_from_string(UeContext->sub_ue_ambr->downlink); + amf_ue->ue_ambr.uplink = + ogs_sbi_bitrate_from_string(UeContext->sub_ue_ambr->uplink); + } + + if (UeContext->seaf_data) { + if (UeContext->seaf_data->ng_ksi->tsc != OpenAPI_sc_type_NULL) { + amf_ue->nas.ue.tsc = + (UeContext->seaf_data->ng_ksi->tsc == OpenAPI_sc_type_NATIVE) ? 0 : 1; + amf_ue->nas.ue.ksi = (uint8_t)UeContext->seaf_data->ng_ksi->ksi; + + ogs_ascii_to_hex( + UeContext->seaf_data->key_amf->key_val, + strlen(UeContext->seaf_data->key_amf->key_val), + amf_ue->kamf, + sizeof(amf_ue->kamf)); + } + } + + if (UeContext->_5g_mm_capability) { + ogs_nas_5gmm_capability_t gmm_capability; + + gmm_capability = amf_namf_comm_base64_decode_5gmm_capability( + UeContext->_5g_mm_capability); + amf_ue->gmm_capability.lte_positioning_protocol_capability = + (bool)gmm_capability.lte_positioning_protocol_capability; + amf_ue->gmm_capability.ho_attach = (bool)gmm_capability.ho_attach; + amf_ue->gmm_capability.s1_mode = (bool)gmm_capability.s1_mode; + } + + if (UeContext->pcf_id) { + /* TODO */ + } + + /* TODO UeContext->pcfAmPolicyUri */ + /* TODO UeContext->pcfUePolicyUri */ + + if (UeContext->mm_context_list) + amf_namf_comm_decode_ue_mm_context_list(amf_ue, UeContext->mm_context_list); + + if (UeContext->session_context_list) + amf_namf_comm_decode_ue_session_context_list(amf_ue, UeContext->session_context_list); + + /* TODO ueRadioCapability */ + + return OGS_OK; +} + static uint8_t gmm_cause_from_access_control(ogs_plmn_id_t *plmn_id) { int i; diff --git a/src/amf/gmm-handler.h b/src/amf/gmm-handler.h index d89dd9f83..4cedf2077 100644 --- a/src/amf/gmm-handler.h +++ b/src/amf/gmm-handler.h @@ -21,6 +21,7 @@ #define GMM_HANDLER_H #include "context.h" +#include "namf-handler.h" #ifdef __cplusplus extern "C" { diff --git a/src/amf/gmm-sm.c b/src/amf/gmm-sm.c index 8972db01f..522cf774a 100644 --- a/src/amf/gmm-sm.c +++ b/src/amf/gmm-sm.c @@ -27,8 +27,10 @@ #include "nsmf-handler.h" #include "nudm-handler.h" #include "npcf-handler.h" +#include "namf-handler.h" #include "sbi-path.h" #include "amf-sm.h" +#include "namf-build.h" #undef OGS_LOG_DOMAIN #define OGS_LOG_DOMAIN __gmm_log_domain @@ -62,6 +64,7 @@ void gmm_state_de_registered(ogs_fsm_t *s, amf_event_t *e) { amf_ue_t *amf_ue = NULL; amf_sess_t *sess = NULL; + ran_ue_t *ran_ue = NULL; ogs_sbi_message_t *sbi_message = NULL; @@ -546,6 +549,61 @@ void gmm_state_de_registered(ogs_fsm_t *s, amf_event_t *e) END break; + CASE(OGS_SBI_SERVICE_NAME_NAMF_COMM) + SWITCH(sbi_message->h.resource.component[0]) + CASE(OGS_SBI_RESOURCE_NAME_UE_CONTEXTS) + SWITCH(sbi_message->h.resource.component[2]) + CASE(OGS_SBI_RESOURCE_NAME_TRANSFER) + + ran_ue = ran_ue_cycle(amf_ue->ran_ue); + ogs_assert(ran_ue); + + if (sbi_message->res_status == OGS_SBI_HTTP_STATUS_OK) { + r = amf_namf_comm_handle_ue_context_transfer_response(sbi_message, amf_ue); + ogs_expect(r == OGS_OK); + } + + int xact_count = amf_sess_xact_count(amf_ue); + + if (!AMF_UE_HAVE_SUCI(amf_ue)) { + CLEAR_AMF_UE_TIMER(amf_ue->t3570); + r = nas_5gs_send_identity_request(amf_ue); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + break; + } + + amf_sbi_send_release_all_sessions( + amf_ue, AMF_RELEASE_SM_CONTEXT_NO_STATE); + + if (!AMF_SESSION_RELEASE_PENDING(amf_ue) && + amf_sess_xact_count(amf_ue) == xact_count) { + r = amf_ue_sbi_discover_and_send( + OGS_SBI_SERVICE_TYPE_NAUSF_AUTH, NULL, + amf_nausf_auth_build_authenticate, + amf_ue, 0, NULL); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + } + + OGS_FSM_TRAN(s, &gmm_state_authentication); + + break; + + DEFAULT + ogs_error("Invalid resource name [%s]", + sbi_message->h.resource.component[2]); + ogs_assert_if_reached(); + END + break; + + DEFAULT + ogs_error("Invalid resource name [%s]", + sbi_message->h.resource.component[0]); + ogs_assert_if_reached(); + END + break; + DEFAULT ogs_error("Invalid service name [%s]", sbi_message->h.service.name); ogs_assert_if_reached(); @@ -1136,6 +1194,9 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, amf_sess_t *sess = NULL; ogs_nas_5gs_message_t *nas_message = NULL; ogs_nas_security_header_type_t h; + ogs_nas_5gs_registration_request_t *registration_request = NULL; + ogs_nas_5gs_mobile_identity_header_t *mobile_identity_header = NULL; + ogs_nas_5gs_mobile_identity_t *mobile_identity = NULL; ogs_assert(e); @@ -1194,6 +1255,66 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, break; } + registration_request = &nas_message->gmm.registration_request; + mobile_identity = ®istration_request->mobile_identity; + mobile_identity_header = + (ogs_nas_5gs_mobile_identity_header_t *)mobile_identity->buffer; + + /* Check if registration is done with GUTI */ + if (mobile_identity_header && mobile_identity_header->type == + OGS_NAS_5GS_MOBILE_IDENTITY_GUTI && + ogs_nas_5gs_guti_is_valid(&amf_ue->current.guti)) { + + /* + * TS 23.502 + * 4.2.2.2.2 General Registration + * (Without UDSF Deployment): If the UE's 5G-GUTI was included in the + * Registration Request and the serving AMF has changed since last + * Registration procedure, the new AMF may invoke the + * Namf_Communication_UEContextTransfer service operation on the + * old AMF including the complete Registration Request NAS message, + * which may be integrity protected, as well as the Access Type, + * to request the UE's SUPI and UE Context. See clause 5.2.2.2.2 + * for details of this service operation. + */ + + int state = e->h.sbi.state; + bool serving_guami = false; + int i; + + /* Compare all serving guamis with guami from UE's GUTI */ + for (i = 0; i < amf_self()->num_of_served_guami; i++) { + if ((memcmp(&amf_self()->served_guami[i].amf_id, + &amf_ue->current.guti.amf_id, + sizeof(ogs_amf_id_t)) == 0) && + (memcmp(&amf_self()->served_guami[i].plmn_id, + &amf_ue->current.guti.nas_plmn_id, + OGS_PLMN_ID_LEN) == 0)) { + + serving_guami = true; + break; + } + } + if (!serving_guami) { + /* Guami from UE is not this AMF's serving guami - send UEContextTransfer */ + ogs_sbi_discovery_option_t *discovery_option = NULL; + + discovery_option = ogs_sbi_discovery_option_new(); + ogs_assert(discovery_option); + + memcpy(discovery_option->target_guami, + amf_ue->guami, sizeof(ogs_guami_t)); + + int r = amf_ue_sbi_discover_and_send( + OGS_SBI_SERVICE_TYPE_NAMF_COMM, discovery_option, + amf_namf_comm_build_ue_context_transfer, + amf_ue, state, nas_message); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + break; + } + } + if (!AMF_UE_HAVE_SUCI(amf_ue)) { CLEAR_AMF_UE_TIMER(amf_ue->t3570); r = nas_5gs_send_identity_request(amf_ue); diff --git a/src/amf/meson.build b/src/amf/meson.build index 3ec5041e5..88ab0bc44 100644 --- a/src/amf/meson.build +++ b/src/amf/meson.build @@ -40,6 +40,7 @@ libamf_sources = files(''' nnrf-build.c nnrf-handler.c + namf-build.c namf-handler.c sbi-path.c diff --git a/src/amf/namf-build.c b/src/amf/namf-build.c new file mode 100644 index 000000000..72d423de6 --- /dev/null +++ b/src/amf/namf-build.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2019,2020 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 . + */ + +#include "namf-build.h" + +static char* ogs_guti_to_string(amf_ue_t *amf_ue) +{ + ogs_plmn_id_t plmn_id; + char plmn_id_buff[OGS_PLMNIDSTRLEN]; + char *amf_id = NULL; + char *tmsi = NULL; + char *guti = NULL; + + memset(&plmn_id, 0, sizeof(plmn_id)); + ogs_nas_to_plmn_id(&plmn_id, &amf_ue->current.guti.nas_plmn_id); + + amf_id = ogs_amf_id_to_string(&amf_ue->current.guti.amf_id); + tmsi = ogs_uint32_to_0string(*(amf_ue->current.m_tmsi)); + + guti = ogs_msprintf("5g-guti-%s%s%s", + ogs_plmn_id_to_string(&plmn_id, plmn_id_buff), + amf_id, + tmsi); + + /* TS29.518 6.1.3.2.2 Guti pattern (27 or 28 characters): + "5g-guti-[0-9]{5,6}[0-9a-fA-F]{14}" */ + ogs_assert(strlen(guti) == (OGS_MAX_5G_GUTI_LEN - 1) || + (strlen(guti)) == OGS_MAX_5G_GUTI_LEN); + + ogs_free(amf_id); + ogs_free(tmsi); + + return guti; +} + +static char* amf_ue_to_context_id(amf_ue_t *amf_ue) +{ + char *ue_context_id = NULL; + + if (amf_ue->supi) { + ue_context_id = ogs_strdup(amf_ue->supi); + } else { + ue_context_id = ogs_guti_to_string(amf_ue); + } + + return ue_context_id; +} + +ogs_sbi_request_t *amf_namf_comm_build_ue_context_transfer( + amf_ue_t *amf_ue, void *data) +{ + ogs_sbi_message_t message; + ogs_sbi_request_t *request = NULL; + OpenAPI_ue_context_transfer_req_data_t UeContextTransferReqData; + char *ue_context_id = NULL; + + ogs_assert(amf_ue); + + ue_context_id = amf_ue_to_context_id(amf_ue); + ogs_assert(ue_context_id); + + memset(&UeContextTransferReqData, 0, sizeof(UeContextTransferReqData)); + UeContextTransferReqData.access_type = amf_ue->nas.access_type; + UeContextTransferReqData.reason = amf_ue->nas.registration.value; + + memset(&message, 0, sizeof(message)); + message.h.method = (char *)OGS_SBI_HTTP_METHOD_POST; + message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NAMF_COMM; + message.h.api.version = (char *)OGS_SBI_API_V1; + message.h.resource.component[0] = + (char *)OGS_SBI_RESOURCE_NAME_UE_CONTEXTS; + message.h.resource.component[1] = ue_context_id; + message.h.resource.component[2] = + (char *)OGS_SBI_RESOURCE_NAME_TRANSFER; + message.UeContextTransferReqData = &UeContextTransferReqData; + + request = ogs_sbi_build_request(&message); + ogs_expect(request); + + if (ue_context_id) + ogs_free(ue_context_id); + + return request; +} diff --git a/src/amf/namf-build.h b/src/amf/namf-build.h new file mode 100644 index 000000000..92cd716dc --- /dev/null +++ b/src/amf/namf-build.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019,2020 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 AMF_NAMF_BUILD_H +#define AMF_NAMF_BUILD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "context.h" + +ogs_sbi_request_t *amf_namf_comm_build_ue_context_transfer( + amf_ue_t *amf_ue, void *data); + +#ifdef __cplusplus +} +#endif + +#endif /* AMF_NAMF_BUILD_H */ diff --git a/src/amf/namf-handler.c b/src/amf/namf-handler.c index 43410c8dd..2cc8c46d4 100644 --- a/src/amf/namf-handler.c +++ b/src/amf/namf-handler.c @@ -1079,3 +1079,396 @@ cleanup: return OGS_OK; } + +static char *amf_namf_comm_base64_encode_ue_security_capability( + ogs_nas_ue_security_capability_t ue_security_capability) +{ + char *enc = NULL; + int enc_len = 0; + + char num_of_octets = + ue_security_capability.length + + sizeof(ue_security_capability.length) + + sizeof((uint8_t)OGS_NAS_5GS_REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_TYPE); + /* Security guarantee */ + num_of_octets = ogs_min( + num_of_octets, sizeof(ue_security_capability) + 1); + /* + * size [sizeof(ue_security_capability) + 1] is a sum of lengths: + * ue_security_capability (9 octets) + + * type (1 octet) + */ + char security_octets_string[sizeof(ue_security_capability) + 1]; + + enc_len = ogs_base64_encode_len(num_of_octets); + + enc = ogs_malloc(enc_len); + ogs_assert(enc); + memset(enc, 0, sizeof(*enc)); + + security_octets_string[0] = + (uint8_t)OGS_NAS_5GS_REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_TYPE; + memcpy(security_octets_string + 1, &ue_security_capability, num_of_octets); + ogs_base64_encode(enc , security_octets_string, num_of_octets); + + return enc; +} + +static char *amf_namf_comm_base64_encode_5gmm_capability(amf_ue_t *amf_ue) +{ + ogs_nas_5gmm_capability_t nas_gmm_capability; + int enc_len = 0; + char *enc = NULL; + + memset(&nas_gmm_capability, 0, sizeof(nas_gmm_capability)); + + /* 1 octet is mandatory, n.3 from TS 24.501 V16.12.0, 9.11.3.1 */ + nas_gmm_capability.length = 1; + nas_gmm_capability.lte_positioning_protocol_capability = + amf_ue->gmm_capability.lte_positioning_protocol_capability; + nas_gmm_capability.ho_attach = amf_ue->gmm_capability.ho_attach; + nas_gmm_capability.s1_mode = amf_ue->gmm_capability.s1_mode; + + uint8_t num_of_octets = + nas_gmm_capability.length + + sizeof(nas_gmm_capability.length) + + sizeof((uint8_t)OGS_NAS_5GS_REGISTRATION_REQUEST_5GMM_CAPABILITY_TYPE); + + /* Security guarantee. + 1 stands for 5GMM capability IEI */ + num_of_octets = ogs_min( + num_of_octets, sizeof(ogs_nas_5gmm_capability_t) + 1); + + char gmm_capability_octets_string[sizeof(ogs_nas_5gmm_capability_t) + 1]; + + enc_len = ogs_base64_encode_len(num_of_octets); + enc = ogs_malloc(enc_len); + ogs_assert(enc); + memset(enc, 0, sizeof(*enc)); + + /* Fill the bytes of data */ + gmm_capability_octets_string[0] = + (uint8_t)OGS_NAS_5GS_REGISTRATION_REQUEST_5GMM_CAPABILITY_TYPE; + memcpy(gmm_capability_octets_string + 1, &nas_gmm_capability, num_of_octets); + ogs_base64_encode(enc, gmm_capability_octets_string, num_of_octets); + + return enc; +} + +static OpenAPI_list_t *amf_namf_comm_encode_ue_session_context_list(amf_ue_t *amf_ue) +{ + ogs_assert(amf_ue); + + amf_sess_t *sess = NULL; + OpenAPI_list_t *PduSessionList = NULL; + OpenAPI_pdu_session_context_t *PduSessionContext = NULL; + OpenAPI_snssai_t *sNSSAI = NULL; + + PduSessionList = OpenAPI_list_create(); + ogs_assert(PduSessionList); + + ogs_list_for_each(&amf_ue->sess_list, sess) { + PduSessionContext = ogs_calloc(1, sizeof(*PduSessionContext)); + ogs_assert(PduSessionContext); + + sNSSAI = ogs_calloc(1, sizeof(*sNSSAI)); + ogs_assert(sNSSAI); + + PduSessionContext->pdu_session_id = sess->psi; + PduSessionContext->sm_context_ref = sess->sm_context_ref; + + sNSSAI->sst = sess->s_nssai.sst; + sNSSAI->sd = ogs_s_nssai_sd_to_string(sess->s_nssai.sd); + PduSessionContext->s_nssai = sNSSAI; + + PduSessionContext->dnn = sess->dnn; + PduSessionContext->access_type = (OpenAPI_access_type_e)amf_ue->nas.access_type; + + OpenAPI_list_add(PduSessionList, PduSessionContext); + } + + return PduSessionList; +} + +static OpenAPI_list_t *amf_namf_comm_encode_ue_mm_context_list(amf_ue_t *amf_ue) +{ + OpenAPI_list_t *MmContextList = NULL; + OpenAPI_mm_context_t *MmContext = NULL; + + int i; + + ogs_assert(amf_ue); + + + MmContextList = OpenAPI_list_create(); + ogs_assert(MmContextList); + + MmContext = ogs_malloc(sizeof(*MmContext)); + ogs_assert(MmContext); + memset(MmContext, 0, sizeof(*MmContext)); + + MmContext->access_type = (OpenAPI_access_type_e)amf_ue->nas.access_type; + + if ((OpenAPI_ciphering_algorithm_e)amf_ue->selected_enc_algorithm && + (OpenAPI_integrity_algorithm_e)amf_ue->selected_int_algorithm) { + + OpenAPI_nas_security_mode_t *NasSecurityMode; + + NasSecurityMode = ogs_calloc(1, sizeof(*NasSecurityMode)); + ogs_assert(NasSecurityMode); + + NasSecurityMode->ciphering_algorithm = + (OpenAPI_ciphering_algorithm_e)amf_ue->selected_enc_algorithm; + NasSecurityMode->integrity_algorithm = + (OpenAPI_integrity_algorithm_e)amf_ue->selected_int_algorithm; + + MmContext->nas_security_mode = NasSecurityMode; + } + + if (amf_ue->dl_count > 0) { + MmContext->is_nas_downlink_count = true; + MmContext->nas_downlink_count = amf_ue->dl_count; + } + + if (amf_ue->ul_count.i32 > 0) { + MmContext->is_nas_uplink_count = true; + MmContext->nas_uplink_count = amf_ue->ul_count.i32; + } + + if (amf_ue->ue_security_capability.length > 0) { + MmContext->ue_security_capability = + amf_namf_comm_base64_encode_ue_security_capability( + amf_ue->ue_security_capability); + } + + if (amf_ue->allowed_nssai.num_of_s_nssai) { + + OpenAPI_list_t *AllowedNssaiList; + OpenAPI_list_t *NssaiMappingList; + + /* This IE shall be present if the source AMF and the target AMF are + * in the same PLMN and if available. When present, this IE shall + * contain the allowed NSSAI for the access type. + */ + AllowedNssaiList = OpenAPI_list_create(); + + /* This IE shall be present if the source AMF and the target AMF are + * in the same PLMN and if available. When present, this IE shall + * contain the mapping of the allowed NSSAI for the UE. + */ + NssaiMappingList = OpenAPI_list_create(); + + ogs_assert(AllowedNssaiList); + ogs_assert(NssaiMappingList); + + for (i = 0; i < amf_ue->allowed_nssai.num_of_s_nssai; i++) { + OpenAPI_snssai_t *AllowedNssai; + + AllowedNssai = ogs_calloc(1, sizeof(*AllowedNssai)); + ogs_assert(AllowedNssai); + + AllowedNssai->sst = amf_ue->allowed_nssai.s_nssai[i].sst; + AllowedNssai->sd = ogs_s_nssai_sd_to_string( + amf_ue->allowed_nssai.s_nssai[i].sd); + + OpenAPI_list_add(AllowedNssaiList, AllowedNssai); + } + + for (i = 0; i < amf_ue->allowed_nssai.num_of_s_nssai; i++) { + OpenAPI_nssai_mapping_t *NssaiMapping; + OpenAPI_snssai_t *HSnssai; + OpenAPI_snssai_t *MappedSnssai; + + NssaiMapping = ogs_calloc(1, sizeof(*NssaiMapping)); + ogs_assert(NssaiMapping); + + /* Indicates the S-NSSAI in home PLMN */ + HSnssai = ogs_calloc(1, sizeof(*HSnssai)); + ogs_assert(HSnssai); + + HSnssai->sst = + amf_ue->allowed_nssai.s_nssai[i].mapped_hplmn_sst; + HSnssai->sd = + ogs_s_nssai_sd_to_string( + amf_ue->allowed_nssai.s_nssai[i].mapped_hplmn_sd); + NssaiMapping->h_snssai = HSnssai; + + /* Indicates the mapped S-NSSAI in the serving PLMN */ + MappedSnssai = ogs_calloc(1, sizeof(*MappedSnssai)); + ogs_assert(MappedSnssai); + + /* MappedSnssai must be defined, else + "nssaiMappingList" will not convert to json*/ + MappedSnssai->sst = 0; + MappedSnssai->sd = ogs_strdup(""); + NssaiMapping->mapped_snssai = MappedSnssai; + + OpenAPI_list_add(NssaiMappingList, NssaiMapping); + } + + MmContext->allowed_nssai = AllowedNssaiList; + MmContext->nssai_mapping_list = NssaiMappingList; + } + + OpenAPI_list_add(MmContextList, MmContext); + + return MmContextList; +} + +int amf_namf_comm_handle_ue_context_transfer_request( + ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg) +{ + ogs_sbi_response_t *response = NULL; + ogs_sbi_message_t sendmsg; + amf_ue_t *amf_ue = NULL; + + OpenAPI_ambr_t *UeAmbr = NULL; + OpenAPI_list_t *MmContextList = NULL; + OpenAPI_mm_context_t *MmContext = NULL; + OpenAPI_list_t *SessionContextList = NULL; + OpenAPI_pdu_session_context_t *PduSessionContext = NULL; + OpenAPI_lnode_t *node = NULL; + OpenAPI_ue_context_t UeContext; + OpenAPI_seaf_data_t SeafData; + OpenAPI_ng_ksi_t Ng_ksi; + OpenAPI_key_amf_t Key_amf; + OpenAPI_sc_type_e Tsc_type; + + OpenAPI_ue_context_transfer_rsp_data_t UeContextTransferRspData; + + char *ue_context_id = NULL; + char *encoded_gmm_capability = NULL; + int status = OGS_SBI_HTTP_STATUS_OK; + char hxkamf_string[OGS_KEYSTRLEN(OGS_SHA256_DIGEST_SIZE)]; + char *strerror = NULL; + + ogs_assert(stream); + ogs_assert(recvmsg); + + memset(&UeContextTransferRspData, 0, sizeof(UeContextTransferRspData)); + memset(&UeContext, 0, sizeof(UeContext)); + UeContextTransferRspData.ue_context = &UeContext; + + memset(&sendmsg, 0, sizeof(sendmsg)); + sendmsg.UeContextTransferRspData = &UeContextTransferRspData; + + ue_context_id = recvmsg->h.resource.component[1]; + if (!ue_context_id) { + status = OGS_SBI_HTTP_STATUS_BAD_REQUEST; + strerror = ogs_msprintf("No UE context ID"); + goto cleanup; + } + + amf_ue = amf_ue_find_by_ue_context_id(ue_context_id); + if (!amf_ue) { + status = OGS_SBI_HTTP_STATUS_NOT_FOUND; + strerror = ogs_msprintf("CONTEXT_NOT_FOUND"); + goto cleanup; + } + + if (amf_ue->supi) { + UeContext.supi = amf_ue->supi; + if (amf_ue->auth_result != + OpenAPI_auth_result_AUTHENTICATION_SUCCESS) { + UeContext.is_supi_unauth_ind = true; + UeContext.supi_unauth_ind = amf_ue->auth_result; + } + } + + /* TODO UeContext.gpsi_list */ + + if (amf_ue->pei) { + UeContext.pei = amf_ue->pei; + } + + if ((amf_ue->ue_ambr.uplink > 0) || (amf_ue->ue_ambr.downlink > 0)) { + UeAmbr = ogs_malloc(sizeof(*UeAmbr)); + ogs_assert(UeAmbr); + memset(UeAmbr, 0, sizeof(*UeAmbr)); + + if (amf_ue->ue_ambr.uplink > 0) + UeAmbr->uplink = ogs_sbi_bitrate_to_string( + amf_ue->ue_ambr.uplink, OGS_SBI_BITRATE_KBPS); + if (amf_ue->ue_ambr.downlink > 0) + UeAmbr->downlink = ogs_sbi_bitrate_to_string( + amf_ue->ue_ambr.downlink, OGS_SBI_BITRATE_KBPS); + UeContext.sub_ue_ambr = UeAmbr; + } + + if ((amf_ue->nas.ue.ksi != 0) && (amf_ue->nas.ue.tsc != 0)) { + memset(&SeafData, 0, sizeof(SeafData)); + Tsc_type = (amf_ue->nas.ue.tsc == 0) ? + OpenAPI_sc_type_NATIVE : OpenAPI_sc_type_MAPPED; + + memset(&Ng_ksi, 0, sizeof(Ng_ksi)); + SeafData.ng_ksi = &Ng_ksi; + Ng_ksi.tsc = Tsc_type; + Ng_ksi.ksi = (int)amf_ue->nas.ue.ksi; + + memset(&Key_amf, 0, sizeof(Key_amf)); + SeafData.key_amf = &Key_amf; + OpenAPI_key_amf_type_e temp_key_type = + (OpenAPI_key_amf_type_e)OpenAPI_key_amf_type_KAMF; + Key_amf.key_type = temp_key_type; + ogs_hex_to_ascii(amf_ue->kamf, sizeof(amf_ue->kamf), + hxkamf_string, sizeof(hxkamf_string)); + Key_amf.key_val = hxkamf_string; + UeContext.seaf_data = &SeafData; + } + + encoded_gmm_capability = amf_namf_comm_base64_encode_5gmm_capability(amf_ue); + UeContext._5g_mm_capability = encoded_gmm_capability; + + UeContext.pcf_id = amf_ue->sbi.service_type_array[ + OGS_SBI_SERVICE_TYPE_NPCF_AM_POLICY_CONTROL].nf_instance->id; + + /* TODO UeContext.pcfAmPolicyUri */ + /* TODO UeContext.pcfUePolicyUri */ + + MmContextList = amf_namf_comm_encode_ue_mm_context_list(amf_ue); + UeContext.mm_context_list = MmContextList; + + if (recvmsg->UeContextTransferReqData->reason == + OpenAPI_transfer_reason_MOBI_REG) { + SessionContextList = amf_namf_comm_encode_ue_session_context_list(amf_ue); + UeContext.session_context_list = SessionContextList; + } + + /* TODO ueRadioCapability */ + + response = ogs_sbi_build_response(&sendmsg, status); + ogs_assert(response); + ogs_assert(true == ogs_sbi_server_send_response(stream, response)); + + if (encoded_gmm_capability) + ogs_free(encoded_gmm_capability); + if (UeAmbr) + OpenAPI_ambr_free(UeAmbr); + + if (SessionContextList) { + OpenAPI_list_for_each(SessionContextList, node) { + PduSessionContext = node->data; + OpenAPI_pdu_session_context_free(PduSessionContext); + } + OpenAPI_list_free(SessionContextList); + } + + if (MmContextList) { + OpenAPI_list_for_each(MmContextList, node) { + MmContext = node->data; + OpenAPI_mm_context_free(MmContext); + } + OpenAPI_list_free(MmContextList); + } + + return OGS_OK; + +cleanup: + ogs_assert(strerror); + ogs_error("%s", strerror); + + ogs_assert(true == + ogs_sbi_server_send_error(stream, status, NULL, strerror, NULL)); + ogs_free(strerror); + + return OGS_ERROR; +} diff --git a/src/amf/namf-handler.h b/src/amf/namf-handler.h index 9581260ec..19bcd203c 100644 --- a/src/amf/namf-handler.h +++ b/src/amf/namf-handler.h @@ -34,6 +34,10 @@ int amf_namf_callback_handle_dereg_notify( ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg); int amf_namf_callback_handle_sdm_data_change_notify( ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg); +int amf_namf_comm_handle_ue_context_transfer_request( + ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg); +int amf_namf_comm_handle_ue_context_transfer_response( + ogs_sbi_message_t *recvmsg, amf_ue_t *amf_ue); #ifdef __cplusplus } diff --git a/src/amf/sbi-path.c b/src/amf/sbi-path.c index 409298850..0f5e4a3cb 100644 --- a/src/amf/sbi-path.c +++ b/src/amf/sbi-path.c @@ -36,6 +36,7 @@ int amf_sbi_open(void) ogs_sbi_nf_instance_build_default(nf_instance); ogs_sbi_nf_instance_add_allowed_nf_type(nf_instance, OpenAPI_nf_type_SCP); ogs_sbi_nf_instance_add_allowed_nf_type(nf_instance, OpenAPI_nf_type_SMF); + ogs_sbi_nf_instance_add_allowed_nf_type(nf_instance, OpenAPI_nf_type_AMF); /* Build NF service information. It will be transmitted to NRF. */ if (ogs_sbi_nf_service_is_available(OGS_SBI_SERVICE_NAME_NAMF_COMM)) { @@ -45,6 +46,7 @@ int amf_sbi_open(void) ogs_sbi_nf_service_add_version( service, OGS_SBI_API_V1, OGS_SBI_API_V1_0_0, NULL); ogs_sbi_nf_service_add_allowed_nf_type(service, OpenAPI_nf_type_SMF); + ogs_sbi_nf_service_add_allowed_nf_type(service, OpenAPI_nf_type_AMF); } /* Initialize NRF NF Instance */ diff --git a/src/nrf/nnrf-handler.c b/src/nrf/nnrf-handler.c index 64649a86a..1368e2fcb 100644 --- a/src/nrf/nnrf-handler.c +++ b/src/nrf/nnrf-handler.c @@ -830,6 +830,13 @@ bool nrf_nnrf_handle_nf_discover( &discovery_option->tai.plmn_id), discovery_option->tai.tac.v); } + if (discovery_option->target_guami) { + ogs_debug("guami[PLMN_ID:%06x,AMF_ID:%x]", + ogs_plmn_id_hexdump( + &discovery_option->target_guami->plmn_id), + ogs_amf_id_hexdump( + &discovery_option->target_guami->amf_id)); + } if (discovery_option->num_of_target_plmn_list) { for (i = 0; i < discovery_option->num_of_target_plmn_list; i++) ogs_debug("[%d] target-plmn-list[MCC:%03d,MNC:%03d]", i, diff --git a/src/scp/sbi-path.c b/src/scp/sbi-path.c index cca4fb4a4..1a16953e0 100644 --- a/src/scp/sbi-path.c +++ b/src/scp/sbi-path.c @@ -225,6 +225,9 @@ static int request_handler(ogs_sbi_request_t *request, void *data) } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_SNSSAIS)) { if (val) ogs_sbi_discovery_option_parse_snssais(discovery_option, val); + } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_GUAMI)) { + if (val) + ogs_sbi_discovery_option_parse_guami(discovery_option, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_DNN)) { ogs_sbi_discovery_option_set_dnn(discovery_option, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_TAI)) {