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 <acetcom@gmail.com>
This commit is contained in:
Matej Gradišar 2024-03-20 23:07:25 +01:00 committed by GitHub
parent ea122da9fc
commit e1820e4e54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 1352 additions and 5 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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(

View File

@ -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)
{

View File

@ -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(

View File

@ -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)

View File

@ -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"

View File

@ -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(

View File

@ -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);

View File

@ -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)
{

View File

@ -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,

View File

@ -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;

View File

@ -21,6 +21,7 @@
#define GMM_HANDLER_H
#include "context.h"
#include "namf-handler.h"
#ifdef __cplusplus
extern "C" {

View File

@ -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 = &registration_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);

View File

@ -40,6 +40,7 @@ libamf_sources = files('''
nnrf-build.c
nnrf-handler.c
namf-build.c
namf-handler.c
sbi-path.c

100
src/amf/namf-build.c Normal file
View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2019,2020 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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;
}

36
src/amf/namf-build.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2019,2020 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

View File

@ -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;
}

View File

@ -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
}

View File

@ -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 */

View File

@ -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,

View File

@ -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)) {