[AMF,UDM] Add support to subscribe to SDM changes

AMF subscribes to UDM for each registered UE.

At the moment, UDM does not send callback to AMF when any of the UE's
properties in the database changes.
At the moment, AMF does properly parse the ModificationNotification, but
does not do anything useful.
This commit is contained in:
Bostjan Meglic 2022-10-04 08:22:38 +00:00 committed by Sukchan Lee
parent e9aaceee98
commit a99a76d916
17 changed files with 394 additions and 1 deletions

View File

@ -179,6 +179,10 @@ void ogs_sbi_message_free(ogs_sbi_message_t *message)
OpenAPI_termination_notification_free(message->TerminationNotification);
if (message->DeregistrationData)
OpenAPI_deregistration_data_free(message->DeregistrationData);
if (message->SDMSubscription)
OpenAPI_sdm_subscription_free(message->SDMSubscription);
if (message->ModificationNotification)
OpenAPI_modification_notification_free(message->ModificationNotification);
/* HTTP Part */
for (i = 0; i < message->num_of_part; i++) {
@ -1022,6 +1026,15 @@ static char *build_json(ogs_sbi_message_t *message)
item = OpenAPI_deregistration_data_convertToJSON(
message->DeregistrationData);
ogs_assert(item);
} else if (message->SDMSubscription) {
item = OpenAPI_sdm_subscription_convertToJSON(
message->SDMSubscription);
ogs_assert(item);
}
else if (message->ModificationNotification) {
item = OpenAPI_modification_notification_convertToJSON(
message->ModificationNotification);
ogs_assert(item);
}
if (item) {
@ -1326,6 +1339,15 @@ static int parse_json(ogs_sbi_message_t *message,
}
break;
CASE(OGS_SBI_RESOURCE_NAME_SDM_SUBSCRIPTIONS)
message->SDMSubscription =
OpenAPI_sdm_subscription_parseFromJSON(item);
if (!message->SDMSubscription) {
rv = OGS_ERROR;
ogs_error("JSON parse error");
}
break;
DEFAULT
rv = OGS_ERROR;
ogs_error("Unknown resource name [%s]",
@ -1869,6 +1891,15 @@ static int parse_json(ogs_sbi_message_t *message,
}
break;
CASE(OGS_SBI_RESOURCE_NAME_SDMSUBSCRIPTION_NOTIFY)
message->ModificationNotification =
OpenAPI_modification_notification_parseFromJSON(item);
if (!message->ModificationNotification) {
rv = OGS_ERROR;
ogs_error("JSON parse error");
}
break;
DEFAULT
rv = OGS_ERROR;
ogs_error("Unknown resource name [%s]",

View File

@ -95,6 +95,7 @@ extern "C" {
#define OGS_SBI_RESOURCE_NAME_UE_CONTEXT_IN_SMF_DATA "ue-context-in-smf-data"
#define OGS_SBI_RESOURCE_NAME_SMF_SELECTION_SUBSCRIPTION_DATA \
"smf-selection-subscription-data"
#define OGS_SBI_RESOURCE_NAME_SDM_SUBSCRIPTIONS "sdm-subscriptions"
#define OGS_SBI_RESOURCE_NAME_SECURITY_INFORMATION "security-information"
#define OGS_SBI_RESOURCE_NAME_GENERATE_AUTH_DATA "generate-auth-data"
@ -126,6 +127,8 @@ extern "C" {
#define OGS_SBI_RESOURCE_NAME_SM_CONTEXT_STATUS "sm-context-status"
#define OGS_SBI_RESOURCE_NAME_AM_POLICY_NOTIFY "am-policy-notify"
#define OGS_SBI_RESOURCE_NAME_DEREG_NOTIFY "dereg-notify"
#define OGS_SBI_RESOURCE_NAME_SDMSUBSCRIPTION_NOTIFY \
"sdmsubscription-notify"
#define OGS_SBI_RESOURCE_NAME_POLICIES "policies"
#define OGS_SBI_RESOURCE_NAME_SM_POLICIES "sm-policies"
@ -452,6 +455,8 @@ typedef struct ogs_sbi_message_s {
OpenAPI_sm_policy_notification_t *SmPolicyNotification;
OpenAPI_termination_notification_t *TerminationNotification;
OpenAPI_deregistration_data_t *DeregistrationData;
OpenAPI_sdm_subscription_t *SDMSubscription;
OpenAPI_modification_notification_t *ModificationNotification;
ogs_sbi_links_t *links;

View File

@ -75,6 +75,8 @@
#include "model/sm_policy_notification.h"
#include "model/termination_notification.h"
#include "model/deregistration_data.h"
#include "model/sdm_subscription.h"
#include "model/modification_notification.h"
#include "custom/links.h"
#include "custom/ue_authentication_ctx.h"

View File

@ -213,6 +213,11 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e)
amf_namf_callback_handle_dereg_notify(stream, &sbi_message);
break;
CASE(OGS_SBI_RESOURCE_NAME_SDMSUBSCRIPTION_NOTIFY)
amf_namf_callback_handle_sdm_data_change_notify(
stream, &sbi_message);
break;
CASE(OGS_SBI_RESOURCE_NAME_AM_POLICY_NOTIFY)
ogs_assert(true == ogs_sbi_send_http_status_no_content(stream));
break;

View File

@ -1387,6 +1387,8 @@ void amf_ue_remove(amf_ue_t *amf_ue)
if (amf_ue->policy_association_id)
ogs_free(amf_ue->policy_association_id);
if (amf_ue->data_change_subscription_id)
ogs_free(amf_ue->data_change_subscription_id);
if (amf_ue->confirmation_url_for_5g_aka)
ogs_free(amf_ue->confirmation_url_for_5g_aka);

View File

@ -393,6 +393,9 @@ struct amf_ue_s {
/* Network Initiated De-Registration */
bool network_initiated_de_reg;
/* SubscriptionId of Subscription to Data Change Notification to UDM */
char *data_change_subscription_id;
ogs_list_t sess_list;
};

View File

@ -988,6 +988,7 @@ void gmm_state_initial_context_setup(ogs_fsm_t *s, amf_event_t *e)
SWITCH(sbi_message->h.resource.component[1])
CASE(OGS_SBI_RESOURCE_NAME_REGISTRATIONS)
if (sbi_message->res_status != OGS_SBI_HTTP_STATUS_CREATED &&
sbi_message->res_status != OGS_SBI_HTTP_STATUS_NO_CONTENT &&
sbi_message->res_status != OGS_SBI_HTTP_STATUS_OK) {
ogs_error("[%s] HTTP response error [%d]",
amf_ue->supi, sbi_message->res_status);
@ -1027,7 +1028,9 @@ void gmm_state_initial_context_setup(ogs_fsm_t *s, amf_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_AM_DATA)
CASE(OGS_SBI_RESOURCE_NAME_SMF_SELECT_DATA)
CASE(OGS_SBI_RESOURCE_NAME_UE_CONTEXT_IN_SMF_DATA)
if (sbi_message->res_status != OGS_SBI_HTTP_STATUS_OK) {
CASE(OGS_SBI_RESOURCE_NAME_SDM_SUBSCRIPTIONS)
if ((sbi_message->res_status != OGS_SBI_HTTP_STATUS_OK) &&
(sbi_message->res_status != OGS_SBI_HTTP_STATUS_CREATED)) {
ogs_error("[%s] HTTP response error [%d]",
amf_ue->supi, sbi_message->res_status);
ogs_assert(OGS_OK ==

View File

@ -606,3 +606,114 @@ cleanup:
return OGS_OK;
}
int amf_namf_callback_handle_sdm_data_change_notify(
ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
int status = OGS_SBI_HTTP_STATUS_NO_CONTENT;
amf_ue_t *amf_ue = NULL;
ogs_sbi_message_t sendmsg;
ogs_sbi_response_t *response = NULL;
OpenAPI_modification_notification_t *ModificationNotification;
OpenAPI_lnode_t *node;
char *ueid = NULL;
char *res_name = NULL;
ogs_assert(stream);
ogs_assert(recvmsg);
ModificationNotification = recvmsg->ModificationNotification;
if (!ModificationNotification) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s] No ModificationNotification", amf_ue->supi);
goto cleanup;
}
OpenAPI_list_for_each(ModificationNotification->notify_items, node)
{
OpenAPI_notify_item_t *item = node->data;
char *saveptr = NULL;
ueid = ogs_sbi_parse_uri(item->resource_id, "/", &saveptr);
if (!ueid) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s] No UeId", item->resource_id);
goto cleanup;
}
amf_ue = amf_ue_find_by_supi(ueid);
if (!amf_ue) {
status = OGS_SBI_HTTP_STATUS_NOT_FOUND;
ogs_error("Cannot find SUPI [%s]", ueid);
goto cleanup;
}
res_name = ogs_sbi_parse_uri(NULL, "/", &saveptr);
if (!res_name) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s] No Resource Name", item->resource_id);
goto cleanup;
}
SWITCH(res_name)
CASE(OGS_SBI_RESOURCE_NAME_AM_DATA)
OpenAPI_lnode_t *node_ci;
OpenAPI_list_for_each(item->changes, node_ci)
{
/*
OpenAPI_change_item_t *item_change = node_ci->data;
item_change->path;
item_change->from;
item_change->new_value;
item_change->orig_value;
*/
/*
switch (item_change->op) {
case OpenAPI_change_type_ADD:
break;
case OpenAPI_change_type_MOVE:
break;
case OpenAPI_change_type__REMOVE:
break;
case OpenAPI_change_type_REPLACE:
break;
default:
break;
}
*/
}
break;
DEFAULT
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("Unknown Resource Name: [%s]", res_name);
goto cleanup;
END
ogs_free(ueid);
ogs_free(res_name);
ueid = NULL;
res_name = NULL;
}
cleanup:
if (ueid)
ogs_free(ueid);
if (res_name)
ogs_free(res_name);
memset(&sendmsg, 0, sizeof(sendmsg));
response = ogs_sbi_build_response(&sendmsg, status);
ogs_assert(response);
ogs_assert(true == ogs_sbi_server_send_response(stream, response));
return OGS_OK;
}

View File

@ -32,6 +32,8 @@ int amf_namf_callback_handle_sm_context_status(
ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg);
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);
#ifdef __cplusplus
}

