Send deregistration event from AMF to UDM (#1599)

* [SBI] Fix converting PatchItem to JSON

* [UDR] Add support for endpoint for patching subscription data

Add support for PATCH HTTP method for the following endpoint:
/subscription-data/{ueId}/context-data/amf-3gpp-access

Currently does not change any data in the database.

* [UDM] Add support for endpoint for patching subscription data

Add support for the following endpoint, HTTP PATCH method:
/nudm-uecm/v1/{ueId}/registrations/amf-3gpp-access

The endpoint is used when UE deregisters from the core, and AMF
sends a subscription modification request with "purgeFlag" set.

* [UDM] Add check for same GUAMI when patching subscription data

* [AMF] Send deregistration event to UDM

When UE sends deregistration request, AMF needs to send a
Nudm_UECM_Deregistration request to UDM.
The order of requests is now the following:
- send PDU session release to SMF
- send deregistration event to UDM
- send AM policy control release to PCF
This commit is contained in:
Bostjan Meglic 2022-06-14 16:44:02 +02:00 committed by GitHub
parent 720b4d3c4c
commit 7be7029ac4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 336 additions and 10 deletions

View File

@ -76,7 +76,7 @@ cJSON *OpenAPI_patch_item_convertToJSON(OpenAPI_patch_item_t *patch_item)
}
} else if (OpenAPI_IsBool(patch_item->value)) {
if (cJSON_AddBoolToObject(
item, "value", OpenAPI_IsTrue(patch_item->value))) {
item, "value", OpenAPI_IsTrue(patch_item->value)) == NULL) {
ogs_error("OpenAPI_patch_item_convertToJSON() failed [value]");
goto end;
}

View File

