open5gs/src/amf/namf-handler.c

1475 lines
49 KiB
C

/*
* Copyright (C) 2019-2023 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-handler.h"
#include "nsmf-handler.h"
#include "nas-path.h"
#include "ngap-path.h"
#include "sbi-path.h"
int amf_namf_comm_handle_n1_n2_message_transfer(
ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
int status, r;
amf_ue_t *amf_ue = NULL;
ran_ue_t *ran_ue = NULL;
amf_sess_t *sess = NULL;
ogs_pkbuf_t *n1buf = NULL;
ogs_pkbuf_t *n2buf = NULL;
ogs_pkbuf_t *gmmbuf = NULL;
ogs_pkbuf_t *ngapbuf = NULL;
char *supi = NULL;
uint8_t pdu_session_id = OGS_NAS_PDU_SESSION_IDENTITY_UNASSIGNED;
ogs_sbi_message_t sendmsg;
ogs_sbi_response_t *response = NULL;
OpenAPI_n1_n2_message_transfer_req_data_t *N1N2MessageTransferReqData;
OpenAPI_n1_n2_message_transfer_rsp_data_t N1N2MessageTransferRspData;
OpenAPI_n1_message_container_t *n1MessageContainer = NULL;
OpenAPI_ref_to_binary_data_t *n1MessageContent = NULL;
OpenAPI_n2_info_container_t *n2InfoContainer = NULL;
OpenAPI_n2_sm_information_t *smInfo = NULL;
OpenAPI_n2_info_content_t *n2InfoContent = NULL;
OpenAPI_ref_to_binary_data_t *ngapData = NULL;
OpenAPI_ngap_ie_type_e ngapIeType = OpenAPI_ngap_ie_type_NULL;
ogs_assert(stream);
ogs_assert(recvmsg);
N1N2MessageTransferReqData = recvmsg->N1N2MessageTransferReqData;
if (!N1N2MessageTransferReqData) {
ogs_error("No N1N2MessageTransferReqData");
return OGS_ERROR;
}
if (N1N2MessageTransferReqData->is_pdu_session_id == false) {
ogs_error("No PDU Session Identity");
return OGS_ERROR;
}
pdu_session_id = N1N2MessageTransferReqData->pdu_session_id;
supi = recvmsg->h.resource.component[1];
if (!supi) {
ogs_error("No SUPI");
return OGS_ERROR;
}
amf_ue = amf_ue_find_by_supi(supi);
if (!amf_ue) {
ogs_error("No UE context [%s]", supi);
return OGS_ERROR;
}
sess = amf_sess_find_by_psi(amf_ue, pdu_session_id);
if (!sess) {
ogs_error("[%s] No PDU Session Context [%d]",
amf_ue->supi, pdu_session_id);
return OGS_ERROR;
}
n1MessageContainer = N1N2MessageTransferReqData->n1_message_container;
if (n1MessageContainer) {
n1MessageContent = n1MessageContainer->n1_message_content;
if (!n1MessageContent || !n1MessageContent->content_id) {
ogs_error("No n1MessageContent");
return OGS_ERROR;
}
n1buf = ogs_sbi_find_part_by_content_id(
recvmsg, n1MessageContent->content_id);
if (!n1buf) {
ogs_error("[%s] No N1 SM Content", amf_ue->supi);
return OGS_ERROR;
}
/*
* NOTE : The pkbuf created in the SBI message will be removed
* from ogs_sbi_message_free(), so it must be copied.
*/
n1buf = ogs_pkbuf_copy(n1buf);
ogs_assert(n1buf);
}
n2InfoContainer = N1N2MessageTransferReqData->n2_info_container;
if (n2InfoContainer) {
smInfo = n2InfoContainer->sm_info;
if (!smInfo) {
ogs_error("No smInfo");
return OGS_ERROR;
}
n2InfoContent = smInfo->n2_info_content;
if (!n2InfoContent) {
ogs_error("No n2InfoContent");
return OGS_ERROR;
}
ngapIeType = n2InfoContent->ngap_ie_type;
ngapData = n2InfoContent->ngap_data;
if (!ngapData || !ngapData->content_id) {
ogs_error("No ngapData");
return OGS_ERROR;
}
n2buf = ogs_sbi_find_part_by_content_id(
recvmsg, ngapData->content_id);
if (!n2buf) {
ogs_error("[%s] No N2 SM Content", amf_ue->supi);
return OGS_ERROR;
}
/*
* NOTE : The pkbuf created in the SBI message will be removed
* from ogs_sbi_message_free(), so it must be copied.
*/
n2buf = ogs_pkbuf_copy(n2buf);
ogs_assert(n2buf);
}
memset(&sendmsg, 0, sizeof(sendmsg));
status = OGS_SBI_HTTP_STATUS_OK;
memset(&N1N2MessageTransferRspData, 0, sizeof(N1N2MessageTransferRspData));
N1N2MessageTransferRspData.cause =
OpenAPI_n1_n2_message_transfer_cause_N1_N2_TRANSFER_INITIATED;
sendmsg.N1N2MessageTransferRspData = &N1N2MessageTransferRspData;
switch (ngapIeType) {
case OpenAPI_ngap_ie_type_PDU_RES_SETUP_REQ:
if (!n2buf) {
ogs_error("[%s] No N2 SM Content", amf_ue->supi);
return OGS_ERROR;
}
if (n1buf) {
gmmbuf = gmm_build_dl_nas_transport(sess,
OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, n1buf, 0, 0);
ogs_assert(gmmbuf);
}
if (gmmbuf) {
/***********************************
* 4.3.2 PDU Session Establishment *
***********************************/
ran_ue = ran_ue_cycle(sess->ran_ue);
if (ran_ue) {
if (sess->pdu_session_establishment_accept) {
ogs_pkbuf_free(sess->pdu_session_establishment_accept);
sess->pdu_session_establishment_accept = NULL;
}
if (ran_ue->initial_context_setup_request_sent == true) {
ngapbuf =
ngap_sess_build_pdu_session_resource_setup_request(
ran_ue, sess, gmmbuf, n2buf);
ogs_assert(ngapbuf);
} else {
ngapbuf = ngap_sess_build_initial_context_setup_request(
ran_ue, sess, gmmbuf, n2buf);
ogs_assert(ngapbuf);
ran_ue->initial_context_setup_request_sent = true;
}
if (SESSION_CONTEXT_IN_SMF(sess)) {
/*
* [1-CLIENT] /nsmf-pdusession/v1/sm-contexts
* [2-SERVER] /namf-comm/v1/ue-contexts/{supi}/n1-n2-messages
*
* If [2-SERVER] arrives after [1-CLIENT],
* sm-context-ref is created in [1-CLIENT].
* So, the PDU session establishment accpet can be transmitted.
*/
r = ngap_send_to_ran_ue(ran_ue, ngapbuf);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
sess->pdu_session_establishment_accept = ngapbuf;
}
} else {
ogs_warn("[%s] RAN-NG Context has already been removed",
amf_ue->supi);
}
} else {
/*********************************************
* 4.2.3.3 Network Triggered Service Request *
*********************************************/
if (CM_IDLE(amf_ue)) {
bool rc;
ogs_sbi_server_t *server = NULL;
ogs_sbi_header_t header;
ogs_sbi_client_t *client = NULL;
OpenAPI_uri_scheme_e scheme = OpenAPI_uri_scheme_NULL;
char *fqdn = NULL;
uint16_t fqdn_port = 0;
ogs_sockaddr_t *addr = NULL, *addr6 = NULL;
if (!N1N2MessageTransferReqData->n1n2_failure_txf_notif_uri) {
ogs_error("[%s:%d] No n1-n2-failure-notification-uri",
amf_ue->supi, sess->psi);
return OGS_ERROR;
}
rc = ogs_sbi_getaddr_from_uri(
&scheme, &fqdn, &fqdn_port, &addr, &addr6,
N1N2MessageTransferReqData->n1n2_failure_txf_notif_uri);
if (rc == false || scheme == OpenAPI_uri_scheme_NULL) {
ogs_error("[%s:%d] Invalid URI [%s]",
amf_ue->supi, sess->psi,
N1N2MessageTransferReqData->
n1n2_failure_txf_notif_uri);
return OGS_ERROR;;
}
client = ogs_sbi_client_find(
scheme, fqdn, fqdn_port, addr, addr6);
if (!client) {
ogs_debug("%s: ogs_sbi_client_add()", OGS_FUNC);
client = ogs_sbi_client_add(
scheme, fqdn, fqdn_port, addr, addr6);
if (!client) {
ogs_error("%s: ogs_sbi_client_add() failed", OGS_FUNC);
ogs_free(fqdn);
ogs_freeaddrinfo(addr);
ogs_freeaddrinfo(addr6);
return OGS_ERROR;
}
}
OGS_SBI_SETUP_CLIENT(&sess->paging, client);
ogs_free(fqdn);
ogs_freeaddrinfo(addr);
ogs_freeaddrinfo(addr6);
status = OGS_SBI_HTTP_STATUS_ACCEPTED;
N1N2MessageTransferRspData.cause =
OpenAPI_n1_n2_message_transfer_cause_ATTEMPTING_TO_REACH_UE;
/* Location */
server = ogs_sbi_server_from_stream(stream);
ogs_assert(server);
memset(&header, 0, sizeof(header));
header.service.name = (char *)OGS_SBI_SERVICE_NAME_NAMF_COMM;
header.api.version = (char *)OGS_SBI_API_V1;
header.resource.component[0] =
(char *)OGS_SBI_RESOURCE_NAME_UE_CONTEXTS;
header.resource.component[1] = amf_ue->supi;
header.resource.component[2] =
(char *)OGS_SBI_RESOURCE_NAME_N1_N2_MESSAGES;
header.resource.component[3] = sess->sm_context_ref;
sendmsg.http.location = ogs_sbi_server_uri(server, &header);
/* Store Paging Info */
AMF_SESS_STORE_PAGING_INFO(
sess, sendmsg.http.location,
N1N2MessageTransferReqData->n1n2_failure_txf_notif_uri);
/* Store N2 Transfer message */
AMF_SESS_STORE_N2_TRANSFER(
sess, pdu_session_resource_setup_request, n2buf);
r = ngap_send_paging(amf_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (CM_CONNECTED(amf_ue)) {
r = nas_send_pdu_session_setup_request(sess, NULL, n2buf);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
ogs_fatal("[%s] Invalid AMF-UE state", amf_ue->supi);
ogs_assert_if_reached();
}
}
break;
case OpenAPI_ngap_ie_type_PDU_RES_MOD_REQ:
if (!n1buf) {
ogs_error("[%s] No N1 SM Content", amf_ue->supi);
return OGS_ERROR;
}
if (!n2buf) {
ogs_error("[%s] No N2 SM Content", amf_ue->supi);
return OGS_ERROR;
}
if (CM_IDLE(amf_ue)) {
ogs_sbi_server_t *server = NULL;
ogs_sbi_header_t header;
status = OGS_SBI_HTTP_STATUS_ACCEPTED;
N1N2MessageTransferRspData.cause =
OpenAPI_n1_n2_message_transfer_cause_ATTEMPTING_TO_REACH_UE;
/* Location */
server = ogs_sbi_server_from_stream(stream);
ogs_assert(server);
memset(&header, 0, sizeof(header));
header.service.name = (char *)OGS_SBI_SERVICE_NAME_NAMF_COMM;
header.api.version = (char *)OGS_SBI_API_V1;
header.resource.component[0] =
(char *)OGS_SBI_RESOURCE_NAME_UE_CONTEXTS;
header.resource.component[1] = amf_ue->supi;
header.resource.component[2] =
(char *)OGS_SBI_RESOURCE_NAME_N1_N2_MESSAGES;
header.resource.component[3] = sess->sm_context_ref;
sendmsg.http.location = ogs_sbi_server_uri(server, &header);
/* Store Paging Info */
AMF_SESS_STORE_PAGING_INFO(
sess, sendmsg.http.location, NULL);
/* Store 5GSM Message */
AMF_SESS_STORE_5GSM_MESSAGE(sess,
OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMMAND,
n1buf, n2buf);
r = ngap_send_paging(amf_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (CM_CONNECTED(amf_ue)) {
if (CONTEXT_SETUP_ESTABLISHED(amf_ue)) {
r = nas_send_pdu_session_modification_command(
sess, n1buf, n2buf);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
/* Store 5GSM Message */
ogs_warn("[Session MODIFY] Context setup is not established");
AMF_SESS_STORE_5GSM_MESSAGE(sess,
OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMMAND,
n1buf, n2buf);
}
} else {
ogs_fatal("[%s] Invalid AMF-UE state", amf_ue->supi);
ogs_assert_if_reached();
}
break;
case OpenAPI_ngap_ie_type_PDU_RES_REL_CMD:
if (!n2buf) {
ogs_error("[%s] No N2 SM Content", amf_ue->supi);
return OGS_ERROR;
}
if (CM_IDLE(amf_ue)) {
if (N1N2MessageTransferReqData->is_skip_ind == true &&
N1N2MessageTransferReqData->skip_ind == true) {
if (n1buf)
ogs_pkbuf_free(n1buf);
if (n2buf)
ogs_pkbuf_free(n2buf);
N1N2MessageTransferRspData.cause =
OpenAPI_n1_n2_message_transfer_cause_N1_MSG_NOT_TRANSFERRED;
} else {
ogs_sbi_server_t *server = NULL;
ogs_sbi_header_t header;
status = OGS_SBI_HTTP_STATUS_ACCEPTED;
N1N2MessageTransferRspData.cause =
OpenAPI_n1_n2_message_transfer_cause_ATTEMPTING_TO_REACH_UE;
/* Location */
server = ogs_sbi_server_from_stream(stream);
ogs_assert(server);
memset(&header, 0, sizeof(header));
header.service.name = (char *)OGS_SBI_SERVICE_NAME_NAMF_COMM;
header.api.version = (char *)OGS_SBI_API_V1;
header.resource.component[0] =
(char *)OGS_SBI_RESOURCE_NAME_UE_CONTEXTS;
header.resource.component[1] = amf_ue->supi;
header.resource.component[2] =
(char *)OGS_SBI_RESOURCE_NAME_N1_N2_MESSAGES;
header.resource.component[3] = sess->sm_context_ref;
sendmsg.http.location = ogs_sbi_server_uri(server, &header);
/* Store Paging Info */
AMF_SESS_STORE_PAGING_INFO(
sess, sendmsg.http.location, NULL);
/* Store 5GSM Message */
AMF_SESS_STORE_5GSM_MESSAGE(sess,
OGS_NAS_5GS_PDU_SESSION_RELEASE_COMMAND,
n1buf, n2buf);
r = ngap_send_paging(amf_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
}
} else if (CM_CONNECTED(amf_ue)) {
if (CONTEXT_SETUP_ESTABLISHED(amf_ue)) {
r = nas_send_pdu_session_release_command(sess, n1buf, n2buf);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
/* Store 5GSM Message */
ogs_warn("[Session RELEASE] Context setup is not established");
AMF_SESS_STORE_5GSM_MESSAGE(sess,
OGS_NAS_5GS_PDU_SESSION_RELEASE_COMMAND,
n1buf, n2buf);
}
} else {
ogs_fatal("[%s] Invalid AMF-UE state", amf_ue->supi);
ogs_assert_if_reached();
}
break;
case OpenAPI_ngap_ie_type_NULL:
/*
* No n2InfoContainer. According to TS23.502, this means that SMF has
* encountered an error and is rejecting the session.
*
* TS23.502
* 6.3.1.7 4.3.2.2 UE Requested PDU Session Establishment
* p100
* 11. ...
* If the PDU session establishment failed anywhere between step 5
* and step 11, then the Namf_Communication_N1N2MessageTransfer
* request shall include the N1 SM container with a PDU Session
* Establishment Reject message ...
*/
if (!n1buf) {
ogs_error("[%s] No N1 SM Content", amf_ue->supi);
return OGS_ERROR;
}
ogs_error("[%d:%d] PDU session establishment reject",
sess->psi, sess->pti);
r = nas_5gs_send_gsm_reject(sess->ran_ue, sess,
OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, n1buf);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
amf_sess_remove(sess);
break;
default:
ogs_error("Not implemented ngapIeType[%d]", ngapIeType);
ogs_assert_if_reached();
}
response = ogs_sbi_build_response(&sendmsg, status);
ogs_assert(response);
ogs_assert(true == ogs_sbi_server_send_response(stream, response));
if (sendmsg.http.location)
ogs_free(sendmsg.http.location);
return OGS_OK;
}
int amf_namf_callback_handle_sm_context_status(
ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
int status = OGS_SBI_HTTP_STATUS_NO_CONTENT;
amf_ue_t *amf_ue = NULL;
amf_sess_t *sess = NULL;
uint8_t pdu_session_identity;
ogs_sbi_message_t sendmsg;
ogs_sbi_response_t *response = NULL;
OpenAPI_sm_context_status_notification_t *SmContextStatusNotification;
OpenAPI_status_info_t *StatusInfo;
ogs_assert(stream);
ogs_assert(recvmsg);
if (!recvmsg->h.resource.component[0]) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("No SUPI");
goto cleanup;
}
amf_ue = amf_ue_find_by_supi(recvmsg->h.resource.component[0]);
if (!amf_ue) {
status = OGS_SBI_HTTP_STATUS_NOT_FOUND;
ogs_error("Cannot find SUPI [%s]", recvmsg->h.resource.component[0]);
goto cleanup;
}
if (!recvmsg->h.resource.component[2]) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s] No PDU Session Identity", amf_ue->supi);
goto cleanup;
}
pdu_session_identity = atoi(recvmsg->h.resource.component[2]);
if (pdu_session_identity == OGS_NAS_PDU_SESSION_IDENTITY_UNASSIGNED) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s] PDU Session Identity is unassigned", amf_ue->supi);
goto cleanup;
}
sess = amf_sess_find_by_psi(amf_ue, pdu_session_identity);
if (!sess) {
status = OGS_SBI_HTTP_STATUS_NOT_FOUND;
ogs_warn("[%s] Cannot find session", amf_ue->supi);
goto cleanup;
}
SmContextStatusNotification = recvmsg->SmContextStatusNotification;
if (!SmContextStatusNotification) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s:%d] No SmContextStatusNotification",
amf_ue->supi, sess->psi);
goto cleanup;
}
StatusInfo = SmContextStatusNotification->status_info;
if (!StatusInfo) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s:%d] No StatusInfo", amf_ue->supi, sess->psi);
goto cleanup;
}
sess->resource_status = StatusInfo->resource_status;
/*
* Race condition for PDU session release complete
* - CLIENT : /nsmf-pdusession/v1/sm-contexts/{smContextRef}/modify
* - SERVER : /namf-callback/v1/{supi}/sm-context-status/{psi})
*
* If NOTIFICATION is received before the CLIENT response is received,
* CLIENT sync is not finished. In this case, the session context
* should not be removed.
*
* If NOTIFICATION comes after the CLIENT response is received,
* sync is done. So, the session context can be removed.
*/
ogs_info("[%s:%d][%d:%d:%s] "
"/namf-callback/v1/{supi}/sm-context-status/{psi}",
amf_ue->supi, sess->psi,
sess->n1_released, sess->n2_released,
OpenAPI_resource_status_ToString(sess->resource_status));
if (sess->n1_released == true &&
sess->n2_released == true &&
sess->resource_status == OpenAPI_resource_status_RELEASED) {
amf_nsmf_pdusession_handle_release_sm_context(
sess, AMF_RELEASE_SM_CONTEXT_NO_STATE);
}
cleanup:
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;
}
int amf_namf_callback_handle_dereg_notify(
ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
int r, state, 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_deregistration_data_t *DeregistrationData;
ogs_assert(stream);
ogs_assert(recvmsg);
if (!recvmsg->h.resource.component[0]) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("No SUPI");
goto cleanup;
}
amf_ue = amf_ue_find_by_supi(recvmsg->h.resource.component[0]);
if (!amf_ue) {
status = OGS_SBI_HTTP_STATUS_NOT_FOUND;
ogs_error("Cannot find SUPI [%s]", recvmsg->h.resource.component[0]);
goto cleanup;
}
DeregistrationData = recvmsg->DeregistrationData;
if (!DeregistrationData) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s] No DeregistrationData", amf_ue->supi);
goto cleanup;
}
if (DeregistrationData->dereg_reason ==
OpenAPI_deregistration_reason_NULL) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s] No Deregistraion Reason ", amf_ue->supi);
goto cleanup;
}
if (DeregistrationData->access_type != OpenAPI_access_type_3GPP_ACCESS) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("[%s] Deregistration access type not 3GPP", amf_ue->supi);
goto cleanup;
}
ogs_info("Deregistration notify reason: %s:%s:%s",
amf_ue->supi,
OpenAPI_deregistration_reason_ToString(DeregistrationData->dereg_reason),
OpenAPI_access_type_ToString(DeregistrationData->access_type));
/*
* TODO: do not start deregistration if UE has emergency sessions
* 4.2.2.3.3
* If the UE has established PDU Session associated with emergency service, the AMF shall not initiate
* Deregistration procedure. In this case, the AMF performs network requested PDU Session Release for any PDU
* session associated with non-emergency service as described in clause 4.3.4.
*/
/*
* - AMF_NETWORK_INITIATED_EXPLICIT_DE_REGISTERED
* 1. UDM_UECM_DeregistrationNotification
* 2. Deregistration request
* 3. UDM_SDM_Unsubscribe
* 4. UDM_UECM_Deregisration
* 5. PDU session release request
* 6. PDUSessionResourceReleaseCommand +
* PDU session release command
* 7. PDUSessionResourceReleaseResponse
* 8. AM_Policy_Association_Termination
* 9. Deregistration accept
* 10. Signalling Connecion Release
*/
if (CM_CONNECTED(amf_ue)) {
r = nas_5gs_send_de_registration_request(
amf_ue,
DeregistrationData->dereg_reason,
OGS_5GMM_CAUSE_5GS_SERVICES_NOT_ALLOWED);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
state = AMF_NETWORK_INITIATED_EXPLICIT_DE_REGISTERED;
} else if (CM_IDLE(amf_ue)) {
ogs_error("Not implemented : Use Implicit De-registration");
state = AMF_NETWORK_INITIATED_IMPLICIT_DE_REGISTERED;
} else {
ogs_fatal("Invalid State");
ogs_assert_if_reached();
}
if (UDM_SDM_SUBSCRIBED(amf_ue)) {
r = amf_ue_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_SDM, NULL,
amf_nudm_sdm_build_subscription_delete,
amf_ue, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (PCF_AM_POLICY_ASSOCIATED(amf_ue)) {
r = amf_ue_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NPCF_AM_POLICY_CONTROL,
NULL,
amf_npcf_am_policy_control_build_delete,
amf_ue, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
}
cleanup:
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;
}
static int update_rat_res_add_one(cJSON *restriction,
OpenAPI_list_t *restrictions, long index)
{
void *restr;
if (!cJSON_IsString(restriction)) {
ogs_error("Invalid type of ratRestriction element");
return OGS_ERROR;
}
restr = (void *) OpenAPI_rat_type_FromString(cJSON_GetStringValue(restriction));
if (!restr) {
ogs_error("No restr");
return OGS_ERROR;
}
if (index == restrictions->count) {
OpenAPI_list_add(restrictions, restr);
} else if (restrictions->count < index && index <= 0) {
OpenAPI_list_insert_prev(
restrictions, OpenAPI_list_find(restrictions, index), restr);
} else {
ogs_error("Can't add RAT restriction to invalid index");
return OGS_ERROR;
}
return OGS_OK;
}
static int update_rat_res_array(cJSON *json_restrictions,
OpenAPI_list_t *restrictions)
{
cJSON *restriction;
if (!cJSON_IsArray(json_restrictions)) {
ogs_error("Invalid type of ratRestrictions");
return OGS_ERROR;
}
OpenAPI_list_clear(restrictions);
cJSON_ArrayForEach(restriction, json_restrictions) {
if (update_rat_res_add_one(restriction, restrictions,
restrictions->count) != OGS_OK) {
return OGS_ERROR;
}
}
return OGS_OK;
}
static int update_rat_res(OpenAPI_change_item_t *item_change,
OpenAPI_list_t *restrictions)
{
cJSON* json = item_change->new_value->json;
cJSON* json_restrictions;
if (!item_change->path) {
return OGS_ERROR;
}
switch (item_change->op) {
case OpenAPI_change_type_REPLACE:
case OpenAPI_change_type_ADD:
if (!strcmp(item_change->path, "")) {
if (!cJSON_IsObject(json)) {
ogs_error("Invalid type of am-data");
}
json_restrictions = cJSON_GetObjectItemCaseSensitive(
json, "ratRestrictions");
if (json_restrictions) {
return update_rat_res_array(json_restrictions, restrictions);
} else {
return OGS_OK;
}
} else if (!strcmp(item_change->path, "/ratRestrictions")) {
return update_rat_res_array(json, restrictions);
} else if (strstr(item_change->path, "/ratRestrictions/") ==
item_change->path) {
char *index = item_change->path + strlen("/ratRestrictions/");
long i = strcmp(index, "-") ? atol(index) : restrictions->count;
return update_rat_res_add_one(json, restrictions, i);
}
return OGS_OK;
case OpenAPI_change_type__REMOVE:
if (!strcmp(item_change->path, "")) {
OpenAPI_list_clear(restrictions);
return OGS_OK;
} else if (!strcmp(item_change->path, "/ratRestrictions")) {
OpenAPI_list_clear(restrictions);
return OGS_OK;
} else if (strstr(item_change->path, "/ratRestrictions/") ==
item_change->path) {
char *index = item_change->path + strlen("/ratRestrictions/");
long i = atol(index);
if (restrictions->count < i && i <= 0) {
OpenAPI_list_remove(
restrictions, OpenAPI_list_find(restrictions, i));
} else {
ogs_error("Can't add RAT restriction to invalid index");
return OGS_ERROR;
}
}
return OGS_OK;
default:
return OGS_OK;
}
}
static int update_ambr_check_one(cJSON *obj, uint64_t *limit,
bool *ambr_changed)
{
if (!cJSON_IsString(obj)) {
ogs_error("Invalid type of subscribedUeAmbr");
return OGS_ERROR;
}
*limit = ogs_sbi_bitrate_from_string(obj->valuestring);
*ambr_changed = true;
return OGS_OK;
}
static int update_ambr_check_obj(cJSON *obj, ogs_bitrate_t *ambr,
bool *ambr_changed)
{
if (!cJSON_IsObject(obj)) {
if (obj == NULL || cJSON_IsNull(obj)) {
/* Limit of 0 means unlimited. */
ambr->uplink = 0;
ambr->downlink = 0;
*ambr_changed = true;
return OGS_OK;
} else {
ogs_error("Invalid type of subscribedUeAmbr");
return OGS_ERROR;
}
}
if (update_ambr_check_one(
cJSON_GetObjectItemCaseSensitive(obj, "uplink"),
&ambr->uplink, ambr_changed)) {
return OGS_ERROR;
}
if (update_ambr_check_one(
cJSON_GetObjectItemCaseSensitive(obj, "downlink"),
&ambr->downlink, ambr_changed)) {
return OGS_ERROR;
}
return OGS_OK;
}
static int update_ambr(OpenAPI_change_item_t *item_change,
ogs_bitrate_t *ambr, bool *ambr_changed)
{
cJSON* json = item_change->new_value->json;
if (!item_change->path) {
return OGS_ERROR;
}
switch (item_change->op) {
case OpenAPI_change_type_REPLACE:
case OpenAPI_change_type_ADD:
if (!strcmp(item_change->path, "")) {
if (!cJSON_IsObject(json)) {
ogs_error("Invalid type of am-data");
}
return update_ambr_check_obj(
cJSON_GetObjectItemCaseSensitive(json, "subscribedUeAmbr"),
ambr, ambr_changed);
} else if (!strcmp(item_change->path, "/subscribedUeAmbr")) {
return update_ambr_check_obj(json, ambr, ambr_changed);
} else if (!strcmp(item_change->path, "/subscribedUeAmbr/uplink")) {
return update_ambr_check_one(json, &ambr->uplink, ambr_changed);
} else if (!strcmp(item_change->path, "/subscribedUeAmbr/downlink")) {
return update_ambr_check_one(json, &ambr->downlink, ambr_changed);
}
return OGS_OK;
case OpenAPI_change_type__REMOVE:
if (!strcmp(item_change->path, "/subscribedUeAmbr")) {
update_ambr_check_obj(NULL, ambr, ambr_changed);
}
return OGS_OK;
default:
return OGS_OK;
}
}
int amf_namf_callback_handle_sdm_data_change_notify(
ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
int r, state, 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;
bool ambr_changed = false;
ogs_assert(stream);
ogs_assert(recvmsg);
ModificationNotification = recvmsg->ModificationNotification;
if (!ModificationNotification) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
ogs_error("No ModificationNotification");
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 *change_item = node_ci->data;
if (update_rat_res(change_item, amf_ue->rat_restrictions) ||
update_ambr(change_item, &amf_ue->ue_ambr,
&ambr_changed)) {
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
goto cleanup;
}
}
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;
}
if (amf_ue) {
ran_ue_t *ran_ue = ran_ue_cycle(amf_ue->ran_ue);
if (!ran_ue) {
ogs_error("NG context has already been removed");
/* ran_ue is required for amf_ue_is_rat_restricted() */
ogs_error("Not implemented : Use Implicit De-registration");
state = AMF_NETWORK_INITIATED_IMPLICIT_DE_REGISTERED;
} else if (amf_ue_is_rat_restricted(amf_ue)) {
/*
* - AMF_NETWORK_INITIATED_EXPLICIT_DE_REGISTERED
* 1. UDM_UECM_DeregistrationNotification
* 2. Deregistration request
* 3. UDM_SDM_Unsubscribe
* 4. UDM_UECM_Deregistration
* 5. PDU session release request
* 6. PDUSessionResourceReleaseCommand +
* PDU session release command
* 7. PDUSessionResourceReleaseResponse
* 8. AM_Policy_Association_Termination
* 9. Deregistration accept
* 10. Signalling Connection Release
*/
r = nas_5gs_send_de_registration_request(
amf_ue,
OpenAPI_deregistration_reason_REREGISTRATION_REQUIRED, 0);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
state = AMF_NETWORK_INITIATED_EXPLICIT_DE_REGISTERED;
if (UDM_SDM_SUBSCRIBED(amf_ue)) {
r = amf_ue_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_SDM, NULL,
amf_nudm_sdm_build_subscription_delete,
amf_ue, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (PCF_AM_POLICY_ASSOCIATED(amf_ue)) {
r = amf_ue_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NPCF_AM_POLICY_CONTROL,
NULL,
amf_npcf_am_policy_control_build_delete,
amf_ue, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
}
} else if (ambr_changed) {
ogs_pkbuf_t *ngapbuf;
ngapbuf = ngap_build_ue_context_modification_request(amf_ue);
ogs_assert(ngapbuf);
r = ngap_send_to_ran_ue(ran_ue, ngapbuf);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
}
}
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;
}
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;
}