View File

@ -137,3 +137,63 @@ ogs_sbi_request_t *amf_nudm_sdm_build_get(amf_ue_t *amf_ue, void *data)
return request;
}
ogs_sbi_request_t *amf_nudm_sdm_build_subscription(amf_ue_t *amf_ue, void *data)
{
ogs_sbi_message_t message;
ogs_sbi_header_t header;
ogs_sbi_request_t *request = NULL;
ogs_sbi_server_t *server = NULL;
OpenAPI_sdm_subscription_t SDMSubscription;
char *monres = NULL;
ogs_assert(amf_ue);
ogs_assert(amf_ue->supi);
ogs_assert(data);
memset(&message, 0, sizeof(message));
message.h.method = (char *)OGS_SBI_HTTP_METHOD_POST;
message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NUDM_SDM;
message.h.api.version = (char *)OGS_SBI_API_V2;
message.h.resource.component[0] = amf_ue->supi;
message.h.resource.component[1] =
(char *)OGS_SBI_RESOURCE_NAME_SDM_SUBSCRIPTIONS;
memset(&SDMSubscription, 0, sizeof(SDMSubscription));
SDMSubscription.nf_instance_id = ogs_sbi_self()->nf_instance->id;
server = ogs_list_first(&ogs_sbi_self()->server_list);
ogs_assert(server);
memset(&header, 0, sizeof(header));
header.service.name = (char *)OGS_SBI_SERVICE_NAME_NAMF_CALLBACK;
header.api.version = (char *)OGS_SBI_API_V1;
header.resource.component[0] = amf_ue->supi;
header.resource.component[1] =
(char *)OGS_SBI_RESOURCE_NAME_SDMSUBSCRIPTION_NOTIFY;
SDMSubscription.callback_reference =
ogs_sbi_server_uri(server, &header);
ogs_assert(SDMSubscription.callback_reference);
SDMSubscription.monitored_resource_uris = OpenAPI_list_create();
monres = ogs_msprintf("%s/%s", amf_ue->supi, (char *)data);
ogs_assert(monres);
OpenAPI_list_add(SDMSubscription.monitored_resource_uris, monres);
SDMSubscription.implicit_unsubscribe = 1;
message.SDMSubscription = &SDMSubscription;
request = ogs_sbi_build_request(&message);
ogs_assert(request);
ogs_free(monres);
OpenAPI_list_free(SDMSubscription.monitored_resource_uris);
ogs_free(SDMSubscription.callback_reference);
return request;
}

