Merge remote-tracking branch 'origin/nr-paging-cu' into integration_2026_w08 (!3893)
NR Paging on CU
Implements the NR paging path on the CU:
- NGAP Paging is decoded and passed to RRC
- RRC builds F1AP Paging, and distributes it to DUs via F1 callback
Changes:
- Refactor and extend F1AP Paging encode/decode libraries
* add missing enc/dec/eq/cp functions
* add unit test
* IEs: CN UE 5G-S-TMSI, RAN UE I-RNTI; optional DRX/priority/origin)
- Refactor CU callback CU_send_Paging
- Refactor DU handler DU_handle_Paging
- Refactor old F1 callback for Paging indication, extend it and align
with existing F1 callbacks in stack
- Refactor and extend NGAP Paging encode/decode lib
* add missing enc/dec/eq/cp functions
* add unit test
* Fixes NGAP paging to accept Paging only on SCTP stream 0 per TS
38.412
- Includes supporting refactors (PDU session split, shared 5G-S-TMSI
helpers, NGAP/F1AP cleanups)
Testing
- NGAP and F1AP Paging lib unit tests (enc/dec)
- Stream-0 fix validated with Open5GS AMF
This MR does not address the DU changes besides the F1 handler. F1AP
Paging does not have an RRC container to pass the PCCH message to the
DU, and F1 DL RRC Message Transfer does not apply to PCCH. PCCH-Message
shall be built at DU per TS 38.331 §5.3.2, with padding as per §8.5 and
delivered as RLC SDU per §8.2. For each cell in Paging Cell List that
belongs to this DU, queue for MAC. MAC schedules at PF/PO per TS 38.304
§7.
Refs:
- F1AP 3GPP TS 38.473 version 16.21.0 Release 16
- NGAP 3GPP TS 38.413 version 16.17.0 Release 16
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
#include <stdint.h>
|
||||
#include "OctetString.h"
|
||||
#include "fgs_nas_utils.h"
|
||||
#include "common/utils/utils.h" // text_info_t, TO_ENUM, TO_TEXT
|
||||
|
||||
#ifndef FGS_MOBILE_IDENTITY_H_
|
||||
#define FGS_MOBILE_IDENTITY_H_
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <stdint.h>
|
||||
#include "FGSNASSecurityModeReject.h"
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
#include "fgmm_lib.h"
|
||||
|
||||
int encode_fgs_security_mode_reject(byte_array_t *buffer, const fgs_security_mode_reject_msg *msg)
|
||||
@@ -45,6 +46,6 @@ int decode_fgs_security_mode_reject(fgs_security_mode_reject_msg *msg, const byt
|
||||
|
||||
bool eq_sec_mode_reject(const fgs_security_mode_reject_msg *a, const fgs_security_mode_reject_msg *b)
|
||||
{
|
||||
_NAS_EQ_CHECK_INT(a->cause, b->cause);
|
||||
_EQ_CHECK_INT(a->cause, b->cause);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "conversions.h"
|
||||
#include "RegistrationAccept.h"
|
||||
#include "fgs_nas_utils.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
#include "common/utils/utils.h"
|
||||
#include "ds/byte_array.h"
|
||||
#include "fgmm_lib.h"
|
||||
@@ -47,7 +48,7 @@
|
||||
static int decode_fgs_registration_result(registration_accept_msg *out, const byte_array_t buffer)
|
||||
{
|
||||
if (buffer.len < FGS_REGISTRATION_RESULT_LEN) {
|
||||
PRINT_NAS_ERROR("5GS registration result decoding failure: invalid buffer length\n");
|
||||
PRINT_ERROR("5GS registration result decoding failure: invalid buffer length\n");
|
||||
return -1;
|
||||
}
|
||||
out->result = buffer.buf[1] & 0x7; // skip length of contents octet
|
||||
@@ -58,7 +59,7 @@ static int decode_fgs_registration_result(registration_accept_msg *out, const by
|
||||
static int encode_fgs_registration_result(byte_array_t buffer, const registration_accept_msg *out)
|
||||
{
|
||||
if (buffer.len < FGS_REGISTRATION_RESULT_LEN) {
|
||||
PRINT_NAS_ERROR("5GS registration result encoding failure: invalid buffer length\n");
|
||||
PRINT_ERROR("5GS registration result encoding failure: invalid buffer length\n");
|
||||
return -1;
|
||||
}
|
||||
uint8_t *buf = buffer.buf;
|
||||
@@ -125,7 +126,7 @@ static int decode_nssai_ie(nr_nas_msg_snssai_t *nssai, uint8_t *num_slices, uint
|
||||
break;
|
||||
|
||||
default:
|
||||
PRINT_NAS_ERROR("Unknown length in Allowed S-NSSAI list item\n");
|
||||
PRINT_ERROR("Unknown length in Allowed S-NSSAI list item\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -139,7 +140,7 @@ size_t decode_registration_accept(registration_accept_msg *registration_accept,
|
||||
byte_array_t ba = buffer; // local copy of buffer
|
||||
|
||||
if (ba.len < REGISTRATION_ACCEPT_MIN_LEN) {
|
||||
PRINT_NAS_ERROR("Registration Accept decoding failed: invalid buffer length\n");
|
||||
PRINT_ERROR("Registration Accept decoding failed: invalid buffer length\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -152,7 +153,7 @@ size_t decode_registration_accept(registration_accept_msg *registration_accept,
|
||||
if (ba.len > 0 && ba.buf[0] == IEI_5G_GUTI) {
|
||||
registration_accept->guti = calloc_or_fail(1, sizeof(*registration_accept->guti));
|
||||
if ((dec = decode_5gs_mobile_identity(registration_accept->guti, IEI_5G_GUTI, ba.buf, ba.len)) < 0) {
|
||||
PRINT_NAS_ERROR("Failed to decode 5GS Mobile Identity in Registration Accept\n");
|
||||
PRINT_ERROR("Failed to decode 5GS Mobile Identity in Registration Accept\n");
|
||||
return -1;
|
||||
}
|
||||
UPDATE_BYTE_ARRAY(ba, dec);
|
||||
@@ -182,7 +183,7 @@ size_t decode_registration_accept(registration_accept_msg *registration_accept,
|
||||
}
|
||||
}
|
||||
if(ba.len != 0) {
|
||||
PRINT_NAS_ERROR("Failed to decode registration accept: ba.len = %ld\n", ba.len);
|
||||
PRINT_ERROR("Failed to decode registration accept: ba.len = %ld\n", ba.len);
|
||||
return -1;
|
||||
}
|
||||
return buffer.len;
|
||||
@@ -209,22 +210,22 @@ int encode_registration_accept(const registration_accept_msg *registration_accep
|
||||
|
||||
bool eq_snssai(const nr_nas_msg_snssai_t *a, const nr_nas_msg_snssai_t *b)
|
||||
{
|
||||
_NAS_EQ_CHECK_INT(a->sst, b->sst);
|
||||
_EQ_CHECK_INT(a->sst, b->sst);
|
||||
|
||||
if ((a->hplmn_sst && !b->hplmn_sst) || (!a->hplmn_sst && b->hplmn_sst))
|
||||
return false;
|
||||
if (a->hplmn_sst && b->hplmn_sst)
|
||||
_NAS_EQ_CHECK_INT(*a->hplmn_sst, *b->hplmn_sst);
|
||||
_EQ_CHECK_INT(*a->hplmn_sst, *b->hplmn_sst);
|
||||
|
||||
if ((a->sd && !b->sd) || (!a->sd && b->sd))
|
||||
return false;
|
||||
if (a->sd && b->sd)
|
||||
_NAS_EQ_CHECK_INT(*a->sd, *b->sd);
|
||||
_EQ_CHECK_INT(*a->sd, *b->sd);
|
||||
|
||||
if ((a->hplmn_sd && !b->hplmn_sd) || (!a->hplmn_sd && b->hplmn_sd))
|
||||
return false;
|
||||
if (a->hplmn_sd && b->hplmn_sd)
|
||||
_NAS_EQ_CHECK_INT(*a->hplmn_sd, *b->hplmn_sd);
|
||||
_EQ_CHECK_INT(*a->hplmn_sd, *b->hplmn_sd);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -233,48 +234,48 @@ bool eq_snssai(const nr_nas_msg_snssai_t *a, const nr_nas_msg_snssai_t *b)
|
||||
bool eq_fgmm_registration_accept(const registration_accept_msg *a, const registration_accept_msg *b)
|
||||
{
|
||||
if (!a || !b) {
|
||||
PRINT_NAS_ERROR("Null pointer in registration_accept_msg_eq\n");
|
||||
PRINT_ERROR("Null pointer in registration_accept_msg_eq\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5GS registration result (mandatory)
|
||||
_NAS_EQ_CHECK_INT(a->result, b->result);
|
||||
_NAS_EQ_CHECK_INT(a->sms_allowed, b->sms_allowed);
|
||||
_EQ_CHECK_INT(a->result, b->result);
|
||||
_EQ_CHECK_INT(a->sms_allowed, b->sms_allowed);
|
||||
|
||||
// GUTI (optional)
|
||||
if (a->guti && b->guti) {
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.spare, b->guti->guti.spare);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.oddeven, b->guti->guti.oddeven);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.typeofidentity, b->guti->guti.typeofidentity);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.mccdigit2, b->guti->guti.mccdigit2);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.mccdigit1, b->guti->guti.mccdigit1);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.mncdigit3, b->guti->guti.mncdigit3);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.mccdigit3, b->guti->guti.mccdigit3);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.mncdigit2, b->guti->guti.mncdigit2);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.mncdigit1, b->guti->guti.mncdigit1);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.amfregionid, b->guti->guti.amfregionid);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.amfsetid, b->guti->guti.amfsetid);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.amfpointer, b->guti->guti.amfpointer);
|
||||
_NAS_EQ_CHECK_INT(a->guti->guti.tmsi, b->guti->guti.tmsi);
|
||||
_EQ_CHECK_INT(a->guti->guti.spare, b->guti->guti.spare);
|
||||
_EQ_CHECK_INT(a->guti->guti.oddeven, b->guti->guti.oddeven);
|
||||
_EQ_CHECK_INT(a->guti->guti.typeofidentity, b->guti->guti.typeofidentity);
|
||||
_EQ_CHECK_INT(a->guti->guti.mccdigit2, b->guti->guti.mccdigit2);
|
||||
_EQ_CHECK_INT(a->guti->guti.mccdigit1, b->guti->guti.mccdigit1);
|
||||
_EQ_CHECK_INT(a->guti->guti.mncdigit3, b->guti->guti.mncdigit3);
|
||||
_EQ_CHECK_INT(a->guti->guti.mccdigit3, b->guti->guti.mccdigit3);
|
||||
_EQ_CHECK_INT(a->guti->guti.mncdigit2, b->guti->guti.mncdigit2);
|
||||
_EQ_CHECK_INT(a->guti->guti.mncdigit1, b->guti->guti.mncdigit1);
|
||||
_EQ_CHECK_INT(a->guti->guti.amfregionid, b->guti->guti.amfregionid);
|
||||
_EQ_CHECK_INT(a->guti->guti.amfsetid, b->guti->guti.amfsetid);
|
||||
_EQ_CHECK_INT(a->guti->guti.amfpointer, b->guti->guti.amfpointer);
|
||||
_EQ_CHECK_INT(a->guti->guti.tmsi, b->guti->guti.tmsi);
|
||||
} else if ((a->guti && !b->guti) || (!a->guti && b->guti)) {
|
||||
PRINT_NAS_ERROR("NAS Equality Check failure: One of the two GUTIs is NULL\n");
|
||||
PRINT_ERROR("NAS Equality Check failure: One of the two GUTIs is NULL\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
_NAS_EQ_CHECK_INT(a->num_allowed_slices, b->num_allowed_slices);
|
||||
_NAS_EQ_CHECK_INT(a->num_configured_slices, b->num_configured_slices);
|
||||
_EQ_CHECK_INT(a->num_allowed_slices, b->num_allowed_slices);
|
||||
_EQ_CHECK_INT(a->num_configured_slices, b->num_configured_slices);
|
||||
|
||||
// Configured NSSAI (optional)
|
||||
for (int i = 0; i < a->num_configured_slices; i++) {
|
||||
if (!eq_snssai(&a->config_nssai[i], &b->config_nssai[i])) {
|
||||
PRINT_NAS_ERROR("NAS Equality Check failure: Configured NSSAI\n");
|
||||
PRINT_ERROR("NAS Equality Check failure: Configured NSSAI\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Allowed NSSAI (optional)
|
||||
for (int i = 0; i < a->num_allowed_slices; i++) {
|
||||
if (!eq_snssai(&a->nas_allowed_nssai[i], &b->nas_allowed_nssai[i])) {
|
||||
PRINT_NAS_ERROR("NAS Equality Check failure: Allowed NSSAI\n");
|
||||
PRINT_ERROR("NAS Equality Check failure: Allowed NSSAI\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <stdlib.h> // For malloc and free
|
||||
#include "fgmm_lib.h"
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
#include "common/utils/utils.h"
|
||||
|
||||
#define MIN_AUTH_FAILURE_LEN 1
|
||||
@@ -35,7 +36,7 @@
|
||||
int encode_fgmm_auth_failure(byte_array_t *buffer, const fgmm_auth_failure_t *msg)
|
||||
{
|
||||
if (buffer->len < MIN_AUTH_FAILURE_LEN) {
|
||||
PRINT_NAS_ERROR("Missing mandatory IE in Authentication Failure");
|
||||
PRINT_ERROR("Missing mandatory IE in Authentication Failure");
|
||||
return -1; // missing mandatory IE
|
||||
}
|
||||
// 5GMM cause (Mandatory)
|
||||
@@ -44,11 +45,11 @@ int encode_fgmm_auth_failure(byte_array_t *buffer, const fgmm_auth_failure_t *ms
|
||||
if (msg->cause == Synch_failure) {
|
||||
// Authentication failure parameter shall be present with cause Synch Failure
|
||||
if (msg->authentication_failure_param.len == 0) {
|
||||
PRINT_NAS_ERROR("Authentication Failure Cause is synch failure but Authentication Failure Parameter is not available");
|
||||
PRINT_ERROR("Authentication Failure Cause is synch failure but Authentication Failure Parameter is not available");
|
||||
return -1;
|
||||
}
|
||||
if (buffer->len - encoded < msg->authentication_failure_param.len + 2) {
|
||||
PRINT_NAS_ERROR("Invalid buffer for encoding of Authentication Failure Parameter");
|
||||
PRINT_ERROR("Invalid buffer for encoding of Authentication Failure Parameter");
|
||||
return -1;
|
||||
}
|
||||
// Encode the IEI
|
||||
@@ -76,7 +77,7 @@ int decode_fgmm_auth_failure(fgmm_auth_failure_t *msg, const byte_array_t *buffe
|
||||
if (iei == AUTH_FAIL_PARAM_IEI) {
|
||||
msg->authentication_failure_param.len = ba.buf[decoded++];
|
||||
if (msg->authentication_failure_param.len > AUTH_FAILURE_PARAM_LEN - 2) { // maximum length of contents is 14 octets
|
||||
PRINT_NAS_ERROR("Length of Authentication Failure Parameter contents is too large");
|
||||
PRINT_ERROR("Length of Authentication Failure Parameter contents is too large");
|
||||
return -1;
|
||||
}
|
||||
msg->authentication_failure_param.buf = malloc_or_fail(msg->authentication_failure_param.len);
|
||||
@@ -90,7 +91,7 @@ int decode_fgmm_auth_failure(fgmm_auth_failure_t *msg, const byte_array_t *buffe
|
||||
|
||||
bool eq_fgmm_auth_failure(const fgmm_auth_failure_t *a, const fgmm_auth_failure_t *b)
|
||||
{
|
||||
_NAS_EQ_CHECK_INT(a->cause, b->cause);
|
||||
_EQ_CHECK_INT(a->cause, b->cause);
|
||||
eq_byte_array(&a->authentication_failure_param, &b->authentication_failure_param);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <arpa/inet.h> // For htons and ntohs
|
||||
#include <stdlib.h> // For malloc and free
|
||||
#include "fgmm_lib.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
|
||||
#define MIN_AUTH_REJECT_LEN 3
|
||||
|
||||
@@ -55,13 +56,13 @@ int decode_fgmm_auth_reject(fgmm_auth_reject_msg_t *msg, const byte_array_t *buf
|
||||
if (iei == IEI_EAPMSG) {
|
||||
return (decoded + decode_eap_msg_ie(&msg->eap_msg, &ba));
|
||||
}
|
||||
PRINT_NAS_ERROR("Expected EAP MSG but it is not present");
|
||||
PRINT_ERROR("Expected EAP MSG but it is not present");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool eq_auth_reject(fgmm_auth_reject_msg_t *a, fgmm_auth_reject_msg_t *b)
|
||||
{
|
||||
_NAS_EQ_CHECK_LONG(a->eap_msg.len, b->eap_msg.len);
|
||||
_EQ_CHECK_LONG(a->eap_msg.len, b->eap_msg.len);
|
||||
if (a->eap_msg.len > 0) {
|
||||
return !memcmp(a->eap_msg.buf, b->eap_msg.buf, a->eap_msg.len);
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@
|
||||
#include "fgmm_identity_request.h"
|
||||
#include <string.h>
|
||||
#include "FGSMobileIdentity.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
|
||||
int decode_fgs_identity_request(fgs_identity_request_msg_t *msg, const byte_array_t *buffer)
|
||||
{
|
||||
if (buffer->len < 1) {
|
||||
PRINT_NAS_ERROR("Failed to decode NAS Identity Request: invalid buffer length %ld\n", buffer->len);
|
||||
PRINT_ERROR("Failed to decode NAS Identity Request: invalid buffer length %ld\n", buffer->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <arpa/inet.h> // For htons and ntohs
|
||||
#include <stdlib.h> // For malloc and free
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
|
||||
#define IEI_LEN 1
|
||||
#define GPRS_TIMER_LENGTH 3 // octets
|
||||
@@ -41,7 +42,7 @@
|
||||
int encode_pdu_session_ie(byte_array_t *buffer, nas_service_IEI_t iei, const uint8_t *psi)
|
||||
{
|
||||
if (buffer->len < MIN_PDU_SESSION_CONTENTS_LEN + 2) {
|
||||
PRINT_NAS_ERROR("%s failed: buffer is too small", __func__);
|
||||
PRINT_ERROR("%s failed: buffer is too small", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -83,14 +84,14 @@ int decode_pdu_session_ie(uint8_t *psi, const byte_array_t *buffer)
|
||||
uint8_t *buf = buffer->buf;
|
||||
|
||||
if (buffer->len < MIN_PDU_SESSION_CONTENTS_LEN + 2) {
|
||||
PRINT_NAS_ERROR("%s failed: buffer length is too small", __func__);
|
||||
PRINT_ERROR("%s failed: buffer length is too small", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// start decoding from length IE (IEI is decoded in the caller)
|
||||
uint8_t pdu_session_status_len = buf[decoded++];
|
||||
if (pdu_session_status_len > MAX_PDU_SESSION_CONTENTS_LEN) {
|
||||
PRINT_NAS_ERROR("decoded length is out of bound for PDU Session Status/Reactivation result\n");
|
||||
PRINT_ERROR("decoded length is out of bound for PDU Session Status/Reactivation result\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -116,7 +117,7 @@ int decode_pdu_session_ie(uint8_t *psi, const byte_array_t *buffer)
|
||||
int encode_gprs_timer_ie(byte_array_t *buffer, nas_service_IEI_t iei, const gprs_timer_t *timer)
|
||||
{
|
||||
if (buffer->len < GPRS_TIMER_LENGTH) {
|
||||
PRINT_NAS_ERROR("Buffer length is too short to encode the GPRS Timer IE\n");
|
||||
PRINT_ERROR("Buffer length is too short to encode the GPRS Timer IE\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -150,8 +151,8 @@ int decode_gprs_timer_ie(gprs_timer_t *timer, const byte_array_t *buffer)
|
||||
/** @brief Equality check for GPRS Timer */
|
||||
bool eq_gprs_timer(const gprs_timer_t *a, const gprs_timer_t *b)
|
||||
{
|
||||
_NAS_EQ_CHECK_INT(a->value, b->value);
|
||||
_NAS_EQ_CHECK_INT(a->unit, b->unit);
|
||||
_EQ_CHECK_INT(a->value, b->value);
|
||||
_EQ_CHECK_INT(a->unit, b->unit);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -162,13 +163,13 @@ int encode_eap_msg_ie(byte_array_t *buffer, const byte_array_t *msg)
|
||||
|
||||
// Sanity check on buffer length
|
||||
if (buffer->len < MIN_EAP_MSG_LEN) {
|
||||
PRINT_NAS_ERROR("Invalid buffer length %ld", buffer->len);
|
||||
PRINT_ERROR("Invalid buffer length %ld", buffer->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Sanity check on input length
|
||||
if (msg->len < MIN_EAP_CONTENTS_LEN || msg->len > MAX_EAP_CONTENTS_LEN) {
|
||||
PRINT_NAS_ERROR("Invalid EAP message length %ld", msg->len);
|
||||
PRINT_ERROR("Invalid EAP message length %ld", msg->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -196,7 +197,7 @@ int decode_eap_msg_ie(byte_array_t *eap_msg, const byte_array_t *buffer)
|
||||
|
||||
// Check buffer length validity
|
||||
if (buffer->len < min_len_to_decode) {
|
||||
PRINT_NAS_ERROR("Invalid buffer for decoding EAP message IE");
|
||||
PRINT_ERROR("Invalid buffer for decoding EAP message IE");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -206,13 +207,13 @@ int decode_eap_msg_ie(byte_array_t *eap_msg, const byte_array_t *buffer)
|
||||
|
||||
// Check payload length validity
|
||||
if (eap_msg->len < MIN_EAP_CONTENTS_LEN || eap_msg->len > MAX_EAP_CONTENTS_LEN) {
|
||||
PRINT_NAS_ERROR("Invalid EAP message length %ld", eap_msg->len);
|
||||
PRINT_ERROR("Invalid EAP message length %ld", eap_msg->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for buffer overflow
|
||||
if (buffer->len < decoded + eap_msg->len) {
|
||||
PRINT_NAS_ERROR("Buffer too short for EAP message: %ld < %ld", buffer->len, decoded + eap_msg->len);
|
||||
PRINT_ERROR("Buffer too short for EAP message: %ld < %ld", buffer->len, decoded + eap_msg->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <stdbool.h>
|
||||
#include "fgs_nas_utils.h"
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
#include "common/utils/utils.h"
|
||||
|
||||
/* The following defines are valide for
|
||||
both PDU Session Status and PDU session Reactivation Result IEs */
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <stdlib.h> // For malloc and free
|
||||
#include "common/utils/utils.h" // For malloc_or_fail
|
||||
#include "common/utils/ds/byte_array.h" // For byte_array_t
|
||||
#include "common/utils/eq_check.h"
|
||||
#include "fgmm_lib.h"
|
||||
|
||||
/** @brief Encode Service Accept (8.2.17 of 3GPP TS 24.501) */
|
||||
@@ -53,7 +54,7 @@ int encode_fgs_service_accept(byte_array_t *buffer, const fgs_service_accept_msg
|
||||
// encode length of PDU session reactivation result error cause
|
||||
uint16_t error_len = msg->num_errors * sizeof(msg->cause[0]);
|
||||
if (error_len > MAX_NUM_PDU_ERRORS) {
|
||||
PRINT_NAS_ERROR("encoded length is out of bound (IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE)\n");
|
||||
PRINT_ERROR("encoded length is out of bound (IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE)\n");
|
||||
return -1;
|
||||
}
|
||||
uint16_t n_len = htons(error_len);
|
||||
@@ -115,7 +116,7 @@ int decode_fgs_service_accept(fgs_service_accept_msg_t *msg, const byte_array_t
|
||||
uint16_t error_len = ntohs(tmp);
|
||||
decoded += sizeof(error_len);
|
||||
if (error_len > MAX_NUM_PDU_ERRORS * sizeof(msg->cause[0])) {
|
||||
PRINT_NAS_ERROR("IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE: decoded length is out of bound\n");
|
||||
PRINT_ERROR("IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE: decoded length is out of bound\n");
|
||||
return -1;
|
||||
}
|
||||
// Decode each PDU Session ID and Cause
|
||||
@@ -137,7 +138,7 @@ int decode_fgs_service_accept(fgs_service_accept_msg_t *msg, const byte_array_t
|
||||
break;
|
||||
|
||||
default:
|
||||
PRINT_NAS_ERROR("Unkwown or invalid IEI %d\n", iei);
|
||||
PRINT_ERROR("Unkwown or invalid IEI %d\n", iei);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -150,26 +151,26 @@ int decode_fgs_service_accept(fgs_service_accept_msg_t *msg, const byte_array_t
|
||||
*/
|
||||
bool eq_service_accept(const fgs_service_accept_msg_t *a, const fgs_service_accept_msg_t *b)
|
||||
{
|
||||
_NAS_EQ_CHECK_INT(a->has_psi_status, b->has_psi_status);
|
||||
_EQ_CHECK_INT(a->has_psi_status, b->has_psi_status);
|
||||
if (a->has_psi_status && b->has_psi_status) {
|
||||
for (int i = 0; i < MAX_NUM_PSI; i++)
|
||||
_NAS_EQ_CHECK_INT(a->psi_status[i], b->psi_status[i]);
|
||||
_EQ_CHECK_INT(a->psi_status[i], b->psi_status[i]);
|
||||
}
|
||||
_NAS_EQ_CHECK_INT(a->has_psi_res, b->has_psi_res);
|
||||
_EQ_CHECK_INT(a->has_psi_res, b->has_psi_res);
|
||||
if (a->has_psi_res && b->has_psi_res) {
|
||||
for (int i = 0; i < MAX_NUM_PSI; i++)
|
||||
_NAS_EQ_CHECK_INT(a->psi_res[i], b->psi_res[i]);
|
||||
_EQ_CHECK_INT(a->psi_res[i], b->psi_res[i]);
|
||||
}
|
||||
_NAS_EQ_CHECK_INT(a->num_errors, b->num_errors);
|
||||
_EQ_CHECK_INT(a->num_errors, b->num_errors);
|
||||
if (a->num_errors && b->num_errors) {
|
||||
for (int i = 0; i < a->num_errors; i++) {
|
||||
_NAS_EQ_CHECK_INT(a->cause[i].cause, b->cause[i].cause);
|
||||
_NAS_EQ_CHECK_INT(a->cause[i].pdu_session_id, b->cause[i].pdu_session_id);
|
||||
_EQ_CHECK_INT(a->cause[i].cause, b->cause[i].cause);
|
||||
_EQ_CHECK_INT(a->cause[i].pdu_session_id, b->cause[i].pdu_session_id);
|
||||
}
|
||||
}
|
||||
if (a->t3448 != NULL || b->t3448 != NULL) {
|
||||
if (a->t3448 == NULL || b->t3448 == NULL) {
|
||||
PRINT_NAS_ERROR("t3448 equality check failed\n");
|
||||
PRINT_ERROR("t3448 equality check failed\n");
|
||||
return false;
|
||||
}
|
||||
if (!eq_gprs_timer(a->t3448, b->t3448))
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <stdlib.h> // For malloc and free
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
#include "common/utils/utils.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
#include "fgmm_lib.h"
|
||||
|
||||
#define SERVICE_REJECT_MIN_LEN 1 // 1 octet
|
||||
@@ -33,7 +34,7 @@
|
||||
int encode_fgs_service_reject(byte_array_t *buffer, const fgs_service_reject_msg_t *msg)
|
||||
{
|
||||
if (buffer->len < SERVICE_REJECT_MIN_LEN) {
|
||||
PRINT_NAS_ERROR("Failed to encode Service Reject: missing Cause IE!\n");
|
||||
PRINT_ERROR("Failed to encode Service Reject: missing Cause IE!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -71,7 +72,7 @@ int encode_fgs_service_reject(byte_array_t *buffer, const fgs_service_reject_msg
|
||||
int decode_fgs_service_reject(fgs_service_reject_msg_t *msg, const byte_array_t *buffer)
|
||||
{
|
||||
if (buffer->len < SERVICE_REJECT_MIN_LEN) {
|
||||
PRINT_NAS_ERROR("Nothing to decode: missing Cause IE!\n");
|
||||
PRINT_ERROR("Nothing to decode: missing Cause IE!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -112,11 +113,11 @@ int decode_fgs_service_reject(fgs_service_reject_msg_t *msg, const byte_array_t
|
||||
int cag_len = cag_list_len + 2; // encoded content + 2 octets for the length IE
|
||||
decoded += cag_len;
|
||||
UPDATE_BYTE_ARRAY(ba, cag_len);
|
||||
PRINT_NAS_ERROR("CAG information list IE in NAS Service Reject is not handled\n");
|
||||
PRINT_ERROR("CAG information list IE in NAS Service Reject is not handled\n");
|
||||
} break;
|
||||
|
||||
default:
|
||||
PRINT_NAS_ERROR("Uknown or invalid NAS Service Reject IEI %d\n", iei);
|
||||
PRINT_ERROR("Uknown or invalid NAS Service Reject IEI %d\n", iei);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -127,15 +128,15 @@ int decode_fgs_service_reject(fgs_service_reject_msg_t *msg, const byte_array_t
|
||||
/** @brief Equality check for Service Reject (enc/dec) */
|
||||
bool eq_service_reject(const fgs_service_reject_msg_t *a, const fgs_service_reject_msg_t *b)
|
||||
{
|
||||
_NAS_EQ_CHECK_INT(a->cause, b->cause);
|
||||
_NAS_EQ_CHECK_INT(a->has_psi_status, b->has_psi_status);
|
||||
_EQ_CHECK_INT(a->cause, b->cause);
|
||||
_EQ_CHECK_INT(a->has_psi_status, b->has_psi_status);
|
||||
if (a->has_psi_status && b->has_psi_status) {
|
||||
for (int i = 0; i < MAX_NUM_PSI; i++)
|
||||
_NAS_EQ_CHECK_INT(a->psi_status[i], b->psi_status[i]);
|
||||
_EQ_CHECK_INT(a->psi_status[i], b->psi_status[i]);
|
||||
}
|
||||
if (a->t3448 != NULL || b->t3448 != NULL) {
|
||||
if (a->t3448 == NULL || b->t3448 == NULL) {
|
||||
PRINT_NAS_ERROR("t3448 equality check failed\n");
|
||||
PRINT_ERROR("t3448 equality check failed\n");
|
||||
return false;
|
||||
}
|
||||
if (!eq_gprs_timer(a->t3448, b->t3448))
|
||||
@@ -143,7 +144,7 @@ bool eq_service_reject(const fgs_service_reject_msg_t *a, const fgs_service_reje
|
||||
}
|
||||
if (a->t3446 != NULL || b->t3446 != NULL) {
|
||||
if (a->t3446 == NULL || b->t3446 == NULL) {
|
||||
PRINT_NAS_ERROR("t3448 equality check failed\n");
|
||||
PRINT_ERROR("t3448 equality check failed\n");
|
||||
return false;
|
||||
}
|
||||
if (!eq_gprs_timer(a->t3446, b->t3446))
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <arpa/inet.h>
|
||||
#include "PduSessionEstablishmentAccept.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/utils/utils.h" // text_info_t, TO_ENUM, TO_TEXT
|
||||
#include "fgs_nas_utils.h"
|
||||
|
||||
/**
|
||||
@@ -205,7 +206,7 @@ int decode_pdu_session_establishment_accept_msg(pdu_session_establishment_accept
|
||||
}
|
||||
|
||||
default:
|
||||
PRINT_NAS_ERROR("Unknown IEI %d\n", psea_iei);
|
||||
PRINT_ERROR("Unknown IEI %d\n", psea_iei);
|
||||
curPtr = buffer + msg_length; // we force stop processing
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "fgs_nas_utils.h"
|
||||
#include "common/utils/utils.h" // text_info_t, TO_ENUM, TO_TEXT
|
||||
|
||||
/* PDU Session Establish Accept Optional IE Identifiers - TS 24.501 Table 8.3.2.1.1 */
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ add_subdirectory(5GMM/IES)
|
||||
add_subdirectory(5GSM/MSG)
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
target_compile_definitions(fgs_5gmm_lib PRIVATE ENABLE_TESTS)
|
||||
target_compile_definitions(fgs_5gmm_ies_lib PRIVATE ENABLE_TESTS)
|
||||
target_compile_definitions(fgs_5gsm_lib PRIVATE ENABLE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
add_library(fgs_lib fgs_nas_lib.c)
|
||||
@@ -16,3 +19,7 @@ target_link_libraries(fgs_lib PUBLIC
|
||||
)
|
||||
|
||||
target_include_directories(fgs_lib PUBLIC . 5GMM/MSG 5GMM/IES)
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
target_compile_definitions(fgs_lib PRIVATE ENABLE_TESTS)
|
||||
endif()
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "NR_NAS_defs.h"
|
||||
#include "nas_log.h"
|
||||
#include "TLVEncoder.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
#include "fgs_nas_utils.h"
|
||||
|
||||
int _nas_mm_msg_encode_header(const fgmm_msg_header_t *header, uint8_t *buffer, uint32_t len)
|
||||
@@ -56,7 +57,7 @@ int mm_msg_encode(const fgmm_nas_message_plain_t *p, uint8_t *buffer, uint32_t l
|
||||
int enc_header = _nas_mm_msg_encode_header(&p->header, buffer, len);
|
||||
|
||||
if (enc_header < 0) {
|
||||
PRINT_NAS_ERROR("Failed to encode 5GMM message header\n");
|
||||
PRINT_ERROR("Failed to encode 5GMM message header\n");
|
||||
return enc_header;
|
||||
}
|
||||
|
||||
@@ -104,7 +105,7 @@ int mm_msg_encode(const fgmm_nas_message_plain_t *p, uint8_t *buffer, uint32_t l
|
||||
}
|
||||
|
||||
if (enc_msg < 0) {
|
||||
PRINT_NAS_ERROR("Failed to encode 5GMM message\n");
|
||||
PRINT_ERROR("Failed to encode 5GMM message\n");
|
||||
return enc_msg;
|
||||
}
|
||||
|
||||
@@ -116,7 +117,7 @@ int nas_protected_security_header_encode(uint8_t *buffer, const fgs_nas_message_
|
||||
int size = 0;
|
||||
|
||||
if (length < sizeof(fgs_nas_message_security_header_t)) {
|
||||
PRINT_NAS_ERROR("Could not encode the NAS security header\n");
|
||||
PRINT_ERROR("Could not encode the NAS security header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -140,7 +141,7 @@ int nas_protected_security_header_encode(uint8_t *buffer, const fgs_nas_message_
|
||||
uint8_t decode_5gmm_msg_header(fgmm_msg_header_t *mm_header, const uint8_t *buffer, uint32_t len)
|
||||
{
|
||||
if (len < sizeof(fgmm_msg_header_t)) {
|
||||
PRINT_NAS_ERROR("Failed to decode plain 5GMM header: buffer length too short\n");
|
||||
PRINT_ERROR("Failed to decode plain 5GMM header: buffer length too short\n");
|
||||
return -1;
|
||||
}
|
||||
mm_header->ex_protocol_discriminator = *buffer++;
|
||||
@@ -155,7 +156,7 @@ uint8_t decode_5gmm_msg_header(fgmm_msg_header_t *mm_header, const uint8_t *buff
|
||||
uint8_t decode_5gsm_msg_header(fgsm_msg_header_t *sm_header, const uint8_t *buffer, uint32_t len)
|
||||
{
|
||||
if (len < sizeof(fgsm_msg_header_t)) {
|
||||
PRINT_NAS_ERROR("Failed to decode plain 5GSM header: buffer length too short\n");
|
||||
PRINT_ERROR("Failed to decode plain 5GSM header: buffer length too short\n");
|
||||
return -1;
|
||||
}
|
||||
sm_header->ex_protocol_discriminator = *buffer++;
|
||||
@@ -172,7 +173,7 @@ int decode_5gs_security_protected_header(fgs_nas_message_security_header_t *head
|
||||
{
|
||||
int decoded = 0;
|
||||
if (len < sizeof(fgs_nas_message_security_header_t)) {
|
||||
PRINT_NAS_ERROR("Failed to decode security protected 5GS header: buffer length too short\n");
|
||||
PRINT_ERROR("Failed to decode security protected 5GS header: buffer length too short\n");
|
||||
return -1;
|
||||
}
|
||||
header->protocol_discriminator = *buf++;
|
||||
|
||||
@@ -26,33 +26,8 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h> // For memcpy
|
||||
#include <stdio.h>
|
||||
|
||||
#define PRINT_NAS_ERROR(...) fprintf(stderr, ##__VA_ARGS__)
|
||||
|
||||
#define _NAS_EQ_CHECK_GENERIC(condition, fmt, ...) \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
PRINT_NAS_ERROR("NAS Equality Check failure: %s:%d: Condition '%s' failed: " fmt " != " fmt "\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
#condition, \
|
||||
##__VA_ARGS__); \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define _NAS_EQ_CHECK_LONG(A, B) _NAS_EQ_CHECK_GENERIC(A == B, "%ld", A, B);
|
||||
#define _NAS_EQ_CHECK_INT(A, B) _NAS_EQ_CHECK_GENERIC(A == B, "%d", A, B);
|
||||
#define _NAS_EQ_CHECK_STR(A, B) _NAS_EQ_CHECK_GENERIC(strcmp(A, B) == 0, "'%s'", A, B);
|
||||
|
||||
/* Map task id to printable name. */
|
||||
typedef struct {
|
||||
int id;
|
||||
char text[256];
|
||||
} text_info_t;
|
||||
|
||||
#define TO_TEXT(LabEl, nUmID) {nUmID, #LabEl},
|
||||
#define TO_ENUM(LabEl, nUmID ) LabEl = nUmID,
|
||||
#include "utils.h" // text_info_t, TO_ENUM, TO_TEXT
|
||||
#include "common/utils/eq_check.h"
|
||||
|
||||
#define GET_SHORT(input, size) ({ \
|
||||
uint16_t tmp16; \
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
add_subdirectory(MESSAGES)
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
@@ -27,9 +27,30 @@
|
||||
* \version 0.1
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "conversions.h"
|
||||
#include "ngap_common.h"
|
||||
#include "ngap_msg_includes.h"
|
||||
|
||||
void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in)
|
||||
{
|
||||
if (in.length) {
|
||||
out->buf = malloc_or_fail(in.length);
|
||||
memcpy(out->buf, in.buffer, in.length);
|
||||
out->size = in.length;
|
||||
out->bits_unused = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in)
|
||||
{
|
||||
if (in.size) {
|
||||
memcpy(out->buffer, in.buf, in.size);
|
||||
out->length = in.size;
|
||||
}
|
||||
}
|
||||
|
||||
void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in)
|
||||
{
|
||||
@@ -271,3 +292,54 @@ bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRIN
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief PDU Session Resource Setup Response Transfer encoding (9.3.4.2 3GPP TS 38.413) */
|
||||
byte_array_t encode_ngap_pdusession_setup_response_transfer(const pdusession_setup_t *pdusession)
|
||||
{
|
||||
NGAP_PDUSessionResourceSetupResponseTransfer_t pdusessionTransfer = {0};
|
||||
byte_array_t out = {0};
|
||||
|
||||
// DL QoS Flow per TNL Information (Mandatory)
|
||||
NGAP_QosFlowPerTNLInformation_t *dLQosFlowPerTNLInformation = &pdusessionTransfer.dLQosFlowPerTNLInformation;
|
||||
|
||||
// UP Transport Layer Information (Mandatory)
|
||||
dLQosFlowPerTNLInformation->uPTransportLayerInformation.present = NGAP_UPTransportLayerInformation_PR_gTPTunnel;
|
||||
asn1cCalloc(dLQosFlowPerTNLInformation->uPTransportLayerInformation.choice.gTPTunnel, gtp);
|
||||
GTP_TEID_TO_ASN1(pdusession->n3_outgoing.teid, >p->gTP_TEID);
|
||||
tnl_to_bitstring(>p->transportLayerAddress, pdusession->n3_outgoing.addr);
|
||||
char ip_str[INET_ADDRSTRLEN] = {0};
|
||||
inet_ntop(AF_INET, gtp->transportLayerAddress.buf, ip_str, sizeof(ip_str));
|
||||
NGAP_DEBUG("Encoded PDU Session Transfer (%d): TEID=0x%08x, Addr=%s\n",
|
||||
pdusession->pdusession_id,
|
||||
pdusession->n3_outgoing.teid,
|
||||
ip_str);
|
||||
|
||||
// Associated QoS Flow List (Mandatory)
|
||||
for (int j = 0; j < pdusession->nb_of_qos_flow; j++) {
|
||||
asn1cSequenceAdd(dLQosFlowPerTNLInformation->associatedQosFlowList.list, NGAP_AssociatedQosFlowItem_t, qos_item);
|
||||
// QoS Flow Identifier (Mandatory)
|
||||
qos_item->qosFlowIdentifier = pdusession->associated_qos_flows[j].qfi;
|
||||
}
|
||||
|
||||
// Encode
|
||||
asn_encode_to_new_buffer_result_t res = asn_encode_to_new_buffer(NULL,
|
||||
ATS_ALIGNED_CANONICAL_PER,
|
||||
&asn_DEF_NGAP_PDUSessionResourceSetupResponseTransfer,
|
||||
&pdusessionTransfer);
|
||||
AssertFatal(res.buffer, "ASN1 message encoding failed (%s, %lu)!\n", res.result.failed_type->name, res.result.encoded);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceSetupResponseTransfer, &pdusessionTransfer);
|
||||
out.buf = res.buffer;
|
||||
out.len = res.result.encoded;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool eq_ngap_plmn(const plmn_id_t *a, const plmn_id_t *b)
|
||||
{
|
||||
if (a == NULL || b == NULL) {
|
||||
return false;
|
||||
}
|
||||
_EQ_CHECK_INT(a->mcc, b->mcc);
|
||||
_EQ_CHECK_INT(a->mnc, b->mnc);
|
||||
_EQ_CHECK_INT(a->mnc_digit_length, b->mnc_digit_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -36,49 +36,19 @@
|
||||
#ifndef NGAP_COMMON_H_
|
||||
#define NGAP_COMMON_H_
|
||||
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include <stdbool.h>
|
||||
#include "oai_asn1.h"
|
||||
#include "ngap_utils.h"
|
||||
#include "ngap_msg_includes.h"
|
||||
#include "openair2/COMMON/ngap_messages_types.h"
|
||||
#include "ngap_messages_types.h"
|
||||
|
||||
/* Checking version of ASN1C compiler */
|
||||
#if (ASN1C_ENVIRONMENT_VERSION < ASN1C_MINIMUM_VERSION)
|
||||
# error "You are compiling ngap with the wrong version of ASN1C"
|
||||
#endif
|
||||
|
||||
# include "common/utils/LOG/log.h"
|
||||
# include "ngap_gNB_default_values.h"
|
||||
# define NGAP_ERROR(x, args...) LOG_E(NGAP, x, ##args)
|
||||
# define NGAP_WARN(x, args...) LOG_W(NGAP, x, ##args)
|
||||
# define NGAP_TRAF(x, args...) LOG_I(NGAP, x, ##args)
|
||||
# define NGAP_INFO(x, args...) LOG_I(NGAP, x, ##args)
|
||||
# define NGAP_DEBUG(x, args...) LOG_D(NGAP, x, ##args)
|
||||
|
||||
#define NGAP_FIND_PROTOCOLIE_BY_ID(IE_TYPE, ie, container, IE_ID, mandatory) \
|
||||
do { \
|
||||
IE_TYPE **ptr; \
|
||||
ie = NULL; \
|
||||
for (ptr = container->protocolIEs.list.array; ptr < &container->protocolIEs.list.array[container->protocolIEs.list.count]; ptr++) { \
|
||||
if ((*ptr)->id == IE_ID) { \
|
||||
ie = *ptr; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
if (ie == NULL) { \
|
||||
if (mandatory) { \
|
||||
AssertFatal(NGAP, "NGAP_FIND_PROTOCOLIE_BY_ID ie is NULL (searching for ie: %ld)\n", IE_ID); \
|
||||
} else { \
|
||||
NGAP_DEBUG("NGAP_FIND_PROTOCOLIE_BY_ID ie is NULL (searching for ie: %ld)\n", IE_ID); \
|
||||
} \
|
||||
} \
|
||||
} while (0); \
|
||||
if (mandatory && !ie) \
|
||||
return -1
|
||||
|
||||
/** \brief Function callback prototype.
|
||||
**/
|
||||
typedef int (*ngap_message_decoded_callback)(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu);
|
||||
|
||||
void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in);
|
||||
void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in);
|
||||
void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in);
|
||||
nr_guami_t decode_ngap_guami(const NGAP_GUAMI_t *in);
|
||||
ngap_cause_t decode_ngap_cause(const NGAP_Cause_t *in);
|
||||
@@ -91,7 +61,9 @@ void encode_ngap_nr_cgi(NGAP_NR_CGI_t *out, const plmn_id_t *plmn, const uint32_
|
||||
pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params);
|
||||
void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf);
|
||||
bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in);
|
||||
byte_array_t encode_ngap_pdusession_setup_response_transfer(const pdusession_setup_t *pdusession);
|
||||
|
||||
bool eq_ngap_plmn(const plmn_id_t *a, const plmn_id_t *b);
|
||||
|
||||
/** @}*/
|
||||
|
||||
#endif /* NGAP_COMMON_H_ */
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
#include "ngap_messages_types.h"
|
||||
#include "ngap_gNB_mobility_management.h"
|
||||
#include "ngap_gNB_ue_context.h"
|
||||
#include "ngap_gNB_pdu_session_management.h"
|
||||
#include "oai_asn1.h"
|
||||
#include "openair3/SECU/kdf.h"
|
||||
#include "queue.h"
|
||||
|
||||
@@ -47,7 +47,9 @@
|
||||
#include "ngap_gNB_nnsf.h"
|
||||
#include "ngap_gNB_management_procedures.h"
|
||||
#include "ngap_gNB_mobility_management.h"
|
||||
#include "ngap_gNB_pdu_session_management.h"
|
||||
#include "ngap_gNB_nas_procedures.h"
|
||||
#include "ngap_gNB_paging.h"
|
||||
#include "ngap_gNB_trace.h"
|
||||
#include "ngap_gNB_ue_context.h"
|
||||
#include "ngap_messages_types.h"
|
||||
@@ -1096,21 +1098,28 @@ static int ngap_gNB_handle_handover_cancel_ack(sctp_assoc_t assoc_id, uint32_t s
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle NGAP Paging message from AMF
|
||||
* @param assoc_id SCTP association ID
|
||||
* @param stream SCTP stream ID (must be 0 for paging per TS 38.412 Clause 7)
|
||||
* @param pdu NGAP PDU containing Paging message
|
||||
* @return 0 on success, -1 on failure */
|
||||
static int ngap_gNB_handle_paging(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
ngap_gNB_amf_data_t *amf_desc_p = NULL;
|
||||
ngap_gNB_instance_t *ngap_gNB_instance = NULL;
|
||||
NGAP_Paging_t *container;
|
||||
NGAP_PagingIEs_t *ie;
|
||||
DevAssert(pdu != NULL);
|
||||
container = &pdu->choice.initiatingMessage->value.choice.Paging;
|
||||
// received Paging Message from AMF
|
||||
NGAP_DEBUG("[SCTP %u] Received Paging Message From AMF\n",assoc_id);
|
||||
|
||||
/* Paging procedure -> stream != 0 */
|
||||
if (stream == 0) {
|
||||
LOG_W(NGAP,"[SCTP %d] Received Paging procedure on stream (%d)\n",
|
||||
assoc_id, stream);
|
||||
/* Per 3GPP TS 38.412 Clause 7 (Transport layer):
|
||||
* Paging is classified as "non-UE-associated signalling" because:
|
||||
* - UE is in RRC_IDLE or RRC_INACTIVE state
|
||||
* - No active UE context exists in the RAN
|
||||
* - Paging targets UE by 5G-S-TMSI, not by RAN-UE-NGAP-ID
|
||||
* Non-UE-associated signalling MUST use stream 0 (or a reserved stream pair) */
|
||||
if (stream != 0) {
|
||||
LOG_W(NGAP,"[SCTP %d] Received Paging procedure on stream != 0 (expected stream 0 per TS 38.412)\n", assoc_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1129,67 +1138,20 @@ static int ngap_gNB_handle_paging(sctp_assoc_t assoc_id, uint32_t stream, NGAP_N
|
||||
}
|
||||
|
||||
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PAGING_IND);
|
||||
ngap_paging_ind_t * msg=&NGAP_PAGING_IND(message_p);
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
ngap_paging_ind_t *msg = &NGAP_PAGING_IND(message_p);
|
||||
|
||||
/* convert NGAP_PagingIEs_t to ngap_paging_ind_t */
|
||||
/* id-UEIdentityIndexValue : convert UE Identity Index value */
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UEPagingIdentity, true);
|
||||
|
||||
struct NGAP_FiveG_S_TMSI *fiveG_S_TMSI = ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI;
|
||||
OCTET_STRING_TO_INT16(&fiveG_S_TMSI->aMFSetID, msg->ue_paging_identity.s_tmsi.amf_set_id);
|
||||
OCTET_STRING_TO_INT8(&fiveG_S_TMSI->aMFPointer, msg->ue_paging_identity.s_tmsi.amf_pointer);
|
||||
OCTET_STRING_TO_INT32(&fiveG_S_TMSI->fiveG_TMSI, msg->ue_paging_identity.s_tmsi.m_tmsi);
|
||||
|
||||
NGAP_DEBUG("[SCTP %u] Received Paging Identity amf_set_id %d, amf_pointer %d, m_tmsi %d\n",
|
||||
assoc_id,
|
||||
msg->ue_paging_identity.s_tmsi.amf_set_id,
|
||||
msg->ue_paging_identity.s_tmsi.amf_pointer,
|
||||
msg->ue_paging_identity.s_tmsi.m_tmsi);
|
||||
|
||||
msg->paging_drx = NGAP_PAGING_DRX_256;
|
||||
/* id-pagingDRX */
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PagingDRX, false);
|
||||
|
||||
/* optional */
|
||||
if (ie) {
|
||||
msg->paging_drx = ie->value.choice.PagingDRX;
|
||||
} else {
|
||||
msg->paging_drx = NGAP_PAGING_DRX_256;
|
||||
/* Decode NGAP Paging message */
|
||||
if (!decode_ng_paging(msg, pdu)) {
|
||||
NGAP_ERROR("[SCTP %u] Failed to decode NGAP Paging message\n", assoc_id);
|
||||
free_ng_paging(msg);
|
||||
itti_free(TASK_NGAP, message_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* id-TAIList */
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container,
|
||||
NGAP_ProtocolIE_ID_id_TAIListForPaging, true);
|
||||
/* send message to RRC */
|
||||
itti_send_msg_to_task(TASK_RRC_GNB, ngap_gNB_instance->instance, message_p);
|
||||
|
||||
NGAP_INFO("[SCTP %u] Received Paging taiList For Paging: count %d\n", assoc_id, ie->value.choice.TAIListForPaging.list.count);
|
||||
|
||||
for (int i = 0; i < ie->value.choice.TAIListForPaging.list.count; i++) {
|
||||
NGAP_TAIListForPagingItem_t *item_p;
|
||||
item_p = (NGAP_TAIListForPagingItem_t *)ie->value.choice.TAIListForPaging.list.array[i];
|
||||
TBCD_TO_MCC_MNC(&(item_p->tAI.pLMNIdentity), msg->plmn_identity[i].mcc, msg->plmn_identity[i].mnc, msg->plmn_identity[i].mnc_digit_length);
|
||||
OCTET_STRING_TO_INT16(&(item_p->tAI.tAC), msg->tac[i]);
|
||||
msg->tai_size++;
|
||||
plmn_id_t *p = &msg->plmn_identity[i];
|
||||
LOG_D(NGAP,
|
||||
"[SCTP %u] PLMN in TAI list for Paging: MCC=%03d, MNC=%0*d, TAC=%d\n",
|
||||
assoc_id,
|
||||
p->mcc,
|
||||
p->mnc_digit_length,
|
||||
p->mnc,
|
||||
msg->tac[i]);
|
||||
}
|
||||
|
||||
//paging parameter values
|
||||
NGAP_DEBUG("[SCTP %u] Received Paging parameters: Paging Identity amf_set_id %d amf_pointer %d m_tmsi %d paging_drx %d paging_priority %d\n",assoc_id,
|
||||
msg->ue_paging_identity.s_tmsi.amf_set_id,
|
||||
msg->ue_paging_identity.s_tmsi.amf_pointer,
|
||||
msg->ue_paging_identity.s_tmsi.m_tmsi,
|
||||
msg->paging_drx, msg->paging_priority);
|
||||
/* send message to RRC */
|
||||
itti_send_msg_to_task(TASK_RRC_GNB, ngap_gNB_instance->instance, message_p);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool decodePDUSessionResourceModify(pdusession_transfer_t *out, const OCTET_STRING_t in)
|
||||
@@ -1528,6 +1490,8 @@ static int ngap_gNB_handle_dl_ran_status_transfer(sctp_assoc_t assoc_id, uint32_
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*ngap_message_decoded_callback)(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu);
|
||||
|
||||
/* Handlers matrix. Only gNB related procedure present here */
|
||||
const ngap_message_decoded_callback ngap_messages_callback[][3] = {
|
||||
{0, 0, 0}, /* AMFConfigurationUpdate */
|
||||
|
||||
@@ -191,20 +191,3 @@ void ngap_gNB_remove_amf_desc(ngap_gNB_instance_t * instance)
|
||||
}
|
||||
}
|
||||
|
||||
void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in)
|
||||
{
|
||||
if (in.length) {
|
||||
out->buf = malloc_or_fail(in.length);
|
||||
memcpy(out->buf, in.buffer, in.length);
|
||||
out->size = in.length;
|
||||
out->bits_unused = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in)
|
||||
{
|
||||
if (in.size) {
|
||||
memcpy(out->buffer, in.buf, in.size);
|
||||
out->length = in.size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +48,4 @@ uint16_t ngap_gNB_fetch_add_global_cnx_id(void);
|
||||
|
||||
void ngap_gNB_prepare_internal_data(void);
|
||||
|
||||
void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in);
|
||||
|
||||
void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in);
|
||||
|
||||
#endif /* NGAP_GNB_MANAGEMENT_PROCEDURES_H_ */
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "ngap_gNB_management_procedures.h"
|
||||
#include "ngap_gNB_encoder.h"
|
||||
#include "ngap_gNB_itti_messaging.h"
|
||||
#include "NR_HandoverPreparationInformation.h"
|
||||
#include "conversions.h"
|
||||
|
||||
/** @brief UE Mobility Management: encode Handover Required
|
||||
|
||||
@@ -558,64 +558,6 @@ int ngap_gNB_nas_non_delivery_ind(instance_t instance,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief PDU Session Resource Setup Response Transfer encoding (9.3.4.2 3GPP TS 38.413) */
|
||||
static byte_array_t encode_ngap_pdusession_setup_response_transfer(const pdusession_setup_t *pdusession)
|
||||
{
|
||||
NGAP_PDUSessionResourceSetupResponseTransfer_t pdusessionTransfer = {0};
|
||||
byte_array_t out = {0};
|
||||
|
||||
// DL QoS Flow per TNL Information (Mandatory)
|
||||
NGAP_QosFlowPerTNLInformation_t *dLQosFlowPerTNLInformation = &pdusessionTransfer.dLQosFlowPerTNLInformation;
|
||||
|
||||
// UP Transport Layer Information (Mandatory)
|
||||
dLQosFlowPerTNLInformation->uPTransportLayerInformation.present = NGAP_UPTransportLayerInformation_PR_gTPTunnel;
|
||||
asn1cCalloc(dLQosFlowPerTNLInformation->uPTransportLayerInformation.choice.gTPTunnel, gtp);
|
||||
GTP_TEID_TO_ASN1(pdusession->n3_outgoing.teid, >p->gTP_TEID);
|
||||
tnl_to_bitstring(>p->transportLayerAddress, pdusession->n3_outgoing.addr);
|
||||
char ip_str[INET_ADDRSTRLEN] = {0};
|
||||
inet_ntop(AF_INET, gtp->transportLayerAddress.buf, ip_str, sizeof(ip_str));
|
||||
NGAP_DEBUG("Encoded PDU Session Transfer (%d): TEID=0x%08x, Addr=%s\n",
|
||||
pdusession->pdusession_id,
|
||||
pdusession->n3_outgoing.teid,
|
||||
ip_str);
|
||||
|
||||
// Associated QoS Flow List (Mandatory)
|
||||
for (int j = 0; j < pdusession->nb_of_qos_flow; j++) {
|
||||
asn1cSequenceAdd(dLQosFlowPerTNLInformation->associatedQosFlowList.list, NGAP_AssociatedQosFlowItem_t, qos_item);
|
||||
// QoS Flow Identifier (Mandatory)
|
||||
qos_item->qosFlowIdentifier = pdusession->associated_qos_flows[j].qfi;
|
||||
}
|
||||
|
||||
// Encode
|
||||
asn_encode_to_new_buffer_result_t res = asn_encode_to_new_buffer(NULL,
|
||||
ATS_ALIGNED_CANONICAL_PER,
|
||||
&asn_DEF_NGAP_PDUSessionResourceSetupResponseTransfer,
|
||||
&pdusessionTransfer);
|
||||
AssertFatal(res.buffer, "ASN1 message encoding failed (%s, %lu)!\n", res.result.failed_type->name, res.result.encoded);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceSetupResponseTransfer, &pdusessionTransfer);
|
||||
out.buf = res.buffer;
|
||||
out.len = res.result.encoded;
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @brief PDU Session Resource Release Response Transfer encoding (9.3.4.21 3GPP TS 38.413)
|
||||
* The transfer structure contains only an optional Secondary RAT Usage Information IE.
|
||||
* Since we don't use secondary RAT (MR-DC), we encode an empty structure. */
|
||||
static byte_array_t encode_ngap_pdusession_release_response_transfer(void)
|
||||
{
|
||||
NGAP_PDUSessionResourceReleaseResponseTransfer_t pdusessionTransfer = {0};
|
||||
|
||||
// Encode
|
||||
asn_encode_to_new_buffer_result_t res = asn_encode_to_new_buffer(NULL,
|
||||
ATS_ALIGNED_CANONICAL_PER,
|
||||
&asn_DEF_NGAP_PDUSessionResourceReleaseResponseTransfer,
|
||||
&pdusessionTransfer);
|
||||
AssertFatal(res.buffer, "ASN1 message encoding failed (%s, %lu)!\n", res.result.failed_type->name, res.result.encoded);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceReleaseResponseTransfer, &pdusessionTransfer);
|
||||
byte_array_t out = {.buf = res.buffer, .len = res.result.encoded};
|
||||
return out;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int ngap_gNB_initial_ctxt_resp(instance_t instance, ngap_initial_context_setup_resp_t *initial_ctxt_resp_p)
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -909,354 +851,3 @@ int ngap_gNB_ue_capabilities(instance_t instance, ngap_ue_cap_info_ind_t *ue_cap
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int ngap_gNB_pdusession_setup_resp(instance_t instance, ngap_pdusession_setup_resp_t *pdusession_setup_resp_p)
|
||||
//------------------------------------------------------------------------------
|
||||
{
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = NULL;
|
||||
struct ngap_gNB_ue_context_s *ue_context_p = NULL;
|
||||
NGAP_NGAP_PDU_t pdu = {0};
|
||||
uint8_t *buffer = NULL;
|
||||
uint32_t length;
|
||||
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(pdusession_setup_resp_p != NULL);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
if ((ue_context_p = ngap_get_ue_context(pdusession_setup_resp_p->gNB_ue_ngap_id)) == NULL) {
|
||||
/* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
|
||||
NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%08x\n", pdusession_setup_resp_p->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Uplink NAS transport can occur either during an ngap connected state
|
||||
* or during initial attach (for example: NAS authentication).
|
||||
*/
|
||||
if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED || ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
|
||||
NGAP_WARN(
|
||||
"You are attempting to send NAS data over non-connected "
|
||||
"gNB ue ngap id: %08x, current state: %d\n",
|
||||
pdusession_setup_resp_p->gNB_ue_ngap_id,
|
||||
ue_context_p->ue_state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
|
||||
asn1cCalloc(pdu.choice.successfulOutcome, successfulOutcome);
|
||||
successfulOutcome->procedureCode = NGAP_ProcedureCode_id_PDUSessionResourceSetup;
|
||||
successfulOutcome->criticality = NGAP_Criticality_reject;
|
||||
successfulOutcome->value.present = NGAP_SuccessfulOutcome__value_PR_PDUSessionResourceSetupResponse;
|
||||
NGAP_PDUSessionResourceSetupResponse_t *out = &successfulOutcome->value.choice.PDUSessionResourceSetupResponse;
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, ue_context_p->amf_ue_ngap_id);
|
||||
}
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie->value.choice.RAN_UE_NGAP_ID = pdusession_setup_resp_p->gNB_ue_ngap_id;
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (pdusession_setup_resp_p->nb_of_pdusessions > 0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie3);
|
||||
ie3->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListSURes;
|
||||
ie3->criticality = NGAP_Criticality_ignore;
|
||||
ie3->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_PDUSessionResourceSetupListSURes;
|
||||
|
||||
for (int i = 0; i < pdusession_setup_resp_p->nb_of_pdusessions; i++) {
|
||||
pdusession_setup_t *pdusession = pdusession_setup_resp_p->pdusessions + i;
|
||||
asn1cSequenceAdd(ie3->value.choice.PDUSessionResourceSetupListSURes.list, NGAP_PDUSessionResourceSetupItemSURes_t, item);
|
||||
/* pDUSessionID */
|
||||
item->pDUSessionID = pdusession->pdusession_id;
|
||||
|
||||
// PDU Session Resource Setup Response Transfer (Mandatory)
|
||||
byte_array_t ba = encode_ngap_pdusession_setup_response_transfer(pdusession);
|
||||
item->pDUSessionResourceSetupResponseTransfer.buf = ba.buf;
|
||||
item->pDUSessionResourceSetupResponseTransfer.size = ba.len;
|
||||
}
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (pdusession_setup_resp_p->nb_of_pdusessions_failed > 0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceFailedToSetupListSURes;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_PDUSessionResourceFailedToSetupListSURes;
|
||||
|
||||
for (int i = 0; i < pdusession_setup_resp_p->nb_of_pdusessions_failed; i++) {
|
||||
pdusession_failed_t *pdusession_failed = pdusession_setup_resp_p->pdusessions_failed + i;
|
||||
asn1cSequenceAdd(ie->value.choice.PDUSessionResourceFailedToSetupListSURes.list, NGAP_PDUSessionResourceFailedToSetupItemSURes_t, item);
|
||||
NGAP_PDUSessionResourceSetupUnsuccessfulTransfer_t pdusessionUnTransfer_p = {0};
|
||||
|
||||
/* pDUSessionID */
|
||||
item->pDUSessionID = pdusession_failed->pdusession_id;
|
||||
|
||||
/* cause */
|
||||
encode_ngap_cause(&pdusessionUnTransfer_p.cause, &pdusession_failed->cause);
|
||||
NGAP_INFO("pdusession setup response: failed pdusession ID %ld\n", item->pDUSessionID);
|
||||
|
||||
asn_encode_to_new_buffer_result_t res = asn_encode_to_new_buffer(NULL, ATS_ALIGNED_CANONICAL_PER, &asn_DEF_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer, &pdusessionUnTransfer_p);
|
||||
item->pDUSessionResourceSetupUnsuccessfulTransfer.buf = res.buffer;
|
||||
item->pDUSessionResourceSetupUnsuccessfulTransfer.size = res.result.encoded;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer, &pdusessionUnTransfer_p);
|
||||
}
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_CriticalityDiagnostics;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_CriticalityDiagnostics;
|
||||
// ie->value.choice.CriticalityDiagnostics = ;
|
||||
}
|
||||
|
||||
if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
|
||||
NGAP_ERROR("Failed to encode uplink transport\n");
|
||||
/* Encode procedure has failed... */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, ue_context_p->amf_ref->assoc_id, buffer, length, ue_context_p->tx_stream);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int ngap_gNB_pdusession_modify_resp(instance_t instance, ngap_pdusession_modify_resp_t *pdusession_modify_resp_p)
|
||||
//------------------------------------------------------------------------------
|
||||
{
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = NULL;
|
||||
struct ngap_gNB_ue_context_s *ue_context_p = NULL;
|
||||
NGAP_NGAP_PDU_t pdu;
|
||||
uint8_t *buffer = NULL;
|
||||
uint32_t length;
|
||||
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(pdusession_modify_resp_p != NULL);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
if ((ue_context_p = ngap_get_ue_context(pdusession_modify_resp_p->gNB_ue_ngap_id)) == NULL) {
|
||||
/* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
|
||||
NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%08x\n", pdusession_modify_resp_p->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Uplink NAS transport can occur either during an ngap connected state
|
||||
* or during initial attach (for example: NAS authentication).
|
||||
*/
|
||||
if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED || ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
|
||||
NGAP_WARN(
|
||||
"You are attempting to send NAS data over non-connected "
|
||||
"gNB ue ngap id: %08x, current state: %d\n",
|
||||
pdusession_modify_resp_p->gNB_ue_ngap_id,
|
||||
ue_context_p->ue_state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
memset(&pdu, 0, sizeof(pdu));
|
||||
pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
|
||||
asn1cCalloc(pdu.choice.successfulOutcome, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_PDUSessionResourceModify;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_SuccessfulOutcome__value_PR_PDUSessionResourceModifyResponse;
|
||||
NGAP_PDUSessionResourceModifyResponse_t *out = &head->value.choice.PDUSessionResourceModifyResponse;
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie = (NGAP_PDUSessionResourceModifyResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSessionResourceModifyResponseIEs_t));
|
||||
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, pdusession_modify_resp_p->amf_ue_ngap_id);
|
||||
asn1cSeqAdd(&out->protocolIEs.list, ie);
|
||||
}
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie->value.choice.RAN_UE_NGAP_ID = pdusession_modify_resp_p->gNB_ue_ngap_id;
|
||||
}
|
||||
|
||||
/* PDUSessionResourceModifyListModRes optional */
|
||||
if (pdusession_modify_resp_p->nb_of_pdusessions > 0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceModifyListModRes;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_PDUSessionResourceModifyListModRes;
|
||||
|
||||
for (int i = 0; i < pdusession_modify_resp_p->nb_of_pdusessions; i++) {
|
||||
asn1cSequenceAdd(ie->value.choice.PDUSessionResourceModifyListModRes.list, NGAP_PDUSessionResourceModifyItemModRes_t, item);
|
||||
item->pDUSessionID = pdusession_modify_resp_p->pdusessions[i].pdusession_id;
|
||||
|
||||
NGAP_PDUSessionResourceModifyResponseTransfer_t transfer = {0};
|
||||
asn1cCalloc(transfer.qosFlowAddOrModifyResponseList, tmp);
|
||||
|
||||
for (int qos_flow_index = 0; qos_flow_index < pdusession_modify_resp_p->pdusessions[i].nb_of_qos_flow; qos_flow_index++) {
|
||||
asn1cSequenceAdd(tmp->list, NGAP_QosFlowAddOrModifyResponseItem_t, qos);
|
||||
qos->qosFlowIdentifier = pdusession_modify_resp_p->pdusessions[i].qos[qos_flow_index].qfi;
|
||||
}
|
||||
asn_encode_to_new_buffer_result_t res = {0};
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_ALIGNED_CANONICAL_PER, &asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, &transfer);
|
||||
item->pDUSessionResourceModifyResponseTransfer.buf = res.buffer;
|
||||
item->pDUSessionResourceModifyResponseTransfer.size = res.result.encoded;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, &transfer);
|
||||
|
||||
NGAP_DEBUG("pdusession_modify_resp: modified pdusession ID %ld\n", item->pDUSessionID);
|
||||
}
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (pdusession_modify_resp_p->nb_of_pdusessions_failed > 0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceFailedToModifyListModRes;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_PDUSessionResourceFailedToModifyListModRes;
|
||||
|
||||
for (int i = 0; i < pdusession_modify_resp_p->nb_of_pdusessions_failed; i++) {
|
||||
asn1cSequenceAdd(ie->value.choice.PDUSessionResourceFailedToModifyListModRes.list, NGAP_PDUSessionResourceFailedToModifyItemModRes_t, item);
|
||||
item->pDUSessionID = pdusession_modify_resp_p->pdusessions_failed[i].pdusession_id;
|
||||
|
||||
NGAP_PDUSessionResourceModifyUnsuccessfulTransfer_t pdusessionTransfer = {0};
|
||||
|
||||
// NGAP cause
|
||||
encode_ngap_cause(&pdusessionTransfer.cause, &pdusession_modify_resp_p->pdusessions_failed[i].cause);
|
||||
|
||||
asn_encode_to_new_buffer_result_t res = {0};
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_ALIGNED_CANONICAL_PER, &asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer, &pdusessionTransfer);
|
||||
item->pDUSessionResourceModifyUnsuccessfulTransfer.buf = res.buffer;
|
||||
item->pDUSessionResourceModifyUnsuccessfulTransfer.size = res.result.encoded;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer, &pdusessionTransfer);
|
||||
|
||||
NGAP_INFO("pdusession_modify_resp: failed pdusession ID %ld\n", item->pDUSessionID);
|
||||
}
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_CriticalityDiagnostics;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_CriticalityDiagnostics;
|
||||
// ie->value.choice.CriticalityDiagnostics = ;
|
||||
}
|
||||
|
||||
if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
|
||||
NGAP_ERROR("Failed to encode uplink transport\n");
|
||||
/* Encode procedure has failed... */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, ue_context_p->amf_ref->assoc_id, buffer, length, ue_context_p->tx_stream);
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
int ngap_gNB_pdusession_release_resp(instance_t instance, ngap_pdusession_release_resp_t *pdusession_release_resp_p)
|
||||
//------------------------------------------------------------------------------
|
||||
{
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = NULL;
|
||||
struct ngap_gNB_ue_context_s *ue_context_p = NULL;
|
||||
NGAP_NGAP_PDU_t pdu;
|
||||
uint8_t *buffer = NULL;
|
||||
uint32_t length;
|
||||
int i;
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(pdusession_release_resp_p != NULL);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
if ((ue_context_p = ngap_get_ue_context(pdusession_release_resp_p->gNB_ue_ngap_id)) == NULL) {
|
||||
/* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
|
||||
NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%08x\n", pdusession_release_resp_p->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
memset(&pdu, 0, sizeof(pdu));
|
||||
pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
|
||||
asn1cCalloc(pdu.choice.successfulOutcome, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_PDUSessionResourceRelease;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_SuccessfulOutcome__value_PR_PDUSessionResourceReleaseResponse;
|
||||
NGAP_PDUSessionResourceReleaseResponse_t *out = &head->value.choice.PDUSessionResourceReleaseResponse;
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceReleaseResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceReleaseResponseIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, ue_context_p->amf_ue_ngap_id);
|
||||
}
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceReleaseResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceReleaseResponseIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie->value.choice.RAN_UE_NGAP_ID = pdusession_release_resp_p->gNB_ue_ngap_id;
|
||||
}
|
||||
|
||||
/* PDU Session Resource Released List (mandatory) */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceReleaseResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceReleasedListRelRes;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceReleaseResponseIEs__value_PR_PDUSessionResourceReleasedListRelRes;
|
||||
|
||||
for (i = 0; i < pdusession_release_resp_p->nb_of_pdusessions_released; i++) {
|
||||
NGAP_PDUSessionResourceReleasedListRelRes_t *list = &ie->value.choice.PDUSessionResourceReleasedListRelRes;
|
||||
asn1cSequenceAdd(list->list, NGAP_PDUSessionResourceReleasedItemRelRes_t, item);
|
||||
pdusession_release_t *r = &pdusession_release_resp_p->pdusession_release[i];
|
||||
/* PDU Session ID (mandatory) */
|
||||
item->pDUSessionID = r->pdusession_id;
|
||||
/* PDU Session Resource Release Response Transfer (mandatory) */
|
||||
// Empty transfer is valid since Secondary RAT Usage Information is optional and not used
|
||||
byte_array_t transfer = encode_ngap_pdusession_release_response_transfer();
|
||||
OCTET_STRING_fromBuf(&item->pDUSessionResourceReleaseResponseTransfer, (const char *)transfer.buf, transfer.len);
|
||||
free_byte_array(transfer);
|
||||
NGAP_DEBUG("PDU Session Resource Release Response: pdusession ID %ld\n", item->pDUSessionID);
|
||||
}
|
||||
}
|
||||
|
||||
if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
|
||||
NGAP_ERROR("Failed to encode release response\n");
|
||||
/* Encode procedure has failed... */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, ue_context_p->amf_ref->assoc_id, buffer, length, ue_context_p->tx_stream);
|
||||
NGAP_INFO("pdusession_release_response sended gNB_UE_NGAP_ID %u amf_ue_ngap_id %lu nb_of_pdusessions_released %d \n",
|
||||
pdusession_release_resp_p->gNB_ue_ngap_id,
|
||||
(uint64_t)ue_context_p->amf_ue_ngap_id,
|
||||
pdusession_release_resp_p->nb_of_pdusessions_released);
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,13 +54,4 @@ int ngap_gNB_initial_ctxt_fail(instance_t instance, ngap_initial_context_setup_f
|
||||
int ngap_gNB_ue_capabilities(instance_t instance,
|
||||
ngap_ue_cap_info_ind_t *ue_cap_info_ind_p);
|
||||
|
||||
int ngap_gNB_pdusession_setup_resp(instance_t instance,
|
||||
ngap_pdusession_setup_resp_t *pdusession_setup_resp_p);
|
||||
|
||||
int ngap_gNB_pdusession_modify_resp(instance_t instance,
|
||||
ngap_pdusession_modify_resp_t *pdusession_modify_resp_p);
|
||||
|
||||
int ngap_gNB_pdusession_release_resp(instance_t instance,
|
||||
ngap_pdusession_release_resp_t *pdusession_release_resp_p);
|
||||
|
||||
#endif /* NGAP_GNB_NAS_PROCEDURES_H_ */
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "assertions.h"
|
||||
#include "common/utils/utils.h"
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
#include "conversions.h"
|
||||
#include "ngap_common.h"
|
||||
#include "ngap_gNB_paging.h"
|
||||
#include "ngap_msg_includes.h"
|
||||
#include "oai_asn1.h"
|
||||
|
||||
/* Paging Attempt range (TS 38.413 §9.3.1.72) */
|
||||
#define NGAP_PAGING_ATTEMPT_MIN 1
|
||||
#define NGAP_PAGING_ATTEMPT_MAX 16
|
||||
|
||||
/** @brief Return text label at index in text_info array */
|
||||
static const char *text_info_at(int index, const text_info_t *info_array, size_t array_size)
|
||||
{
|
||||
DevAssert(index >= 0 && index < array_size);
|
||||
return info_array[index].text;
|
||||
}
|
||||
|
||||
/** @brief Encode NGAP Paging message (9.2.4.1 of 3GPP TS 38.413)
|
||||
* @param msg Pointer to paging indication structure to encode
|
||||
* @return Pointer to allocated NGAP_NGAP_PDU_t */
|
||||
NGAP_NGAP_PDU_t *encode_ng_paging(const ngap_paging_ind_t *msg)
|
||||
{
|
||||
DevAssert(msg != NULL);
|
||||
|
||||
NGAP_NGAP_PDU_t *pdu = calloc_or_fail(1, sizeof(NGAP_NGAP_PDU_t));
|
||||
|
||||
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
|
||||
asn1cCalloc(pdu->choice.initiatingMessage, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_Paging;
|
||||
head->criticality = NGAP_Criticality_ignore;
|
||||
head->value.present = NGAP_InitiatingMessage__value_PR_Paging;
|
||||
NGAP_Paging_t *container = &head->value.choice.Paging;
|
||||
|
||||
// UE Paging Identity (M)
|
||||
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_UEPagingIdentity;
|
||||
ie->criticality = NGAP_Criticality_reject;
|
||||
ie->value.present = NGAP_PagingIEs__value_PR_UEPagingIdentity;
|
||||
ie->value.choice.UEPagingIdentity.present = NGAP_UEPagingIdentity_PR_fiveG_S_TMSI;
|
||||
asn1cCalloc(ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI, fiveg_s_tmsi);
|
||||
const fiveg_s_tmsi_t *s_tmsi = &msg->ue_paging_identity.s_tmsi;
|
||||
AMF_SETID_TO_BIT_STRING(s_tmsi->amf_set_id, &fiveg_s_tmsi->aMFSetID);
|
||||
AMF_POINTER_TO_BIT_STRING(s_tmsi->amf_pointer, &fiveg_s_tmsi->aMFPointer);
|
||||
M_TMSI_TO_OCTET_STRING(s_tmsi->m_tmsi, &fiveg_s_tmsi->fiveG_TMSI);
|
||||
|
||||
// TAI List for Paging (M)
|
||||
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie1);
|
||||
ie1->id = NGAP_ProtocolIE_ID_id_TAIListForPaging;
|
||||
ie1->criticality = NGAP_Criticality_reject;
|
||||
ie1->value.present = NGAP_PagingIEs__value_PR_TAIListForPaging;
|
||||
|
||||
for (int i = 0; i < msg->n_tai; i++) {
|
||||
asn1cSequenceAdd(ie1->value.choice.TAIListForPaging.list, NGAP_TAIListForPagingItem_t, item);
|
||||
NGAP_TAI_t *tai = &item->tAI;
|
||||
const plmn_id_t *plmn = &msg->tai_list[i].plmn;
|
||||
MCC_MNC_TO_TBCD(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &tai->pLMNIdentity);
|
||||
INT24_TO_OCTET_STRING(msg->tai_list[i].tac, &tai->tAC);
|
||||
}
|
||||
|
||||
// Paging DRX (O)
|
||||
if (msg->paging_drx != NULL) {
|
||||
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie2);
|
||||
ie2->id = NGAP_ProtocolIE_ID_id_PagingDRX;
|
||||
ie2->criticality = NGAP_Criticality_ignore;
|
||||
ie2->value.present = NGAP_PagingIEs__value_PR_PagingDRX;
|
||||
ie2->value.choice.PagingDRX = *msg->paging_drx;
|
||||
}
|
||||
|
||||
// Paging Priority (O)
|
||||
if (msg->paging_priority != NULL) {
|
||||
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie3);
|
||||
ie3->id = NGAP_ProtocolIE_ID_id_PagingPriority;
|
||||
ie3->criticality = NGAP_Criticality_ignore;
|
||||
ie3->value.present = NGAP_PagingIEs__value_PR_PagingPriority;
|
||||
ie3->value.choice.PagingPriority = *msg->paging_priority;
|
||||
}
|
||||
|
||||
// UERadioCapabilityForPaging (O)
|
||||
if (msg->ue_radio_capability != NULL && msg->ue_radio_capability->buf != NULL) {
|
||||
const byte_array_t *ue_radio_cap_ba = msg->ue_radio_capability;
|
||||
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie4);
|
||||
ie4->id = NGAP_ProtocolIE_ID_id_UERadioCapabilityForPaging;
|
||||
ie4->criticality = NGAP_Criticality_ignore;
|
||||
ie4->value.present = NGAP_PagingIEs__value_PR_UERadioCapabilityForPaging;
|
||||
NGAP_UERadioCapabilityForPaging_t *ue_radio_cap = &ie4->value.choice.UERadioCapabilityForPaging;
|
||||
// UERadioCapabilityForPaging is a SEQUENCE with optional OCTET_STRING fields (Encode as NR)
|
||||
asn1cCalloc(ue_radio_cap->uERadioCapabilityForPagingOfNR, nr_field);
|
||||
OCTET_STRING_fromBuf(nr_field, (const char *)ue_radio_cap_ba->buf, ue_radio_cap_ba->len);
|
||||
}
|
||||
|
||||
// PagingOrigin (O)
|
||||
if (msg->origin != NULL) {
|
||||
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie5);
|
||||
ie5->id = NGAP_ProtocolIE_ID_id_PagingOrigin;
|
||||
ie5->criticality = NGAP_Criticality_ignore;
|
||||
ie5->value.present = NGAP_PagingIEs__value_PR_PagingOrigin;
|
||||
ie5->value.choice.PagingOrigin = *msg->origin;
|
||||
}
|
||||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
/** @brief Free memory allocated for NGAP paging indication
|
||||
* @param msg Pointer to paging indication structure to free */
|
||||
void free_ng_paging(ngap_paging_ind_t *msg)
|
||||
{
|
||||
DevAssert(msg != NULL);
|
||||
free(msg->paging_drx);
|
||||
free(msg->paging_priority);
|
||||
if (msg->ue_radio_capability != NULL) {
|
||||
free_byte_array(*msg->ue_radio_capability);
|
||||
free(msg->ue_radio_capability);
|
||||
}
|
||||
free(msg->origin);
|
||||
free(msg->paging_attempt_info);
|
||||
}
|
||||
|
||||
/** @brief Decode NGAP Paging message (9.2.4.1 of 3GPP TS 38.413)
|
||||
* @param out Pointer to output structure to fill
|
||||
* @param pdu Pointer to NGAP PDU containing the Paging message
|
||||
* @return true on success, false on failure */
|
||||
bool decode_ng_paging(ngap_paging_ind_t *out, const NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
DevAssert(pdu != NULL);
|
||||
DevAssert(out != NULL);
|
||||
|
||||
if (pdu->present != NGAP_NGAP_PDU_PR_initiatingMessage) {
|
||||
NGAP_ERROR("Invalid PDU present value for Paging message\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const NGAP_Paging_t *container = &pdu->choice.initiatingMessage->value.choice.Paging;
|
||||
NGAP_PagingIEs_t *ie;
|
||||
fiveg_s_tmsi_t *s_tmsi = &out->ue_paging_identity.s_tmsi;
|
||||
|
||||
// Reset output structure
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
// UE Paging Identity (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UEPagingIdentity, true);
|
||||
|
||||
if (ie->value.choice.UEPagingIdentity.present == NGAP_UEPagingIdentity_PR_fiveG_S_TMSI) {
|
||||
const struct NGAP_FiveG_S_TMSI *fiveG_S_TMSI = ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI;
|
||||
s_tmsi->amf_set_id = BIT_STRING_to_uint16(&fiveG_S_TMSI->aMFSetID);
|
||||
s_tmsi->amf_pointer = BIT_STRING_to_uint8(&fiveG_S_TMSI->aMFPointer);
|
||||
OCTET_STRING_TO_INT32(&fiveG_S_TMSI->fiveG_TMSI, s_tmsi->m_tmsi);
|
||||
} else {
|
||||
NGAP_ERROR("Unsupported UE Paging Identity type\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TAI List for Paging (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_TAIListForPaging, true);
|
||||
|
||||
// Validate TAI list count
|
||||
int tai_count = ie->value.choice.TAIListForPaging.list.count;
|
||||
if (tai_count > NGAP_MAX_NO_TAI_PAGING) {
|
||||
NGAP_ERROR("Invalid TAI list count: %d (must be <= %d)\n", tai_count, NGAP_MAX_NO_TAI_PAGING);
|
||||
return false;
|
||||
}
|
||||
|
||||
out->n_tai = 0;
|
||||
for (int i = 0; i < tai_count; i++) {
|
||||
const NGAP_TAIListForPagingItem_t *item_p = ie->value.choice.TAIListForPaging.list.array[i];
|
||||
DevAssert(item_p != NULL);
|
||||
const NGAP_TAI_t *tai = &item_p->tAI;
|
||||
nr_tai_t *tai_out = &out->tai_list[i];
|
||||
TBCD_TO_MCC_MNC(&tai->pLMNIdentity, tai_out->plmn.mcc, tai_out->plmn.mnc, tai_out->plmn.mnc_digit_length);
|
||||
OCTET_STRING_TO_INT24(&tai->tAC, tai_out->tac);
|
||||
NGAP_DEBUG("TAI[%d] for paging: MCC=%03d, MNC=%0*d, TAC=%d\n",
|
||||
i,
|
||||
tai_out->plmn.mcc,
|
||||
tai_out->plmn.mnc_digit_length,
|
||||
tai_out->plmn.mnc,
|
||||
tai_out->tac);
|
||||
out->n_tai++;
|
||||
}
|
||||
|
||||
// Paging DRX (O)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PagingDRX, false);
|
||||
if (ie != NULL) {
|
||||
out->paging_drx = malloc_or_fail(sizeof(ngap_paging_drx_t));
|
||||
*out->paging_drx = ie->value.choice.PagingDRX;
|
||||
const char *s = text_info_at(*out->paging_drx, paging_drx_text, sizeofArray(paging_drx_text));
|
||||
NGAP_DEBUG("Decoded Paging DRX: %s", s);
|
||||
}
|
||||
|
||||
// Paging Priority (O)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PagingPriority, false);
|
||||
if (ie != NULL) {
|
||||
out->paging_priority = malloc_or_fail(sizeof(ngap_paging_priority_t));
|
||||
*out->paging_priority = ie->value.choice.PagingPriority;
|
||||
const char *s = text_info_at(*out->paging_priority, paging_prio_text, sizeofArray(paging_prio_text));
|
||||
NGAP_DEBUG("Decoded Paging Priority: %s", s);
|
||||
}
|
||||
|
||||
// UERadioCapabilityForPaging (O)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UERadioCapabilityForPaging, false);
|
||||
if (ie != NULL) {
|
||||
const NGAP_UERadioCapabilityForPaging_t *ue_radio_cap = &ie->value.choice.UERadioCapabilityForPaging;
|
||||
const OCTET_STRING_t *nr_field = ue_radio_cap->uERadioCapabilityForPagingOfNR;
|
||||
const OCTET_STRING_t *eutra_field = ue_radio_cap->uERadioCapabilityForPagingOfEUTRA;
|
||||
if (nr_field != NULL) {
|
||||
out->ue_radio_capability = malloc_or_fail(sizeof(byte_array_t));
|
||||
*out->ue_radio_capability = create_byte_array(nr_field->size, nr_field->buf);
|
||||
NGAP_DEBUG("Decoded UERadioCapabilityForPaging (NR): %zu bytes\n", nr_field->size);
|
||||
} else if (eutra_field != NULL) {
|
||||
out->ue_radio_capability = malloc_or_fail(sizeof(byte_array_t));
|
||||
*out->ue_radio_capability = create_byte_array(eutra_field->size, eutra_field->buf);
|
||||
NGAP_DEBUG("Decoded UERadioCapabilityForPaging (EUTRA): %zu bytes\n", eutra_field->size);
|
||||
} else {
|
||||
NGAP_WARN("UERadioCapabilityForPaging present but both NR and EUTRA fields are NULL\n");
|
||||
}
|
||||
}
|
||||
|
||||
// PagingOrigin (O)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PagingOrigin, false);
|
||||
if (ie != NULL) {
|
||||
// PagingOrigin shall be transferred to UE according to TS 38.331 and TS 36.331
|
||||
out->origin = calloc_or_fail(1, sizeof(*out->origin));
|
||||
*out->origin = ie->value.choice.PagingOrigin;
|
||||
}
|
||||
|
||||
// AssistanceDataForPaging (O) - contains PagingAttemptInformation
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AssistanceDataForPaging, false);
|
||||
if (ie != NULL) {
|
||||
const NGAP_AssistanceDataForPaging_t *assistance_data = &ie->value.choice.AssistanceDataForPaging;
|
||||
|
||||
// Extract PagingAttemptInformation if present (TS 38.413 §9.3.1.72)
|
||||
if (assistance_data->pagingAttemptInformation != NULL) {
|
||||
const NGAP_PagingAttemptInformation_t *paging_attempt_info = assistance_data->pagingAttemptInformation;
|
||||
out->paging_attempt_info = calloc_or_fail(1, sizeof(ngap_paging_attempt_info_t));
|
||||
|
||||
// Paging Attempt Count (mandatory)
|
||||
uint8_t attempt_count = paging_attempt_info->pagingAttemptCount;
|
||||
DevAssert(attempt_count >= NGAP_PAGING_ATTEMPT_MIN && attempt_count <= NGAP_PAGING_ATTEMPT_MAX);
|
||||
out->paging_attempt_info->paging_attempt_count = attempt_count;
|
||||
NGAP_DEBUG("Decoded Paging Attempt Count: %u\n", out->paging_attempt_info->paging_attempt_count);
|
||||
|
||||
// Intended Number of Paging Attempts (mandatory)
|
||||
uint8_t intended_attempts = paging_attempt_info->intendedNumberOfPagingAttempts;
|
||||
DevAssert(intended_attempts >= NGAP_PAGING_ATTEMPT_MIN && intended_attempts <= NGAP_PAGING_ATTEMPT_MAX);
|
||||
out->paging_attempt_info->intended_paging_attempts = intended_attempts;
|
||||
NGAP_DEBUG("Decoded Intended Number of Paging Attempts: %u\n", out->paging_attempt_info->intended_paging_attempts);
|
||||
}
|
||||
}
|
||||
|
||||
NGAP_DEBUG("Decoded Paging: m_tmsi=0x%08x, TAI=%d", s_tmsi->m_tmsi, out->n_tai);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief Check equality of two NGAP paging indication structures
|
||||
* @param a First structure
|
||||
* @param b Second structure
|
||||
* @return true if equal, false otherwise */
|
||||
bool eq_ng_paging(const ngap_paging_ind_t *a, const ngap_paging_ind_t *b)
|
||||
{
|
||||
if (a == NULL || b == NULL) {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
// UE Paging Identity
|
||||
const fiveg_s_tmsi_t *a_tmsi = &a->ue_paging_identity.s_tmsi;
|
||||
const fiveg_s_tmsi_t *b_tmsi = &b->ue_paging_identity.s_tmsi;
|
||||
_EQ_CHECK_INT(a_tmsi->amf_set_id, b_tmsi->amf_set_id);
|
||||
_EQ_CHECK_INT(a_tmsi->amf_pointer, b_tmsi->amf_pointer);
|
||||
_EQ_CHECK_UINT32(a_tmsi->m_tmsi, b_tmsi->m_tmsi);
|
||||
|
||||
// TAI List
|
||||
_EQ_CHECK_INT(a->n_tai, b->n_tai);
|
||||
for (int i = 0; i < a->n_tai; i++) {
|
||||
if (!eq_ngap_plmn(&a->tai_list[i].plmn, &b->tai_list[i].plmn)) {
|
||||
return false;
|
||||
}
|
||||
_EQ_CHECK_INT(a->tai_list[i].tac, b->tai_list[i].tac);
|
||||
}
|
||||
|
||||
// Paging DRX (optional)
|
||||
_EQ_CHECK_OPTIONAL_IE(a, b, paging_drx, _EQ_CHECK_INT);
|
||||
|
||||
// Paging Priority (optional)
|
||||
_EQ_CHECK_OPTIONAL_IE(a, b, paging_priority, _EQ_CHECK_INT);
|
||||
|
||||
// UERadioCapabilityForPaging (optional)
|
||||
_EQ_CHECK_OPTIONAL_PTR(a, b, ue_radio_capability);
|
||||
if (a->ue_radio_capability != NULL && b->ue_radio_capability != NULL) {
|
||||
if (!eq_byte_array(a->ue_radio_capability, b->ue_radio_capability)) {
|
||||
NGAP_ERROR("Equality condition 'eq_byte_array' failed: byte array contents differ\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// PagingOrigin (optional)
|
||||
_EQ_CHECK_OPTIONAL_IE(a, b, origin, _EQ_CHECK_INT);
|
||||
|
||||
// Assistance Data for Paging (optional)
|
||||
_EQ_CHECK_OPTIONAL_PTR(a, b, paging_attempt_info);
|
||||
if (a->paging_attempt_info != NULL && b->paging_attempt_info != NULL) {
|
||||
_EQ_CHECK_INT(a->paging_attempt_info->paging_attempt_count, b->paging_attempt_info->paging_attempt_count);
|
||||
_EQ_CHECK_INT(a->paging_attempt_info->intended_paging_attempts, b->paging_attempt_info->intended_paging_attempts);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef NGAP_GNB_PAGING_H_
|
||||
#define NGAP_GNB_PAGING_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "ngap_messages_types.h"
|
||||
|
||||
/** @brief Encode NGAP Paging message (9.2.4.1 of 3GPP TS 38.413)
|
||||
* @param msg Pointer to paging indication structure to encode
|
||||
* @return Pointer to allocated NGAP_NGAP_PDU_t, or NULL on failure */
|
||||
NGAP_NGAP_PDU_t *encode_ng_paging(const ngap_paging_ind_t *msg);
|
||||
|
||||
/** @brief Decode NGAP Paging message (9.2.4.1 of 3GPP TS 38.413)
|
||||
* @param out Pointer to output structure to fill
|
||||
* @param pdu Pointer to NGAP PDU containing the Paging message
|
||||
* @return true on success, false on failure */
|
||||
bool decode_ng_paging(ngap_paging_ind_t *out, const NGAP_NGAP_PDU_t *pdu);
|
||||
|
||||
/** @brief Free memory allocated for NGAP paging indication
|
||||
* @param msg Pointer to paging indication structure to free */
|
||||
void free_ng_paging(ngap_paging_ind_t *msg);
|
||||
|
||||
/** @brief Check equality of two NGAP paging indication structures
|
||||
* @param a First structure
|
||||
* @param b Second structure
|
||||
* @return true if equal, false otherwise */
|
||||
bool eq_ng_paging(const ngap_paging_ind_t *a, const ngap_paging_ind_t *b);
|
||||
|
||||
#endif /* NGAP_GNB_PAGING_H_ */
|
||||
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "ngap_gNB_pdu_session_management.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "INTEGER.h"
|
||||
#include "ngap_msg_includes.h"
|
||||
#include "OCTET_STRING.h"
|
||||
#include "asn_application.h"
|
||||
#include "assertions.h"
|
||||
#include "conversions.h"
|
||||
#include "ngap_common.h"
|
||||
#include "ngap_gNB_encoder.h"
|
||||
#include "ngap_gNB_itti_messaging.h"
|
||||
#include "ngap_gNB_ue_context.h"
|
||||
#include "ngap_gNB_management_procedures.h"
|
||||
#include "oai_asn1.h"
|
||||
#include "ds/byte_array.h"
|
||||
|
||||
int ngap_gNB_pdusession_setup_resp(instance_t instance, ngap_pdusession_setup_resp_t *pdusession_setup_resp_p)
|
||||
{
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = NULL;
|
||||
struct ngap_gNB_ue_context_s *ue_context_p = NULL;
|
||||
NGAP_NGAP_PDU_t pdu = {0};
|
||||
uint8_t *buffer = NULL;
|
||||
uint32_t length;
|
||||
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(pdusession_setup_resp_p != NULL);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
if ((ue_context_p = ngap_get_ue_context(pdusession_setup_resp_p->gNB_ue_ngap_id)) == NULL) {
|
||||
/* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
|
||||
NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%08x\n", pdusession_setup_resp_p->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Uplink NAS transport can occur either during an ngap connected state
|
||||
* or during initial attach (for example: NAS authentication).
|
||||
*/
|
||||
if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED || ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
|
||||
NGAP_WARN(
|
||||
"You are attempting to send NAS data over non-connected "
|
||||
"gNB ue ngap id: %08x, current state: %d\n",
|
||||
pdusession_setup_resp_p->gNB_ue_ngap_id,
|
||||
ue_context_p->ue_state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
|
||||
asn1cCalloc(pdu.choice.successfulOutcome, successfulOutcome);
|
||||
successfulOutcome->procedureCode = NGAP_ProcedureCode_id_PDUSessionResourceSetup;
|
||||
successfulOutcome->criticality = NGAP_Criticality_reject;
|
||||
successfulOutcome->value.present = NGAP_SuccessfulOutcome__value_PR_PDUSessionResourceSetupResponse;
|
||||
NGAP_PDUSessionResourceSetupResponse_t *out = &successfulOutcome->value.choice.PDUSessionResourceSetupResponse;
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, ue_context_p->amf_ue_ngap_id);
|
||||
}
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie->value.choice.RAN_UE_NGAP_ID = pdusession_setup_resp_p->gNB_ue_ngap_id;
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (pdusession_setup_resp_p->nb_of_pdusessions > 0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie3);
|
||||
ie3->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListSURes;
|
||||
ie3->criticality = NGAP_Criticality_ignore;
|
||||
ie3->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_PDUSessionResourceSetupListSURes;
|
||||
|
||||
for (int i = 0; i < pdusession_setup_resp_p->nb_of_pdusessions; i++) {
|
||||
pdusession_setup_t *pdusession = pdusession_setup_resp_p->pdusessions + i;
|
||||
asn1cSequenceAdd(ie3->value.choice.PDUSessionResourceSetupListSURes.list, NGAP_PDUSessionResourceSetupItemSURes_t, item);
|
||||
/* pDUSessionID */
|
||||
item->pDUSessionID = pdusession->pdusession_id;
|
||||
|
||||
// PDU Session Resource Setup Response Transfer (Mandatory)
|
||||
byte_array_t ba = encode_ngap_pdusession_setup_response_transfer(pdusession);
|
||||
item->pDUSessionResourceSetupResponseTransfer.buf = ba.buf;
|
||||
item->pDUSessionResourceSetupResponseTransfer.size = ba.len;
|
||||
}
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (pdusession_setup_resp_p->nb_of_pdusessions_failed > 0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceFailedToSetupListSURes;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_PDUSessionResourceFailedToSetupListSURes;
|
||||
|
||||
for (int i = 0; i < pdusession_setup_resp_p->nb_of_pdusessions_failed; i++) {
|
||||
pdusession_failed_t *pdusession_failed = pdusession_setup_resp_p->pdusessions_failed + i;
|
||||
asn1cSequenceAdd(ie->value.choice.PDUSessionResourceFailedToSetupListSURes.list,
|
||||
NGAP_PDUSessionResourceFailedToSetupItemSURes_t,
|
||||
item);
|
||||
NGAP_PDUSessionResourceSetupUnsuccessfulTransfer_t pdusessionUnTransfer_p = {0};
|
||||
|
||||
/* pDUSessionID */
|
||||
item->pDUSessionID = pdusession_failed->pdusession_id;
|
||||
|
||||
/* cause */
|
||||
encode_ngap_cause(&pdusessionUnTransfer_p.cause, &pdusession_failed->cause);
|
||||
NGAP_INFO("pdusession setup response: failed pdusession ID %ld\n", item->pDUSessionID);
|
||||
|
||||
asn_encode_to_new_buffer_result_t res = asn_encode_to_new_buffer(NULL,
|
||||
ATS_ALIGNED_CANONICAL_PER,
|
||||
&asn_DEF_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer,
|
||||
&pdusessionUnTransfer_p);
|
||||
item->pDUSessionResourceSetupUnsuccessfulTransfer.buf = res.buffer;
|
||||
item->pDUSessionResourceSetupUnsuccessfulTransfer.size = res.result.encoded;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer, &pdusessionUnTransfer_p);
|
||||
}
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceSetupResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_CriticalityDiagnostics;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceSetupResponseIEs__value_PR_CriticalityDiagnostics;
|
||||
}
|
||||
|
||||
if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
|
||||
NGAP_ERROR("Failed to encode uplink transport\n");
|
||||
/* Encode procedure has failed... */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
|
||||
ue_context_p->amf_ref->assoc_id,
|
||||
buffer,
|
||||
length,
|
||||
ue_context_p->tx_stream);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngap_gNB_pdusession_modify_resp(instance_t instance, ngap_pdusession_modify_resp_t *pdusession_modify_resp_p)
|
||||
{
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = NULL;
|
||||
struct ngap_gNB_ue_context_s *ue_context_p = NULL;
|
||||
NGAP_NGAP_PDU_t pdu;
|
||||
uint8_t *buffer = NULL;
|
||||
uint32_t length;
|
||||
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(pdusession_modify_resp_p != NULL);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
if ((ue_context_p = ngap_get_ue_context(pdusession_modify_resp_p->gNB_ue_ngap_id)) == NULL) {
|
||||
/* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
|
||||
NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%08x\n", pdusession_modify_resp_p->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Uplink NAS transport can occur either during an ngap connected state
|
||||
* or during initial attach (for example: NAS authentication).
|
||||
*/
|
||||
if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED || ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
|
||||
NGAP_WARN(
|
||||
"You are attempting to send NAS data over non-connected "
|
||||
"gNB ue ngap id: %08x, current state: %d\n",
|
||||
pdusession_modify_resp_p->gNB_ue_ngap_id,
|
||||
ue_context_p->ue_state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
memset(&pdu, 0, sizeof(pdu));
|
||||
pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
|
||||
asn1cCalloc(pdu.choice.successfulOutcome, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_PDUSessionResourceModify;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_SuccessfulOutcome__value_PR_PDUSessionResourceModifyResponse;
|
||||
NGAP_PDUSessionResourceModifyResponse_t *out = &head->value.choice.PDUSessionResourceModifyResponse;
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie = (NGAP_PDUSessionResourceModifyResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSessionResourceModifyResponseIEs_t));
|
||||
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, pdusession_modify_resp_p->amf_ue_ngap_id);
|
||||
asn1cSeqAdd(&out->protocolIEs.list, ie);
|
||||
}
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie->value.choice.RAN_UE_NGAP_ID = pdusession_modify_resp_p->gNB_ue_ngap_id;
|
||||
}
|
||||
|
||||
/* PDUSessionResourceModifyListModRes optional */
|
||||
if (pdusession_modify_resp_p->nb_of_pdusessions > 0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceModifyListModRes;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_PDUSessionResourceModifyListModRes;
|
||||
|
||||
for (int i = 0; i < pdusession_modify_resp_p->nb_of_pdusessions; i++) {
|
||||
asn1cSequenceAdd(ie->value.choice.PDUSessionResourceModifyListModRes.list, NGAP_PDUSessionResourceModifyItemModRes_t, item);
|
||||
item->pDUSessionID = pdusession_modify_resp_p->pdusessions[i].pdusession_id;
|
||||
|
||||
NGAP_PDUSessionResourceModifyResponseTransfer_t transfer = {0};
|
||||
asn1cCalloc(transfer.qosFlowAddOrModifyResponseList, tmp);
|
||||
|
||||
for (int qos_flow_index = 0; qos_flow_index < pdusession_modify_resp_p->pdusessions[i].nb_of_qos_flow; qos_flow_index++) {
|
||||
asn1cSequenceAdd(tmp->list, NGAP_QosFlowAddOrModifyResponseItem_t, qos);
|
||||
qos->qosFlowIdentifier = pdusession_modify_resp_p->pdusessions[i].qos[qos_flow_index].qfi;
|
||||
}
|
||||
asn_encode_to_new_buffer_result_t res = {0};
|
||||
res = asn_encode_to_new_buffer(NULL,
|
||||
ATS_ALIGNED_CANONICAL_PER,
|
||||
&asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer,
|
||||
&transfer);
|
||||
item->pDUSessionResourceModifyResponseTransfer.buf = res.buffer;
|
||||
item->pDUSessionResourceModifyResponseTransfer.size = res.result.encoded;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, &transfer);
|
||||
|
||||
NGAP_DEBUG("pdusession_modify_resp: modified pdusession ID %ld\n", item->pDUSessionID);
|
||||
}
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (pdusession_modify_resp_p->nb_of_pdusessions_failed > 0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceFailedToModifyListModRes;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_PDUSessionResourceFailedToModifyListModRes;
|
||||
|
||||
for (int i = 0; i < pdusession_modify_resp_p->nb_of_pdusessions_failed; i++) {
|
||||
asn1cSequenceAdd(ie->value.choice.PDUSessionResourceFailedToModifyListModRes.list,
|
||||
NGAP_PDUSessionResourceFailedToModifyItemModRes_t,
|
||||
item);
|
||||
item->pDUSessionID = pdusession_modify_resp_p->pdusessions_failed[i].pdusession_id;
|
||||
|
||||
NGAP_PDUSessionResourceModifyUnsuccessfulTransfer_t pdusessionTransfer = {0};
|
||||
|
||||
// NGAP cause
|
||||
encode_ngap_cause(&pdusessionTransfer.cause, &pdusession_modify_resp_p->pdusessions_failed[i].cause);
|
||||
|
||||
asn_encode_to_new_buffer_result_t res = {0};
|
||||
NGAP_PDUSessionResourceModifyUnsuccessfulTransfer_t *pdusessionTransfer_p = NULL;
|
||||
res = asn_encode_to_new_buffer(NULL,
|
||||
ATS_ALIGNED_CANONICAL_PER,
|
||||
&asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer,
|
||||
&pdusessionTransfer);
|
||||
item->pDUSessionResourceModifyUnsuccessfulTransfer.buf = res.buffer;
|
||||
item->pDUSessionResourceModifyUnsuccessfulTransfer.size = res.result.encoded;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer, pdusessionTransfer_p);
|
||||
|
||||
NGAP_INFO("pdusession_modify_resp: failed pdusession ID %ld\n", item->pDUSessionID);
|
||||
}
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (0) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceModifyResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_CriticalityDiagnostics;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_CriticalityDiagnostics;
|
||||
}
|
||||
|
||||
if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
|
||||
NGAP_ERROR("Failed to encode uplink transport\n");
|
||||
/* Encode procedure has failed... */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
|
||||
ue_context_p->amf_ref->assoc_id,
|
||||
buffer,
|
||||
length,
|
||||
ue_context_p->tx_stream);
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief PDU Session Resource Release Response Transfer encoding (9.3.4.21 3GPP TS 38.413)
|
||||
* The transfer structure contains only an optional Secondary RAT Usage Information IE.
|
||||
* Since we don't use secondary RAT (MR-DC), we encode an empty structure. */
|
||||
static byte_array_t encode_ngap_pdusession_release_response_transfer(void)
|
||||
{
|
||||
NGAP_PDUSessionResourceReleaseResponseTransfer_t pdusessionTransfer = {0};
|
||||
|
||||
// Encode
|
||||
asn_encode_to_new_buffer_result_t res = asn_encode_to_new_buffer(NULL,
|
||||
ATS_ALIGNED_CANONICAL_PER,
|
||||
&asn_DEF_NGAP_PDUSessionResourceReleaseResponseTransfer,
|
||||
&pdusessionTransfer);
|
||||
AssertFatal(res.buffer, "ASN1 message encoding failed (%s, %lu)!\n", res.result.failed_type->name, res.result.encoded);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceReleaseResponseTransfer, &pdusessionTransfer);
|
||||
byte_array_t out = {.buf = res.buffer, .len = res.result.encoded};
|
||||
return out;
|
||||
}
|
||||
|
||||
int ngap_gNB_pdusession_release_resp(instance_t instance, ngap_pdusession_release_resp_t *pdusession_release_resp_p)
|
||||
{
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = NULL;
|
||||
struct ngap_gNB_ue_context_s *ue_context_p = NULL;
|
||||
NGAP_NGAP_PDU_t pdu;
|
||||
uint8_t *buffer = NULL;
|
||||
uint32_t length;
|
||||
int i;
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(pdusession_release_resp_p != NULL);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
if ((ue_context_p = ngap_get_ue_context(pdusession_release_resp_p->gNB_ue_ngap_id)) == NULL) {
|
||||
/* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
|
||||
NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%08x\n", pdusession_release_resp_p->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
memset(&pdu, 0, sizeof(pdu));
|
||||
pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
|
||||
asn1cCalloc(pdu.choice.successfulOutcome, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_PDUSessionResourceRelease;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_SuccessfulOutcome__value_PR_PDUSessionResourceReleaseResponse;
|
||||
NGAP_PDUSessionResourceReleaseResponse_t *out = &head->value.choice.PDUSessionResourceReleaseResponse;
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceReleaseResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceReleaseResponseIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, ue_context_p->amf_ue_ngap_id);
|
||||
}
|
||||
/* mandatory */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceReleaseResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceReleaseResponseIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie->value.choice.RAN_UE_NGAP_ID = pdusession_release_resp_p->gNB_ue_ngap_id;
|
||||
}
|
||||
|
||||
/* PDU Session Resource Released List (mandatory) */
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PDUSessionResourceReleaseResponseIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceReleasedListRelRes;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_PDUSessionResourceReleaseResponseIEs__value_PR_PDUSessionResourceReleasedListRelRes;
|
||||
|
||||
for (i = 0; i < pdusession_release_resp_p->nb_of_pdusessions_released; i++) {
|
||||
NGAP_PDUSessionResourceReleasedListRelRes_t *list = &ie->value.choice.PDUSessionResourceReleasedListRelRes;
|
||||
asn1cSequenceAdd(list->list, NGAP_PDUSessionResourceReleasedItemRelRes_t, item);
|
||||
pdusession_release_t *r = &pdusession_release_resp_p->pdusession_release[i];
|
||||
/* PDU Session ID (mandatory) */
|
||||
item->pDUSessionID = r->pdusession_id;
|
||||
/* PDU Session Resource Release Response Transfer (mandatory) */
|
||||
// Empty transfer is valid since Secondary RAT Usage Information is optional and not used
|
||||
byte_array_t transfer = encode_ngap_pdusession_release_response_transfer();
|
||||
OCTET_STRING_fromBuf(&item->pDUSessionResourceReleaseResponseTransfer, (const char *)transfer.buf, transfer.len);
|
||||
free_byte_array(transfer);
|
||||
NGAP_DEBUG("PDU Session Resource Release Response: pdusession ID %ld\n", item->pDUSessionID);
|
||||
}
|
||||
}
|
||||
|
||||
if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
|
||||
NGAP_ERROR("Failed to encode release response\n");
|
||||
/* Encode procedure has failed... */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
|
||||
ue_context_p->amf_ref->assoc_id,
|
||||
buffer,
|
||||
length,
|
||||
ue_context_p->tx_stream);
|
||||
NGAP_INFO("pdusession_release_response sended gNB_UE_NGAP_ID %u amf_ue_ngap_id %lu nb_of_pdusessions_released %d \n",
|
||||
pdusession_release_resp_p->gNB_ue_ngap_id,
|
||||
(uint64_t)ue_context_p->amf_ue_ngap_id,
|
||||
pdusession_release_resp_p->nb_of_pdusessions_released);
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef NGAP_GNB_PDU_SESSION_MANAGEMENT_H_
|
||||
#define NGAP_GNB_PDU_SESSION_MANAGEMENT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "assertions.h"
|
||||
#include "ngap_messages_types.h"
|
||||
#include "ngap_msg_includes.h"
|
||||
|
||||
int ngap_gNB_pdusession_setup_resp(instance_t instance, ngap_pdusession_setup_resp_t *pdusession_setup_resp_p);
|
||||
|
||||
int ngap_gNB_pdusession_modify_resp(instance_t instance, ngap_pdusession_modify_resp_t *pdusession_modify_resp_p);
|
||||
|
||||
int ngap_gNB_pdusession_release_resp(instance_t instance, ngap_pdusession_release_resp_t *pdusession_release_resp_p);
|
||||
|
||||
#endif /* NGAP_GNB_PDU_SESSION_MANAGEMENT_H_ */
|
||||
@@ -41,6 +41,12 @@
|
||||
#include "NGAP_GTPTunnel.h"
|
||||
#include "NGAP_InitiatingMessage.h"
|
||||
#include "NGAP_NGAP-PDU.h"
|
||||
#include "NGAP_Paging.h"
|
||||
#include "NGAP_AssistanceDataForPaging.h"
|
||||
#include "NGAP_PagingAttemptInformation.h"
|
||||
#include "NGAP_FiveG-S-TMSI.h"
|
||||
#include "NGAP_TAI.h"
|
||||
#include "NGAP_UEPagingIdentity.h"
|
||||
#include "NGAP_PDUSessionResourceFailedToModifyItemModRes.h"
|
||||
#include "NGAP_PDUSessionResourceFailedToSetupItemCxtRes.h"
|
||||
#include "NGAP_PDUSessionResourceFailedToSetupItemSURes.h"
|
||||
@@ -102,7 +108,6 @@
|
||||
#include "NGAP_PDUSessionResourceAdmittedItem.h"
|
||||
#include "NGAP_HandoverRequestAcknowledgeTransfer.h"
|
||||
#include "NGAP_QosFlowItemWithDataForwarding.h"
|
||||
#include "NR_HandoverPreparationInformation.h"
|
||||
#include "NGAP_HandoverNotify.h"
|
||||
#include "NGAP_DRBsSubjectToStatusTransferItem.h"
|
||||
#include "NGAP_DRBStatusUL.h"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef NGAP_UTILS_H_
|
||||
#define NGAP_UTILS_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "assertions.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
|
||||
#define NGAP_UE_ID_FMT "0x%06" PRIX32
|
||||
|
||||
#define NGAP_ERROR(x, args...) LOG_E(NGAP, x, ##args)
|
||||
#define NGAP_WARN(x, args...) LOG_W(NGAP, x, ##args)
|
||||
#define NGAP_TRAF(x, args...) LOG_I(NGAP, x, ##args)
|
||||
#define NGAP_INFO(x, args...) LOG_I(NGAP, x, ##args)
|
||||
#define NGAP_DEBUG(x, args...) LOG_D(NGAP, x, ##args)
|
||||
|
||||
#define NGAP_FIND_PROTOCOLIE_BY_ID(IE_TYPE, ie, container, IE_ID, mandatory) \
|
||||
do { \
|
||||
IE_TYPE **ptr; \
|
||||
ie = NULL; \
|
||||
for (ptr = container->protocolIEs.list.array; ptr < &container->protocolIEs.list.array[container->protocolIEs.list.count]; \
|
||||
ptr++) { \
|
||||
if ((*ptr)->id == IE_ID) { \
|
||||
ie = *ptr; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
if (ie == NULL) { \
|
||||
if (mandatory) { \
|
||||
AssertFatal(NGAP, "NGAP_FIND_PROTOCOLIE_BY_ID ie is NULL (searching for ie: %ld)\n", IE_ID); \
|
||||
} else { \
|
||||
NGAP_DEBUG("NGAP_FIND_PROTOCOLIE_BY_ID ie is NULL (searching for ie: %ld)\n", IE_ID); \
|
||||
} \
|
||||
} \
|
||||
} while (0); \
|
||||
if (mandatory && !ie) \
|
||||
return -1
|
||||
|
||||
#endif /* NGAP_UTILS_H_ */
|
||||
@@ -0,0 +1,6 @@
|
||||
add_executable(ngap_lib_test ngap_lib_test.c)
|
||||
add_dependencies(tests ngap_lib_test)
|
||||
add_test(NAME ngap_lib_test COMMAND ngap_lib_test)
|
||||
target_link_libraries(ngap_lib_test PRIVATE ngap asn1_ngap asn1_lte_rrc_hdrs UTIL ds)
|
||||
target_include_directories(ngap_lib_test PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include <stdlib.h> // abort
|
||||
#include <stdint.h> // uint*_t
|
||||
#include <stdio.h> // printf
|
||||
#include <stdbool.h> // bool
|
||||
|
||||
#include "common/utils/assertions.h" // AssertFatal, DevAssert
|
||||
#include "common/utils/utils.h" // malloc_or_fail, calloc_or_fail
|
||||
#include "common/utils/ds/byte_array.h" // create_byte_array
|
||||
#include "conversions.h" // TBCD_TO_MCC_MNC, OCTET_STRING_TO_INT*
|
||||
#include "ngap_common.h" // NGAP_ERROR, NGAP_DEBUG, ngap_messages_types.h
|
||||
#include "ngap_gNB_paging.h" // encode_ng_paging, decode_ng_paging, free_ng_paging
|
||||
#include "ngap_msg_includes.h" // NGAP_NGAP_PDU_t, NGAP_Paging_t, etc.
|
||||
#include "oai_asn1.h" // ASN_STRUCT_FREE, asn_DEF_NGAP_NGAP_PDU
|
||||
#include "config/config_load_configmodule.h" // configmodule_interface_t
|
||||
#include "common/utils/LOG/log.h" // log_init
|
||||
|
||||
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
|
||||
{
|
||||
printf("detected error at %s:%d:%s: %s\n", file, line, function, s);
|
||||
abort();
|
||||
}
|
||||
|
||||
static NGAP_NGAP_PDU_t *ngap_encode_decode(const NGAP_NGAP_PDU_t *enc_pdu)
|
||||
{
|
||||
DevAssert(enc_pdu != NULL);
|
||||
char errbuf[1024];
|
||||
size_t errlen = sizeof(errbuf);
|
||||
int ret = asn_check_constraints(&asn_DEF_NGAP_NGAP_PDU, enc_pdu, errbuf, &errlen);
|
||||
AssertFatal(ret == 0, "asn_check_constraints() failed: %s\n", errbuf);
|
||||
|
||||
uint8_t msgbuf[16384];
|
||||
asn_enc_rval_t enc = aper_encode_to_buffer(&asn_DEF_NGAP_NGAP_PDU, NULL, enc_pdu, msgbuf, sizeof(msgbuf));
|
||||
AssertFatal(enc.encoded > 0, "aper_encode_to_buffer() failed\n");
|
||||
|
||||
NGAP_NGAP_PDU_t *dec_pdu = NULL;
|
||||
asn_codec_ctx_t st = {.max_stack_size = 100 * 1000};
|
||||
asn_dec_rval_t dec = aper_decode(&st, &asn_DEF_NGAP_NGAP_PDU, (void **)&dec_pdu, msgbuf, enc.encoded, 0, 0);
|
||||
AssertFatal(dec.code == RC_OK, "aper_decode() failed\n");
|
||||
|
||||
return dec_pdu;
|
||||
}
|
||||
|
||||
static void ngap_msg_free(NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
}
|
||||
|
||||
/** @brief Test NGAP Paging encoding/decoding roundtrip */
|
||||
static void test_ngap_paging_encode_decode(void)
|
||||
{
|
||||
// Create test paging indication
|
||||
ngap_paging_ind_t orig = {0};
|
||||
orig.ue_paging_identity.s_tmsi.amf_set_id = 123; // Valid range: 0-1023 (10 bits)
|
||||
orig.ue_paging_identity.s_tmsi.amf_pointer = 45; // Valid range: 0-63 (6 bits)
|
||||
orig.ue_paging_identity.s_tmsi.m_tmsi = 0x12345678;
|
||||
orig.n_tai = 2;
|
||||
orig.tai_list[0].plmn.mcc = 208;
|
||||
orig.tai_list[0].plmn.mnc = 95;
|
||||
orig.tai_list[0].plmn.mnc_digit_length = 2;
|
||||
orig.tai_list[0].tac = 12345;
|
||||
orig.tai_list[1].plmn.mcc = 001;
|
||||
orig.tai_list[1].plmn.mnc = 01;
|
||||
orig.tai_list[1].plmn.mnc_digit_length = 2;
|
||||
orig.tai_list[1].tac = 23456;
|
||||
orig.paging_drx = malloc_or_fail(sizeof(ngap_paging_drx_t));
|
||||
*orig.paging_drx = NGAP_PAGING_DRX_128;
|
||||
orig.paging_priority = malloc_or_fail(sizeof(ngap_paging_priority_t));
|
||||
*orig.paging_priority = NGAP_PAGING_PRIO_LEVEL2;
|
||||
uint8_t ue_cap_data[] = {0x01, 0x02, 0x03, 0x04};
|
||||
orig.ue_radio_capability = malloc_or_fail(sizeof(byte_array_t));
|
||||
*orig.ue_radio_capability = create_byte_array(sizeof(ue_cap_data), ue_cap_data);
|
||||
orig.origin = calloc_or_fail(1, sizeof(*orig.origin));
|
||||
*orig.origin = NGAP_PAGING_ORIGIN_NON_3GPP;
|
||||
|
||||
// Encode
|
||||
NGAP_NGAP_PDU_t *enc_pdu = encode_ng_paging(&orig);
|
||||
AssertFatal(enc_pdu != NULL, "encode_ng_paging(): failed to encode message\n");
|
||||
LOG_A(NGAP, "encoded: m_tmsi=0x%x, n_tai=%d\n", orig.ue_paging_identity.s_tmsi.m_tmsi, orig.n_tai);
|
||||
|
||||
// Encode/decode roundtrip
|
||||
NGAP_NGAP_PDU_t *dec_pdu = ngap_encode_decode(enc_pdu);
|
||||
ngap_msg_free(enc_pdu);
|
||||
|
||||
// Decode
|
||||
ngap_paging_ind_t decoded = {0};
|
||||
bool decode_ret = decode_ng_paging(&decoded, dec_pdu);
|
||||
AssertFatal(decode_ret, "decode_ng_paging(): could not decode message\n");
|
||||
ngap_msg_free(dec_pdu);
|
||||
LOG_A(NGAP, "decoded: m_tmsi=0x%x, n_tai=%d\n", decoded.ue_paging_identity.s_tmsi.m_tmsi, decoded.n_tai);
|
||||
|
||||
// Verify decoded values using equality function
|
||||
bool eq_ret = eq_ng_paging(&orig, &decoded);
|
||||
AssertFatal(eq_ret, "eq_ng_paging(): decoded message doesn't match original\n");
|
||||
LOG_A(NGAP, "eq_ng_paging(): decoded message matches original\n");
|
||||
|
||||
free_ng_paging(&decoded);
|
||||
free_ng_paging(&orig);
|
||||
}
|
||||
|
||||
// Stub for uniqCfg required by logging/config system
|
||||
configmodule_interface_t *uniqCfg = NULL;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Initialize logging system
|
||||
logInit();
|
||||
set_log(NGAP, OAILOG_INFO);
|
||||
|
||||
test_ngap_paging_encode_decode();
|
||||
LOG_A(NGAP, "All NGAP paging tests passed!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -548,6 +548,18 @@ do { \
|
||||
(bITsTRING)->bits_unused = 0; \
|
||||
} while(0)
|
||||
|
||||
#define IRNTI_TO_BIT_STRING(mACRO, bITsTRING) \
|
||||
do { \
|
||||
(bITsTRING)->buf = calloc(5, sizeof(uint8_t)); \
|
||||
(bITsTRING)->buf[0] = ((mACRO) >> 32) & 0xff; \
|
||||
(bITsTRING)->buf[1] = ((mACRO) >> 24) & 0xff; \
|
||||
(bITsTRING)->buf[2] = ((mACRO) >> 16) & 0xff; \
|
||||
(bITsTRING)->buf[3] = ((mACRO) >> 8) & 0xff; \
|
||||
(bITsTRING)->buf[4] = ((mACRO) & 0xff); \
|
||||
(bITsTRING)->size = 5; \
|
||||
(bITsTRING)->bits_unused = 0; \
|
||||
} while (0)
|
||||
|
||||
/* Used to format an uint32_t containing an ipv4 address */
|
||||
#define IPV4_ADDR "%u.%u.%u.%u"
|
||||
#define IPV4_ADDR_FORMAT(aDDRESS) \
|
||||
|
||||
Reference in New Issue
Block a user