@ -100,6 +100,9 @@ void ogs_sbi_message_free(ogs_sbi_message_t *message)
if (message->Amf3GppAccessRegistration)
OpenAPI_amf3_gpp_access_registration_free(
message->Amf3GppAccessRegistration);
if (message->Amf3GppAccessRegistrationModification)
OpenAPI_amf3_gpp_access_registration_modification_free(
message->Amf3GppAccessRegistrationModification);
if (message->AccessAndMobilitySubscriptionData)
OpenAPI_access_and_mobility_subscription_data_free(
message->AccessAndMobilitySubscriptionData);
@ -760,6 +763,10 @@ static char *build_json(ogs_sbi_message_t *message)
item = OpenAPI_amf3_gpp_access_registration_convertToJSON(
message->Amf3GppAccessRegistration);
ogs_assert(item);
} else if (message->Amf3GppAccessRegistrationModification) {
item = OpenAPI_amf3_gpp_access_registration_modification_convertToJSON(
message->Amf3GppAccessRegistrationModification);
ogs_assert(item);
} else if (message->AccessAndMobilitySubscriptionData) {
item = OpenAPI_access_and_mobility_subscription_data_convertToJSON(
message->AccessAndMobilitySubscriptionData);
@ -1098,13 +1105,30 @@ static int parse_json(ogs_sbi_message_t *message,
CASE(OGS_SBI_RESOURCE_NAME_REGISTRATIONS)
SWITCH(message->h.resource.component[2])
CASE(OGS_SBI_RESOURCE_NAME_AMF_3GPP_ACCESS)
message->Amf3GppAccessRegistration =
OpenAPI_amf3_gpp_access_registration_parseFromJSON(
item);
if (!message->Amf3GppAccessRegistration) {
rv = OGS_ERROR;
ogs_error("JSON parse error");
}
SWITCH(message->h.method)
CASE(OGS_SBI_HTTP_METHOD_PUT)
message->Amf3GppAccessRegistration =
OpenAPI_amf3_gpp_access_registration_parseFromJSON(
item);
if (!message->Amf3GppAccessRegistration) {
rv = OGS_ERROR;
ogs_error("JSON parse error");
}
break;
CASE(OGS_SBI_HTTP_METHOD_PATCH)
message->Amf3GppAccessRegistrationModification =
OpenAPI_amf3_gpp_access_registration_modification_parseFromJSON(
item);
if (!message->Amf3GppAccessRegistrationModification) {
rv = OGS_ERROR;
ogs_error("JSON parse error");
}
break;
DEFAULT
rv = OGS_ERROR;
ogs_error("Unknown method [%s]", message->h.method);
END
break;
DEFAULT
rv = OGS_ERROR;

View File

@ -373,6 +373,8 @@ typedef struct ogs_sbi_message_s {
OpenAPI_confirmation_data_response_t *ConfirmationDataResponse;
OpenAPI_auth_event_t *AuthEvent;
OpenAPI_amf3_gpp_access_registration_t *Amf3GppAccessRegistration;
OpenAPI_amf3_gpp_access_registration_modification_t
*Amf3GppAccessRegistrationModification;
OpenAPI_access_and_mobility_subscription_data_t
*AccessAndMobilitySubscriptionData;
OpenAPI_smf_selection_subscription_data_t *SmfSelectionSubscriptionData;

View File

@ -42,6 +42,7 @@
#include "model/confirmation_data_response.h"
#include "model/auth_event.h"
#include "model/amf3_gpp_access_registration.h"
#include "model/amf3_gpp_access_registration_modification.h"
#include "model/access_and_mobility_subscription_data.h"
#include "model/smf_selection_subscription_data.h"
#include "model/ue_context_in_smf_data.h"

View File

@ -450,6 +450,39 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e)
END
break;
CASE(OGS_SBI_SERVICE_NAME_NUDM_UECM)
SWITCH(sbi_message->h.resource.component[1])
CASE(OGS_SBI_RESOURCE_NAME_REGISTRATIONS)
SWITCH(sbi_message->h.resource.component[2])
CASE(OGS_SBI_RESOURCE_NAME_AMF_3GPP_ACCESS)
SWITCH(sbi_message->h.method)
CASE(OGS_SBI_HTTP_METHOD_PATCH)
ogs_assert(true ==
amf_ue_sbi_discover_and_send(
OpenAPI_nf_type_PCF, amf_ue,
NULL, amf_npcf_am_policy_control_build_delete));
break;
DEFAULT
ogs_error("Unknown method [%s]", sbi_message->h.method);
ogs_assert_if_reached();
END
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[1]);
ogs_assert_if_reached();
END
break;
DEFAULT
ogs_error("Invalid service name [%s]", sbi_message->h.service.name);
ogs_assert_if_reached();

View File

@ -886,8 +886,8 @@ int amf_nsmf_pdusession_handle_release_sm_context(amf_sess_t *sess, int state)
ogs_assert(true ==
amf_ue_sbi_discover_and_send(
OpenAPI_nf_type_PCF, amf_ue,
NULL, amf_npcf_am_policy_control_build_delete));
OpenAPI_nf_type_UDM, amf_ue,
NULL, amf_nudm_uecm_build_registration_delete));
} else if (OGS_FSM_CHECK(&amf_ue->sm, gmm_state_registered)) {
/*

View File

@ -74,6 +74,48 @@ ogs_sbi_request_t *amf_nudm_uecm_build_registration(
return request;
}
ogs_sbi_request_t *amf_nudm_uecm_build_registration_delete(
amf_ue_t *amf_ue, void *data)
{
ogs_sbi_message_t message;
ogs_sbi_request_t *request = NULL;
OpenAPI_amf3_gpp_access_registration_modification_t
Amf3GppAccessRegistrationModification;
ogs_assert(amf_ue);
ogs_assert(amf_ue->supi);
memset(&message, 0, sizeof(message));
message.h.method = (char *)OGS_SBI_HTTP_METHOD_PATCH;
message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NUDM_UECM;
message.h.api.version = (char *)OGS_SBI_API_V1;
message.h.resource.component[0] = amf_ue->supi;
message.h.resource.component[1] =
(char *)OGS_SBI_RESOURCE_NAME_REGISTRATIONS;
message.h.resource.component[2] =
(char *)OGS_SBI_RESOURCE_NAME_AMF_3GPP_ACCESS;
memset(&Amf3GppAccessRegistrationModification, 0,
sizeof(Amf3GppAccessRegistrationModification));
Amf3GppAccessRegistrationModification.guami =
ogs_sbi_build_guami(amf_ue->guami);
Amf3GppAccessRegistrationModification.is_purge_flag = true;
Amf3GppAccessRegistrationModification.purge_flag = 1;
message.Amf3GppAccessRegistrationModification =
&Amf3GppAccessRegistrationModification;
request = ogs_sbi_build_request(&message);
ogs_assert(request);
if (Amf3GppAccessRegistrationModification.guami)
ogs_sbi_free_guami(Amf3GppAccessRegistrationModification.guami);
return request;
}
ogs_sbi_request_t *amf_nudm_sdm_build_get(amf_ue_t *amf_ue, void *data)
{
ogs_sbi_message_t message;

View File

@ -28,6 +28,8 @@ extern "C" {
ogs_sbi_request_t *amf_nudm_uecm_build_registration(
amf_ue_t *amf_ue, void *data);
ogs_sbi_request_t *amf_nudm_uecm_build_registration_delete(
amf_ue_t *amf_ue, void *data);
ogs_sbi_request_t *amf_nudm_sdm_build_get(amf_ue_t *amf_ue, void *data);
#ifdef __cplusplus

View File

@ -336,6 +336,113 @@ bool udm_nudm_uecm_handle_registration(
return true;
}
bool udm_nudm_uecm_handle_registration_update(
udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message)
{
OpenAPI_amf3_gpp_access_registration_modification_t
*Amf3GppAccessRegistrationModification = NULL;
OpenAPI_guami_t *Guami = NULL;
ogs_guami_t recv_guami;
OpenAPI_list_t *PatchItemList = NULL;
OpenAPI_patch_item_t item;
ogs_assert(udm_ue);
ogs_assert(stream);
ogs_assert(message);
Amf3GppAccessRegistrationModification = message->Amf3GppAccessRegistrationModification;
if (!Amf3GppAccessRegistrationModification) {
ogs_error("[%s] No Amf3GppAccessRegistrationModification", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
message, "No Amf3GppAccessRegistrationModification", udm_ue->supi));
return false;
}
Guami = Amf3GppAccessRegistrationModification->guami;
if (!Guami) {
ogs_error("[%s] No Guami", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
message, "No Guami", udm_ue->supi));
return false;
}
if (!Guami->amf_id) {
ogs_error("[%s] No Guami.AmfId", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
message, "No Guami.AmfId", udm_ue->supi));
return false;
}
if (!Guami->plmn_id) {
ogs_error("[%s] No PlmnId", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
message, "No PlmnId", udm_ue->supi));
return false;
}
if (!Guami->plmn_id->mnc) {
ogs_error("[%s] No PlmnId.Mnc", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
message, "No PlmnId.Mnc", udm_ue->supi));
return false;
}
if (!Guami->plmn_id->mcc) {
ogs_error("[%s] No PlmnId.Mcc", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
message, "No PlmnId.Mcc", udm_ue->supi));
return false;
}
/* TS 29.503: 5.3.2.4.2 AMF deregistration for 3GPP access
* 2a. The UDM shall check whether the received GUAMI matches the stored
* GUAMI. If so, the UDM shall set the PurgeFlag. The UDM responds with
* "204 No Content".
* 2b. Otherwise the UDM responds with "403 Forbidden". */
ogs_sbi_parse_guami(&recv_guami, Guami);
if (memcmp(&recv_guami, &udm_ue->guami, sizeof(recv_guami)) != 0) {
ogs_error("[%s] Guami mismatch", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_FORBIDDEN,
message, "Guami mismatch", udm_ue->supi));
}
if (Amf3GppAccessRegistrationModification->is_purge_flag) {
udm_ue->amf_3gpp_access_registration->is_purge_flag =
Amf3GppAccessRegistrationModification->is_purge_flag;
udm_ue->amf_3gpp_access_registration->purge_flag =
Amf3GppAccessRegistrationModification->purge_flag;
}
PatchItemList = OpenAPI_list_create();
ogs_assert(PatchItemList);
if (Amf3GppAccessRegistrationModification->is_purge_flag) {
memset(&item, 0, sizeof(item));
item.op = OpenAPI_patch_operation_replace;
item.path = (char *)"PurgeFlag";
item.value = OpenAPI_any_type_create_bool(
Amf3GppAccessRegistrationModification->purge_flag);
ogs_assert(item.value);
OpenAPI_list_add(PatchItemList, &item);
}
ogs_assert(true ==
udm_sbi_discover_and_send(OpenAPI_nf_type_UDR, udm_ue, stream,
PatchItemList, udm_nudr_dr_build_patch_amf_context));
return true;
}
bool udm_nudm_sdm_handle_subscription_provisioned(
udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{

View File

@ -33,6 +33,8 @@ bool udm_nudm_ueau_handle_result_confirmation_inform(
bool udm_nudm_uecm_handle_registration(
udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message);
bool udm_nudm_uecm_handle_registration_update(
udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message);
bool udm_nudm_sdm_handle_subscription_provisioned(
udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg);

View File

@ -149,6 +149,50 @@ ogs_sbi_request_t *udm_nudr_dr_build_update_amf_context(
return request;
}
ogs_sbi_request_t *udm_nudr_dr_build_patch_amf_context(
udm_ue_t *udm_ue, void *data)
{
ogs_sbi_message_t message;
ogs_sbi_request_t *request = NULL;
OpenAPI_patch_item_t *pitem = NULL;
OpenAPI_lnode_t *node = NULL;
OpenAPI_list_t *PatchItemList = (OpenAPI_list_t *)data;
ogs_assert(udm_ue);
ogs_assert(PatchItemList);
memset(&message, 0, sizeof(message));
message.http.content_type = (char *)OGS_SBI_CONTENT_PATCH_TYPE;
message.h.method = (char *)OGS_SBI_HTTP_METHOD_PATCH;
message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NUDR_DR;
message.h.api.version = (char *)OGS_SBI_API_V1;
message.h.resource.component[0] =
(char *)OGS_SBI_RESOURCE_NAME_SUBSCRIPTION_DATA;
message.h.resource.component[1] = (char *)udm_ue->supi;
message.h.resource.component[2] =
(char *)OGS_SBI_RESOURCE_NAME_CONTEXT_DATA;
message.h.resource.component[3] =
(char *)OGS_SBI_RESOURCE_NAME_AMF_3GPP_ACCESS;
message.PatchItemList = PatchItemList;
request = ogs_sbi_build_request(&message);
ogs_assert(request);
if (PatchItemList) {
OpenAPI_list_for_each(PatchItemList, node) {
pitem = node->data;
if (pitem)
OpenAPI_any_type_free(pitem->value);
}
OpenAPI_list_free(PatchItemList);
}
return request;
}
ogs_sbi_request_t *udm_nudr_dr_build_query_subscription_provisioned(
udm_ue_t *udm_ue, void *data)
{

View File

@ -34,6 +34,8 @@ ogs_sbi_request_t *udm_nudr_dr_build_update_authentication_status(
udm_ue_t *udm_ue, void *data);
ogs_sbi_request_t *udm_nudr_dr_build_update_amf_context(
udm_ue_t *udm_ue, void *data);
ogs_sbi_request_t *udm_nudr_dr_build_patch_amf_context(
udm_ue_t *udm_ue, void *data);
#ifdef __cplusplus
}

View File

@ -406,6 +406,34 @@ bool udm_nudr_dr_handle_subscription_context(
return false;
}
SWITCH(recvmsg->h.method)
CASE(OGS_SBI_HTTP_METHOD_PATCH)
SWITCH(recvmsg->h.resource.component[3])
CASE(OGS_SBI_RESOURCE_NAME_AMF_3GPP_ACCESS)
memset(&sendmsg, 0, sizeof(sendmsg));
response = ogs_sbi_build_response(
&sendmsg, OGS_SBI_HTTP_STATUS_NO_CONTENT);
ogs_assert(response);
ogs_assert(true == ogs_sbi_server_send_response(stream, response));
return true;
DEFAULT
strerror = ogs_msprintf("[%s] Invalid resource name [%s]",
udm_ue->supi, recvmsg->h.resource.component[3]);
ogs_assert(strerror);
ogs_error("%s", strerror);
ogs_assert(true ==
ogs_sbi_server_send_error(
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
recvmsg, strerror, NULL));
ogs_free(strerror);
return false;
END
END
SWITCH(recvmsg->h.resource.component[3])
CASE(OGS_SBI_RESOURCE_NAME_AMF_3GPP_ACCESS)
Amf3GppAccessRegistration = udm_ue->amf_3gpp_access_registration;

View File

@ -101,6 +101,21 @@ void udm_ue_state_operational(ogs_fsm_t *s, udm_event_t *e)
udm_nudm_uecm_handle_registration(udm_ue, stream, message);
break;
DEFAULT
ogs_error("[%s] Invalid resource name [%s]",
udm_ue->suci, message->h.resource.component[1]);
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, message,
"Invalid HTTP method", message->h.method));
END
break;
CASE(OGS_SBI_HTTP_METHOD_PATCH)
SWITCH(message->h.resource.component[1])
CASE(OGS_SBI_RESOURCE_NAME_REGISTRATIONS)
udm_nudm_uecm_handle_registration_update(udm_ue, stream, message);
break;
DEFAULT
ogs_error("[%s] Invalid resource name [%s]",
udm_ue->suci, message->h.resource.component[1]);

View File

@ -303,6 +303,30 @@ bool udr_nudr_dr_handle_subscription_context(
return true;
CASE(OGS_SBI_HTTP_METHOD_PATCH)
OpenAPI_list_t *PatchItemList;
PatchItemList = recvmsg->PatchItemList;
if (!PatchItemList) {
ogs_error("[%s] No PatchItemList", supi);
ogs_assert(true ==
ogs_sbi_server_send_error(
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
recvmsg, "No PatchItemList", supi));
return false;
}
/* TODO: parse PatchItemList */
memset(&sendmsg, 0, sizeof(sendmsg));
response = ogs_sbi_build_response(
&sendmsg, OGS_SBI_HTTP_STATUS_NO_CONTENT);
ogs_assert(response);
ogs_assert(true == ogs_sbi_server_send_response(stream, response));
return true;
DEFAULT
ogs_error("Invalid HTTP method [%s]", recvmsg->h.method);
ogs_assert(true ==