View File

@ -31,6 +31,8 @@ ogs_sbi_request_t *amf_nudm_uecm_build_registration(
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);
ogs_sbi_request_t *amf_nudm_sdm_build_subscription(
amf_ue_t *amf_ue, void *data);
#ifdef __cplusplus
}

View File

@ -220,6 +220,69 @@ int amf_nudm_sdm_handle_provisioned(
break;
CASE(OGS_SBI_RESOURCE_NAME_UE_CONTEXT_IN_SMF_DATA)
if (amf_ue->data_change_subscription_id) {
/* we already have a SDM subscription to UDM; continue without
* subscribing again */
ogs_assert(true ==
amf_ue_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NPCF_AM_POLICY_CONTROL, NULL,
amf_npcf_am_policy_control_build_create, amf_ue, NULL));
}
else {
ogs_assert(true ==
amf_ue_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_SDM, NULL,
amf_nudm_sdm_build_subscription, amf_ue,
(char *)OGS_SBI_RESOURCE_NAME_AM_DATA));
}
break;
CASE(OGS_SBI_RESOURCE_NAME_SDM_SUBSCRIPTIONS)
int rv;
ogs_sbi_message_t message;
ogs_sbi_header_t header;
if (!recvmsg->http.location) {
ogs_error("[%s] No http.location", amf_ue->supi);
ogs_assert(OGS_OK ==
nas_5gs_send_gmm_reject_from_sbi(
amf_ue, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR));
return OGS_ERROR;
}
memset(&header, 0, sizeof(header));
header.uri = recvmsg->http.location;
rv = ogs_sbi_parse_header(&message, &header);
if (rv != OGS_OK) {
ogs_error("[%s] Cannot parse http.location [%s]",
amf_ue->supi, recvmsg->http.location);
ogs_assert(OGS_OK ==
nas_5gs_send_gmm_reject_from_sbi(
amf_ue, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR));
return OGS_ERROR;
}
if (!message.h.resource.component[2]) {
ogs_error("[%s] No Subscription ID [%s]",
amf_ue->supi, recvmsg->http.location);
ogs_sbi_header_free(&header);
ogs_assert(OGS_OK ==
nas_5gs_send_gmm_reject_from_sbi(
amf_ue, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR));
return OGS_ERROR;
}
if (amf_ue->data_change_subscription_id)
ogs_free(amf_ue->data_change_subscription_id);
amf_ue->data_change_subscription_id =
ogs_strdup(message.h.resource.component[2]);
ogs_sbi_header_free(&header);
ogs_assert(true ==
amf_ue_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NPCF_AM_POLICY_CONTROL, NULL,

View File

@ -188,6 +188,8 @@ void udm_ue_remove(udm_ue_t *udm_ue)
ogs_free(udm_ue->amf_instance_id);
if (udm_ue->dereg_callback_uri)
ogs_free(udm_ue->dereg_callback_uri);
if (udm_ue->data_change_callback_uri)
ogs_free(udm_ue->data_change_callback_uri);
ogs_pool_free(&udm_ue_pool, udm_ue);
}

View File

@ -58,6 +58,7 @@ struct udm_ue_s {
char *amf_instance_id;
char *dereg_callback_uri;
char *data_change_callback_uri;
uint8_t k[OGS_KEY_LEN];
uint8_t opc[OGS_KEY_LEN];

View File

@ -479,3 +479,85 @@ bool udm_nudm_sdm_handle_subscription_provisioned(
return true;
}
bool udm_nudm_sdm_handle_subscription_create(
udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
ogs_sbi_message_t sendmsg;
ogs_sbi_response_t *response = NULL;
ogs_sbi_server_t *server = NULL;
ogs_sbi_header_t header;
OpenAPI_sdm_subscription_t *SDMSubscription = NULL;
ogs_assert(udm_ue);
ogs_assert(stream);
ogs_assert(recvmsg);
SDMSubscription = recvmsg->SDMSubscription;
if (!SDMSubscription) {
ogs_error("[%s] No SDMSubscription", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
recvmsg, "No SDMSubscription", udm_ue->supi));
return false;
}
if (!SDMSubscription->nf_instance_id) {
ogs_error("[%s] No nfInstanceId", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
recvmsg, "No nfInstanceId", udm_ue->supi));
return false;
}
if (!SDMSubscription->callback_reference) {
ogs_error("[%s] No callbackReference", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
recvmsg, "No callbackReference", udm_ue->supi));
return false;
}
if ((!SDMSubscription->monitored_resource_uris) &&
(!SDMSubscription->monitored_resource_uris->count)) {
ogs_error("[%s] No monitoredResourceUris", udm_ue->supi);
ogs_assert(true ==
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
recvmsg, "No monitoredResourceUris", udm_ue->supi));
return false;
}
if (udm_ue->data_change_callback_uri)
ogs_free(udm_ue->data_change_callback_uri);
udm_ue->data_change_callback_uri =
ogs_strdup(SDMSubscription->callback_reference);
server = ogs_sbi_server_from_stream(stream);
ogs_assert(server);
memset(&header, 0, sizeof(header));
header.service.name = (char *)OGS_SBI_SERVICE_NAME_NUDM_SDM;
header.api.version = (char *)OGS_SBI_API_V2;
header.resource.component[0] = udm_ue->supi;
header.resource.component[1] =
(char *)OGS_SBI_RESOURCE_NAME_SDM_SUBSCRIPTIONS;
/* TODO: subscription id */
header.resource.component[2] = udm_ue->ctx_id;
memset(&sendmsg, 0, sizeof(sendmsg));
sendmsg.http.location = ogs_sbi_server_uri(server, &header);
sendmsg.SDMSubscription = OpenAPI_sdm_subscription_copy(
sendmsg.SDMSubscription, SDMSubscription);
response = ogs_sbi_build_response(&sendmsg, OGS_SBI_HTTP_STATUS_CREATED);
ogs_assert(response);
ogs_sbi_server_send_response(stream, response);
ogs_free(sendmsg.http.location);
OpenAPI_sdm_subscription_free(sendmsg.SDMSubscription);
return true;
}

View File

@ -38,6 +38,8 @@ bool udm_nudm_uecm_handle_registration_update(
bool udm_nudm_sdm_handle_subscription_provisioned(
udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg);
bool udm_nudm_sdm_handle_subscription_create(
udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg);
#ifdef __cplusplus
}

View File

@ -163,6 +163,23 @@ void udm_ue_state_operational(ogs_fsm_t *s, udm_event_t *e)
"Invalid resource name", message->h.method));
END
break;
CASE(OGS_SBI_HTTP_METHOD_POST)
SWITCH(message->h.resource.component[1])
CASE(OGS_SBI_RESOURCE_NAME_SDM_SUBSCRIPTIONS)
udm_nudm_sdm_handle_subscription_create(
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 resource name", message->h.method));
END
break;
DEFAULT
ogs_error("[%s] Invalid HTTP method [%s]",
udm_ue->supi, message->h.method);