This sample is provided as part of the OSS ASN.1 Tools trial and commercial shipments. The source code and related files are listed below for reference.
The sample demonstrates how to use the OSS ASN.1 Tools to process messages for the 3GPP LTE X2AP standard (TS 36.423 version 19.0.0).
It runs on Linux on x86-64 as an example and illustrates how to decode, inspect, and create LTE X2AP messages using the OSS ASN.1 Tools API.
The sample reads X2AP messages from .per files, decodes and prints them, and creates and encodes successful outcome messages.
A makefile is included for building and running the test program using the OSS ASN.1/C runtime.
To explore more samples (LTE RRC, 5G RRC, S1AP, X2AP, NGAP), visit the main Sample Code page.
This sample shows how to:
The files listed below are included in the OSS ASN.1 Tools trial and commercial shipments and are used by this sample program:
| File | Description |
|---|---|
| README.TXT | Instructions and sample overview. |
| *.asn | ASN.1 source files that describe the 3GPP LTE X2AP protocol (TS 36.423), used with this program example. |
| X2AP-PDU_HandoverRequest_bin.per | Valid PER-encoded X2AP message (HandoverRequest). It should pass testing. |
| tx2ap.c | Simple C program that shows how to work with LTE X2AP protocol data. It reads input messages from files, decodes and prints them, and creates and encodes successful outcome messages. |
| makefile | Makefile that builds and runs the sample test. |
| makefile.rtoed | Makefile that builds and runs the sample test using RTOED. |
| gui/ | ASN.1 Studio project for viewing/compiling schema and generating sample data. |
(Installation instructions for the OSS ASN.1 Tools are included in all trial and commercial shipments.)
To build and run this sample, install a trial or licensed version of the OSS ASN.1 Tools for C.
If your shipment is runtime-only, generate the .c and .h files by running:
make cross
This creates a samples/cross directory with:
make
This command compiles the sample C source, links it with the OSS ASN.1 static library, and executes the test. To use another type of library in the shipment (such as a shared library), set the A makefile variable, for example:
make A=so
make clean
Note: The C source code in this sample is platform-independent. Linux commands are shown as an example, but equivalent samples and build instructions are available for Windows, macOS, and other platforms. For help with platform-specific instructions, please contact OSS Support.
The following listing shows the main C source file for this sample test program, tx2ap.c. It demonstrates how to read PER-encoded LTE X2AP messages from files, decode and print them, and create and encode a response message using the OSS ASN.1 Tools API.
/*****************************************************************************/
/* Copyright (C) 2025 OSS Nokalva, Inc. All rights reserved. */
/* THIS FILE IS PROPRIETARY MATERIAL OF OSS NOKALVA, INC. */
/* AND MAY BE USED ONLY BY DIRECT LICENSEES OF OSS NOKALVA, INC. */
/* THIS FILE MAY NOT BE DISTRIBUTED. */
/* THIS COPYRIGHT STATEMENT MAY NOT BE REMOVED. */
/* THIS SAMPLE PROGRAM IS PROVIDED AS IS. THE SAMPLE PROGRAM AND ANY RESULTS */
/* OBTAINED FROM IT ARE PROVIDED WITHOUT ANY WARRANTIES OR REPRESENTATIONS, */
/* EXPRESS, IMPLIED OR STATUTORY. */
/*****************************************************************************/
/*
* Demonstrates work with data for X2AP protocol of LTE
*/
#include <errno.h>
#include <string.h>
#include "x2ap_r19.h"
/* Names of Criticality values */
static const char *Criticalities[] = { "reject", "ignore", "notify" };
/* Names of CSGMembershipStatus values */
static const char *MemshipStatus[] = { "member", "not-member" };
#define printBorder() \
ossPrint(world, "-------------------------------------------------------\n")
/* The directory where input files reside. It is set to the command line parameter
* if any and to the directory where the sample is started, otherwise.
*/
char *filesDir;
/*
* FUNCTION reportError() prints error message and error code returned
* by any runtime API function.
*
* PARAMETERS
* world OSS environment variable
* where error origin
* errcode error code
* errmsg error message or NULL if ossGetErrMsg() should be used
* to get it
*
* RETURNS errcode
*/
static int reportError(OssGlobal *world, const char *where,
int errcode, const char *errmsg)
{
/* Error occured */
if (errcode) {
ossPrint(world, "%s failed, error code: %d\n", where, errcode);
/* Get and print error message */
if (errmsg || (world && NULL != (errmsg = ossGetErrMsg(world))))
ossPrint(world, "Error text: %s\n", errmsg);
}
return errcode;
}
/*
* FUNCTION Helper function to open file in the specified directory.
*
* PARAMETERS
* world OSS environment variable
* directoryName directory where fileName resides
* fileName file to open
*
* RETURNS pointer to file on success, NULL on failure
*/
FILE * openInputFile(OssGlobal *world, char *directoryName, const char *fileName)
{
char *path;
FILE *fv;
const char kPathSeparator =
#ifdef _WIN32
'\\';
#else
'/';
#endif
if (directoryName) {
size_t dLength = strlen(directoryName);
if (NULL == (path = ossGetMemory(world, dLength + strlen(fileName) + 2))) {
return NULL;
}
memcpy(path, directoryName, dLength);
if (path[dLength - 1] != kPathSeparator)
path[dLength++] = kPathSeparator;
strcpy(path+dLength, fileName);
} else {
path = (char *) fileName;
}
if (!(fv = fopen(path, "rb"))) {
ossPrint(world, "Failed to open the '%s' file. Restart the sample program using the file "
"location as the input parameter.\n", path);
}
if (path != fileName)
ossFreeMemory(world, path);
return fv;
}
/*
* FUNCTION readEncodingFromFile() reads serialized message from specified
* file
*
* PARAMETERS
* world OSS environment variable
* filename name of file containing serialized message
* encoding read message
*
* RETURNS 0 on success, error code on failure
*/
static int readEncodingFromFile(OssGlobal *world,
const char *filename, OssBuf *encoding)
{
FILE *in = openInputFile(world, filesDir, filename);
long length;
unsigned char *data;
if (!in)
return reportError(world, "fopen()", errno, strerror(errno));
if (fseek(in, 0, SEEK_END))
return reportError(world, "fseek()", errno, strerror(errno));
length = ftell(in);
if (length < 0)
return reportError(world, "ftell()", errno, strerror(errno));
/* Allocate memory to store completely read message in it */
data = ossGetMemory(world, (size_t)length);
if (!data) {
reportError(world, "ossGetMemory()", OUT_MEMORY, NULL);
fclose(in);
return OUT_MEMORY;
}
rewind(in);
if ((size_t)length != fread(data, sizeof(char), (size_t)length, in))
return reportError(world, "fread()", 1, NULL);
fclose(in);
/* Store read message in output argument */
encoding->value = data;
encoding->length = (size_t)length;
return 0;
}
/*****************************************************************************
* Printing functions below are intended to demonstrate how to access X2AP
* PDU components and to analyze their contents.
*****************************************************************************/
/*
* FUNCTION printHexString() prints data (octet string) as a sequence of
* hexadecimal digits 'XXXX...'H
*
* PARAMETERS
* world OSS environment variable
* length length of input data
* value pointer to input data
* indent indentation
*
* RETURNS 0 on success, error code on failure
*/
static int printHexString(OssGlobal *world,
unsigned int length, unsigned char *value, int indent)
{
unsigned int i, j,
max_pos = 74 - indent;
ossPrint(world, "'");
for (i = 0, j = 1; i < length; i++, j += 2) {
/* skip to next line when the current one is filled */
if (j >= max_pos) {
ossPrint(world, "'\n%*s'", indent, "");
j = 1;
}
ossPrint(world, "%02X", value[i]);
}
ossPrint(world, "'H");
return 0;
}
/* Prints a BIT STRING as a sequence of hexadecimal digits */
#define printHexBitString(ctx, length, value, indent)\
printHexString(ctx, (length + 7)/8, value, indent)
/*
* FUNCTION copyBits() copies necessary number of bits from input string
*
* PARAMETERS
* world OSS environment variable
* value pointer to input data
* length length of new string in bits
* (number of bits that should be copied from the input string)
*
* RETURNS copied string on success, NULL on failure
*/
static unsigned char *copyBits(OssGlobal *world, unsigned char *value, OSS_UINT32 length)
{
unsigned char *return_value;
int byte_length = (length + 7) >> 3;
if (!length || NULL == (return_value = (unsigned char*)ossGetInitializedMemory(world, byte_length)))
return NULL;
memcpy(return_value, value, byte_length);
return return_value;
}
/*
* FUNCTION copyOctets() copies necessary number of bytes from input string
*
* PARAMETERS
* world OSS environment variable
* value pointer to input data
* length length of new string in bytes
* (number of bytes that should be copied from the input string)
*
* RETURNS copied string on success, NULL on failure
*/
static unsigned char *copyOctets(OssGlobal *world, unsigned char *value, OSS_UINT32 length)
{
unsigned char *return_value;
if (!length || NULL == (return_value = (unsigned char*)ossGetInitializedMemory(world, length)))
return NULL;
memcpy(return_value, value, length);
return return_value;
}
/*
* FUNCTION printMDTPLMNList() prints MDTPLMNList data
*
* PARAMETERS
* world OSS environment variable
* lst pointer to MDTPLMNList data to print
* indent indentation
*
* RETURNS 0 on success, error code on failure
*/
static int printMDTPLMNList(OssGlobal *world,
OSS_X2AP_MDTPLMNList lst,
int indent)
{
size_t i;
ossPrint(world, "%*s%s includes pLMN-Identity elements:\n", indent++, "",
"MDTPLMNList");
/* Print each IE in the list */
for (i = 1; lst; lst = lst->next, i++) {
ossPrint(world, "\n%*s#%d : ", indent, "", i);
printHexString(world, sizeof(lst->value), lst->value, indent);
ossPrint(world, "\n");
}
return 0;
}
/*
* FUNCTION printProtocolExtensions() prints ProtocolExtensionContainer
* data. ossPrintPDU() is called to print PDU stored in
* an OpenType value. The function has been changed for 10th
* release since some lists for iE-Extensions fields are not
* empty.
*
* PARAMETERS
* world OSS environment variable
* ie_ext pointer to ProtocolExtensionContainer data to print
* oset_num Object Set number for iE-Extensions field
* indent indentation
*
* RETURNS 0 on success, error code on failure
*/
static int printProtocolExtensions(OssGlobal *world, void *ie_ext, int oset_num,
int indent)
{
struct ProtocolExtensions {
struct ProtocolExtensions *next;
struct {
OSS_X2AP_ProtocolIE_ID id;
OSS_X2AP_Criticality criticality;
struct {
enum {max = INT_MAX} enum_holder;
OssBuf encoded_holder;
void *decoded_holder;
} extensionValue;
} value;
}; /* The structure for correct type cast of ProtocolExtensionContainer data */
struct ProtocolExtensions *ie_cst = (struct ProtocolExtensions *)ie_ext;
size_t i;
ossPrint(world, "%*s%s includes the following IEs:\n", indent++, "",
"iE-Extensions");
/* Print each IE in the list */
for (i = 1; ie_cst; ie_cst = ie_cst->next, i++) {
switch (oset_num) {
case OSS_X2AP_HandoverRestrictionList_ExtIEs_OSET:
case OSS_X2AP_UEAggregate_MaximumBitrate_ExtIEs_OSET:
case OSS_X2AP_AS_SecurityInformation_ExtIEs_OSET:
case OSS_X2AP_GUMMEI_ExtIEs_OSET:
case OSS_X2AP_CellType_ExtIEs_OSET:
case OSS_X2AP_ECGI_ExtIEs_OSET:
case OSS_X2AP_LastVisitedEUTRANCellInformation_ExtIEs_OSET:
case OSS_X2AP_UESecurityCapabilities_ExtIEs_OSET:
{
OSS_X2AP_UESecurityCapabilities_ExtIEs_Extension *extval =
(OSS_X2AP_UESecurityCapabilities_ExtIEs_Extension *)
&ie_cst->value.extensionValue;
ossPrint(world, "%*s#%d: id = %2u, criticality = %s\n", indent, "",
i, ie_cst->value.id, Criticalities[ie_cst->value.criticality]);
if (extval->pduNum && extval->decoded.other.pdu_X2AP_PDU)
ossPrintPDU(world, extval->pduNum, extval->decoded.other.pdu_X2AP_PDU);
else
ossPrint(world, "PDU is not decoded\n");
break;
}
case OSS_X2AP_UE_ContextInformation_ExtIEs_OSET:
{
OSS_X2AP_UE_ContextInformation_ExtIEs_Extension *extval =
(OSS_X2AP_UE_ContextInformation_ExtIEs_Extension *)
&ie_cst->value.extensionValue;
ossPrint(world, "%*s#%d: id = %2u, criticality = %s\n", indent, "",
i, ie_cst->value.id, Criticalities[ie_cst->value.criticality]);
if (extval->pduNum ==
OSS_X2AP_PDU_UE_ContextInformation_ExtIEs_Extension_ManagementBasedMDTallowed)
ossPrintPDU(world, extval->pduNum,
extval->decoded.pdu_ManagementBasedMDTallowed);
else if (extval->pduNum ==
OSS_X2AP_PDU_UE_ContextInformation_ExtIEs_Extension_MDTPLMNList)
printMDTPLMNList(world, *(extval->decoded.pdu_MDTPLMNList), indent);
else if (extval->pduNum && extval->decoded.other.pdu_X2AP_PDU)
ossPrintPDU(world, extval->pduNum, extval->decoded.other.pdu_X2AP_PDU);
else
ossPrint(world, "PDU is not decoded\n");
break;
}
case OSS_X2AP_TraceActivation_ExtIEs_OSET:
{
OSS_X2AP_TraceActivation_ExtIEs_Extension *extval =
(OSS_X2AP_TraceActivation_ExtIEs_Extension *)
&ie_cst->value.extensionValue;
ossPrint(world, "%*s#%d: id = %2u, criticality = %s\n", indent, "",
i, ie_cst->value.id, Criticalities[ie_cst->value.criticality]);
if (extval->pduNum ==
OSS_X2AP_PDU_TraceActivation_ExtIEs_Extension_MDT_Configuration)
ossPrintPDU(world, extval->pduNum,
extval->decoded.pdu_MDT_Configuration);
else if (extval->pduNum && extval->decoded.other.pdu_X2AP_PDU)
ossPrintPDU(world, extval->pduNum, extval->decoded.other.pdu_X2AP_PDU);
else
ossPrint(world, "PDU is not decoded\n");
break;
}
}
}
return 0;
}
/*
* FUNCTION printE_RABs_ToBeSetup_List() prints E_RABs_ToBeSetup_List
* data. ossPrintPDU() is called to print PDU stored in
* an OpenType value.
*
* PARAMETERS
* world OSS environment variable
* lst pointer to E_RABs_ToBeSetup_List data to print
* indent indentation
*
* RETURNS 0 on success, error code on failure
*/
static int printE_RABs_ToBeSetup_List(OssGlobal *world,
OSS_X2AP_E_RABs_ToBeSetup_List lst,
int indent)
{
size_t i;
ossPrint(world, "%*s%s includes the following IEs:\n", indent++, "",
"e_RABs-ToBeSetup-List");
/* Print each IE in the list */
for (i = 1; lst; lst = lst->next, i++) {
OSS_X2AP_E_RABs_ToBeSetup_ItemIEs_Value *extval = &lst->value.value;
ossPrint(world, "%*s#%d: id = %2u, criticality = %s\n", indent, "",
i, lst->value.id, Criticalities[lst->value.criticality]);
if (extval->pduNum && extval->decoded.pdu_E_RABs_ToBeSetup_Item)
ossPrintPDU(world, extval->pduNum, extval->decoded.pdu_E_RABs_ToBeSetup_Item);
else
ossPrint(world, "PDU is not decoded\n");
}
return 0;
}
/*
* FUNCTION printHistoryInformation() prints HistoryInformation
* data.
*
* PARAMETERS
* world OSS environment variable
* lst pointer to HistoryInformation data to print
* indent indentation
*
* RETURNS 0 on success, error code on failure
*/
static int printHistoryInformation(OssGlobal *world,
OSS_X2AP_UE_HistoryInformation lst,
int indent)
{
OSS_X2AP_LastVisitedCell_Item *lv_item;
int i;
ossPrint(world, "%*s%s includes the following IEs:\n", indent++, "",
"HistoryInformation");
/* Print each IE in the list */
for (i = 1; lst; lst = lst->next, i++) {
lv_item = &lst->value;
if (lv_item->choice == OSS_X2AP_e_UTRAN_Cell_chosen) {
OSS_X2AP_LastVisitedEUTRANCellInformation *cell_inf = &lv_item->u.e_UTRAN_Cell;
ossPrint(world, "%*s#%d LastVisitedCell-Item: e-UTRAN-Cell: \n", indent, "", i);
ossPrint(world, "%*sglobal-Cell-ID:\n", ++indent, "");
ossPrintPDU(world, OSS_X2AP_ECGI_PDU, &cell_inf->global_Cell_ID);
ossPrint(world, "%*sCellType:\n", indent, "");
ossPrint(world, "%*sCell-Size: %u\n", ++indent, "", cell_inf->cellType.cell_Size);
if (cell_inf->cellType.iE_Extensions)
printProtocolExtensions(world, (void*)cell_inf->cellType.iE_Extensions,
OSS_X2AP_CellType_ExtIEs_OSET, 1);
ossPrint(world, "%*stime-UE-StayedInCell: %u\n", --indent, "", cell_inf->time_UE_StayedInCell);
if (cell_inf->iE_Extensions)
printProtocolExtensions(world, (void*)cell_inf->iE_Extensions,
OSS_X2AP_LastVisitedEUTRANCellInformation_ExtIEs_OSET, indent);
indent--;
} else if (lv_item->choice == OSS_X2AP_uTRAN_Cell_chosen) {
OSS_X2AP_LastVisitedUTRANCellInformation *cell_inf = &lv_item->u.uTRAN_Cell;
ossPrint(world, "%*s#%d LastVisitedCell-Item: uTRAN_Cell: \n", indent, "");
printHexString(world, cell_inf->length, cell_inf->value, indent);
} else if (lv_item->choice == OSS_X2AP_gERAN_Cell_chosen) {
OSS_X2AP_LastVisitedGERANCellInformation *cell_inf = &lv_item->u.gERAN_Cell;
ossPrint(world, "%*s#%d LastVisitedCell-Item: gERAN_Cell: ", indent, "");
if (cell_inf->choice == OSS_X2AP_undefined_chosen) {
ossPrint(world, "undefined: %c", cell_inf->u.undefined);
} else {
ossPrint(world, "\n");
ossPrint(world, "Incorrect value\n");
}
} else {
ossPrint(world, "Incorrect value\n");
}
}
return 0;
}
/*
* FUNCTION printProtocolIEs() prints ProtocolIE_Container data.
* ossPrintPDU() is called to print IEs that are not
* handled by this function.
*
* PARAMETERS
* world OSS environment variable
* pdu input X2AP PDU which data should be printed
* ies pointer to ProtocolIE-Container data to print
* indent indentation
*
* RETURNS 0 on success, error code on failure
*/
static int printProtocolIEs(OssGlobal *world,
struct OSS_X2AP_HandoverRequestIEs *ies, int indent)
{
int err = 0;
int i;
ossPrint(world, "%*s%s includes the following IEs:\n", indent++, "", "protocolIEs");
/* Print each IE */
for (i = 1; ies; ies = ies->next, i++) {
struct OSS_X2AP_HandoverRequestIEsVal *field = &ies->value;
OSS_X2AP_HandoverRequest_IEs_Value *value = &field->value;
ossPrint(world, "\n%*s#%d: id = %2u, criticality = %s\n", indent, "",
i, field->id, Criticalities[field->criticality]);
++indent;
/* Print UE-X2AP-ID IE */
if (field->id == OSS_X2AP_id_Old_eNB_UE_X2AP_ID) {
ossPrint(world, "%*svalue %s: %u\n", indent, "", "UE-X2AP-ID",
*value->decoded.pdu_UE_X2AP_ID);
/* Cause IE */
} else if (field->id == OSS_X2AP_id_Cause) {
OSS_X2AP_Cause *pcause = value->decoded.pdu_Cause;
/*
* Named values can be printed below instead of numbers for all
* Cause components
*/
ossPrint(world, "%*svalue %s: ", indent, "", "Cause");
/* radioNetwork */
if (pcause->choice == OSS_X2AP_radioNetwork_chosen)
ossPrint(world, "%s : %d", "radioNetwork",
pcause->u.radioNetwork);
/* transport */
else if (pcause->choice == OSS_X2AP_transport_chosen)
ossPrint(world, "%s : %d", "transport", pcause->u.transport);
/* protocol */
else if (pcause->choice == OSS_X2AP_protocol_chosen)
ossPrint(world, "%s : %d", "protocol", pcause->u.protocol);
/* misc */
else if (pcause->choice == OSS_X2AP_misc_chosen)
ossPrint(world, "%s : %d", "misc", pcause->u.misc);
ossPrint(world, "\n");
/* ECGI IE */
} else if (field->id == OSS_X2AP_id_TargetCell_ID) {
OSS_X2AP_ECGI *ptarget = value->decoded.pdu_ECGI;
ossPrint(world, "%*svalue %s: ", indent, "", "ECGI");
indent++;
/* pLMN-Identity */
ossPrint(world, "\n%*s%s: ", indent, "", "pLMN-Identity");
printHexString(world, sizeof(ptarget->pLMN_Identity),
ptarget->pLMN_Identity, indent);
/* eUTRANcellIdentifier */
ossPrint(world, "\n%*s%s: ", indent, "", "eUTRANcellIdentifier");
printHexBitString(world, ptarget->eUTRANcellIdentifier.length,
ptarget->eUTRANcellIdentifier.value, indent);
ossPrint(world, "\n");
/* iE_Extensions is optional */
if (ptarget->iE_Extensions)
printProtocolExtensions(world, (void*)ptarget->iE_Extensions,
OSS_X2AP_ECGI_ExtIEs_OSET, indent);
indent--;
/* GUMMEI IE */
} else if (field->id == OSS_X2AP_id_GUMMEI_ID) {
OSS_X2AP_GUMMEI *pgummei = value->decoded.pdu_GUMMEI;
ossPrint(world, "%*svalue %s: ", indent, "", "GUMMEI");
indent++;
/* gU-Group-ID */
ossPrint(world, "\n%*s%s: ", indent, "", "gU-Group-ID");
{
OSS_X2AP_GU_Group_ID *pgroup = &pgummei->gU_Group_ID;
indent++;
/* pLMN-Identity */
ossPrint(world, "\n%*s%s: ", indent, "", "pLMN-Identity");
printHexString(world, sizeof(pgroup->pLMN_Identity),
pgroup->pLMN_Identity, indent);
/* mME-Group-ID */
ossPrint(world, "\n%*s%s: ", indent, "", "mME-Group-ID");
printHexString(world, sizeof(pgroup->mME_Group_ID),
pgroup->mME_Group_ID, indent);
ossPrint(world, "\n");
/* iE_Extensions is optional */
if (pgroup->iE_Extensions)
printProtocolExtensions(world, (void*)pgroup->iE_Extensions,
OSS_X2AP_GU_Group_ID_ExtIEs_OSET, indent);
indent--;
}
/* mME-Code */
ossPrint(world, "%*s%s: ", indent, "", "mME-Code");
printHexString(world, sizeof(pgummei->mME_Code),
pgummei->mME_Code, indent);
ossPrint(world, "\n");
/* iE_Extensions is optional */
if (pgummei->iE_Extensions)
printProtocolExtensions(world, (void *)pgummei->iE_Extensions,
OSS_X2AP_GUMMEI_ExtIEs_OSET, indent);
indent--;
/* UE-ContextInformation IE */
} else if (field->id == OSS_X2AP_id_UE_ContextInformation) {
OSS_X2AP_UE_ContextInformation *pinfo = value->decoded.pdu_UE_ContextInformation;
ossPrint(world, "%*svalue %s : ", indent, "", "UE-ContextInformation");
indent++;
/* mME-UE-S1AP-ID */
ossPrint(world, "\n%*s%s: %u", indent, "", "mME-UE-S1AP-ID",
pinfo->mME_UE_S1AP_ID);
/* uESecurityCapabilities */
ossPrint(world, "\n%*s%s: ", indent, "",
"uESecurityCapabilities");
{
OSS_X2AP_UESecurityCapabilities *pseccap =
&pinfo->uESecurityCapabilities;
indent++;
/* encryptionAlgorithms */
ossPrint(world, "\n%*s%s: ", indent, "", "encryptionAlgorithms");
printHexBitString(world, pseccap->encryptionAlgorithms.length,
pseccap->encryptionAlgorithms.value, indent);
/* integrityProtectionAlgorithms */
ossPrint(world, "\n%*s%s: ", indent, "", "integrityProtectionAlgorithms");
printHexBitString(world, pseccap->integrityProtectionAlgorithms.length,
pseccap->integrityProtectionAlgorithms.value, indent);
/* iE_Extensions is optional */
if (pseccap->iE_Extensions)
printProtocolExtensions(world, (void *)pseccap->iE_Extensions,
OSS_X2AP_UESecurityCapabilities_ExtIEs_OSET, indent);
indent--;
}
/* aS_SecurityInformation */
ossPrint(world, "\n%*s%s: ", indent, "", "aS_SecurityInformation");
{
OSS_X2AP_AS_SecurityInformation *psecinf =
&pinfo->aS_SecurityInformation;
indent++;
/* key_eNB */
ossPrint(world, "\n%*s%s: ", indent, "", "key_eNB");
printHexBitString(world, psecinf->key_eNodeB_star.length,
psecinf->key_eNodeB_star.value, indent);
/* nextHopChainingCounter */
ossPrint(world, "\n%*s%s: %u", indent, "", "nextHopChainingCounter", psecinf->nextHopChainingCount);
/* iE_Extensions is optional */
if (psecinf->iE_Extensions)
printProtocolExtensions(world, (void *)psecinf->iE_Extensions,
OSS_X2AP_AS_SecurityInformation_ExtIEs_OSET, indent);
indent--;
}
/* uEaggregateMaximumBitRate */
ossPrint(world, "\n%*s%s: ", indent, "",
"uEaggregateMaximumBitRate");
{
OSS_X2AP_UEAggregateMaximumBitRate *prate =
&pinfo->uEaggregateMaximumBitRate;
indent++;
/* uEaggregateMaximumBitRateDownlink */
ossPrint(world, "\n%*s%s: "ULLONG_FMT, indent, "",
"uEaggregateMaximumBitRateDownlink",
prate->uEaggregateMaximumBitRateDownlink);
/* uEaggregateMaximumBitRateUplink */
ossPrint(world, "\n%*s%s: "ULLONG_FMT, indent, "",
"uEaggregateMaximumBitRateUplink",
prate->uEaggregateMaximumBitRateUplink);
ossPrint(world, "\n");
/* iE_Extensions is optional */
if (prate->iE_Extensions)
printProtocolExtensions(world, (void*)prate->iE_Extensions,
OSS_X2AP_UEAggregate_MaximumBitrate_ExtIEs_OSET, indent);
indent--;
}
/* subscriberProfileIDforRFP is optional */
if (pinfo->bit_mask & OSS_X2AP_UE_ContextInformation_subscriberProfileIDforRFP_present)
ossPrint(world, "%*s%s: %u\n", indent, "",
"subscriberProfileIDforRFP",
pinfo->subscriberProfileIDforRFP);
/* e-RABs-ToBeSetup-List */
printE_RABs_ToBeSetup_List(world,
pinfo->e_RABs_ToBeSetup_List, indent);
/* rRC-Context */
ossPrint(world, "%*s%s: ", indent, "", "rRC-Context");
printHexString(world, pinfo->rRC_Context.length,
pinfo->rRC_Context.value, indent);
ossPrint(world, "\n");
/* handoverRestrictionList is optional */
if (pinfo->bit_mask & OSS_X2AP_UE_ContextInformation_handoverRestrictionList_present) {
OSS_X2AP_HandoverRestrictionList *plist =
&pinfo->handoverRestrictionList;
ossPrint(world, "%*s%s: ", indent, "",
"handoverRestrictionList");
indent++;
/* servingPLMN */
ossPrint(world, "\n%*s%s: ", indent, "", "servingPLMN");
printHexString(world, sizeof(plist->servingPLMN),
plist->servingPLMN, indent);
ossPrint(world, "\n");
/* equivalentPLMNs is optional */
if (plist->equivalentPLMNs) {
OSS_X2AP_EPLMNs peplmns = plist->equivalentPLMNs;
size_t i;
ossPrint(world,
"%*s%s includes the following PLMN-Identity elements:\n",
indent++, "", "equivalentPLMNs");
/* Print each list element */
for (i = 1; peplmns; peplmns = peplmns->next, i++) {
ossPrint(world, "%*s#%d: ", indent, "", i);
printHexString(world, sizeof(peplmns->value),
peplmns->value, indent);
ossPrint(world, "\n");
}
indent--;
}
/* forbiddenTAs is optional */
if (plist->forbiddenTAs) {
ossPrint(world,
"%*s%s includes ForbiddenTAs-Item elements:\n",
indent++, "", "forbiddenTAs");
/* Miss printing of ForbiddenTAs-Item data */
ossPrint(world, "%*s...\n", indent--, "");
}
/* forbiddenLAs is optional */
if (plist->forbiddenLAs) {
ossPrint(world,
"%*s%s includes ForbiddenLAs-Item elements:\n",
indent++, "", "forbiddenLAs");
/* Miss printing of ForbiddenLAs-Item data */
ossPrint(world, "%*s...\n", indent--, "");
}
/* forbiddenInterRATs is optional */
if (plist->bit_mask & OSS_X2AP_forbiddenInterRATs_present) {
ossPrint(world, "%*s%s: %u\n",
indent++, "", "forbiddenInterRATs",
plist->forbiddenInterRATs);
}
/* iE_Extensions is optional */
if (plist->iE_Extensions)
printProtocolExtensions(world, (void*)plist->iE_Extensions,
OSS_X2AP_HandoverRestrictionList_ExtIEs_OSET, indent);
indent--;
}
/* locationReportingInformation is optional */
if (pinfo->bit_mask & OSS_X2AP_UE_ContextInformation_locationReportingInformation_present) {
OSS_X2AP_LocationReportingInformation *ploc =
&pinfo->locationReportingInformation;
ossPrint(world, "%*s%s:\n", indent, "",
"locationReportingInformation");
indent++;
/* eventType */
ossPrint(world, "%*s%s: %s\n", indent, "", "eventType",
(ploc->eventType == 0) ?
"change-of-serving-cell" : "unknown");
/* reportArea */
ossPrint(world, "%*s%s: %s\n", indent, "", "reportArea",
(ploc->reportArea == 0) ? "ecgi" : "unknown");
/* iE_Extensions is optional */
if (ploc->iE_Extensions)
printProtocolExtensions(world, (void *)ploc->iE_Extensions,
OSS_X2AP_LocationReportingInformation_ExtIEs_OSET, indent);
indent--;
}
/* iE_Extensions is optional */
if (pinfo->iE_Extensions)
printProtocolExtensions(world, pinfo->iE_Extensions,
OSS_X2AP_UE_ContextInformation_ExtIEs_OSET, indent);
indent--;
/* UE-HistoryInformation IE */
} else if (field->id == OSS_X2AP_id_UE_HistoryInformation) {
printHistoryInformation(world, *value->decoded.pdu_UE_HistoryInformation, indent);
/* TraceActivation IE */
} else if (field->id == OSS_X2AP_id_TraceActivation) {
OSS_X2AP_TraceActivation *ptrace = value->decoded.pdu_TraceActivation;
ossPrint(world, "%*svalue %s : ", indent, "", "TraceActivation");
indent++;
/* eUTRANTraceID */
ossPrint(world, "\n%*s%s: ", indent, "", "eUTRANTraceID");
printHexString(world, sizeof(ptrace->eUTRANTraceID),
ptrace->eUTRANTraceID, indent);
ossPrint(world, "\n");
/* interfacesToTrace */
ossPrint(world, "\n%*s%s: ", indent, "", "interfacesToTrace");
printHexBitString(world, ptrace->interfacesToTrace.length,
ptrace->interfacesToTrace.value, indent);
ossPrint(world, "\n");
/* traceDepth */
ossPrint(world, "\n%*s%s: %u", indent, "", "traceDepth",
ptrace->traceDepth);
ossPrint(world, "\n");
/* traceCollectionEntityIPAddress */
ossPrint(world, "\n%*s%s: ", indent, "", "traceCollectionEntityIPAddress");
printHexBitString(world, ptrace->traceCollectionEntityIPAddress.length,
ptrace->traceCollectionEntityIPAddress.value, indent);
ossPrint(world, "\n");
/* iE_Extensions is optional */
if (ptrace->iE_Extensions)
printProtocolExtensions(world, ptrace->iE_Extensions,
OSS_X2AP_TraceActivation_ExtIEs_OSET, indent);
indent--;
/* CSGMembershipStatus IE has been added in the 10th release */
} else if (field->id == OSS_X2AP_id_CSGMembershipStatus) {
ossPrint(world, "%*svalue %s: %s\n", indent, "", "CSGMembershipStatus",
MemshipStatus[*value->decoded.pdu_CSGMembershipStatus]);
/* MobilityInformation IE has been added in the 11th release */
} else if (field->id == OSS_X2AP_id_MobilityInformation) {
OSS_X2AP_MobilityInformation *pMobilityInfo = value->decoded.pdu_MobilityInformation;
ossPrint(world, "%*s%s: ", indent, "", "MobilityInformation");
printHexBitString(world, pMobilityInfo->length,
pMobilityInfo->value, indent);
ossPrint(world, "\n");
/* Masked-IMEISV IE has been added in the 12th release */
} else if (field->id == OSS_X2AP_id_Masked_IMEISV) {
OSS_X2AP_Masked_IMEISV *pMaskedIMEISV = value->decoded.pdu_Masked_IMEISV;
ossPrint(world, "%*s%s: ", indent, "", "Masked-MEISV");
printHexBitString(world, pMaskedIMEISV->length,
pMaskedIMEISV->value, indent);
ossPrint(world, "\n");
/* other IEs are printed via OSS_X2AP_PrintDecoded() */
} else if (value->pduNum && value->decoded.pdu_UE_X2AP_ID) {
ossPrint(world, "%*s", indent, "");
ossPrintPDU(world, value->pduNum, value->decoded.pdu_UE_X2AP_ID);
} else
ossPrint(world, "PDU is not decoded\n");
indent--;
}
return err;
}
/*
* FUNCTION printHandoverRequestMsg() prints X2AP pdu which contains
* HandoverRequest message. The function is not intended to
* handle other types of messages.
*
* PARAMETERS
* world OSS enviroonment variable
* pdu input X2AP PDU which data should be printed
*
* RETURNS 0 on success, error code on failure
*/
static int printHandoverRequestMsg(OssGlobal *world, OSS_X2AP_PDU *pdu)
{
int msg_id;
OSS_X2AP_X2AP_ELEMENTARY_PROCEDURES_InitiatingMessage *msg_value;
OSS_X2AP_HandoverRequest *hr_value;
ossPrint(world, "\nGet message information\n");
printBorder();
/* Get identifier of message stored in PDU */
if (pdu->choice != OSS_X2AP_initiatingMessage_chosen) {
ossPrint(world, "Unexpected type of message");
return -1;
}
msg_value = &pdu->u.initiatingMessage.value;
msg_id = msg_value->pduNum;
if ( msg_id != OSS_X2AP_HandoverRequest_PDU) {
ossPrint(world, "Unexpected message id = %d\n", msg_id);
return -1;
} else {
hr_value = msg_value->decoded.pdu_HandoverRequest;
}
ossPrint(world, "Message id = %d (HandoverRequest)\n\n", msg_id);
if (!hr_value || !hr_value->protocolIEs) {
ossPrint(world, "Incorrect message \n");
return -1;
}
return printProtocolIEs(world, hr_value->protocolIEs, 0);
}
/*
* FUNCTION createSuccessResponse() creates X2AP successful outcome
* message for given HandoverRequest request.
*
* PARAMETERS
* world OSS environment variable
* req input message (request)
* resp pointer to the successful outcome message (response)
* created by the function
*
* RETURNS 0 on success, error code on failure
*/
static int createSuccessResponse(OssGlobal *world, OSS_X2AP_PDU *req,
OSS_X2AP_PDU **resp)
{
OSS_X2AP_SuccessfulOutcome *outcome_msg;
struct OSS_X2AP_HandoverRequestIEs *reqies;
struct OSS_X2AP_HandoverRequestAcknowledgeIEs *respies;
OSS_X2AP_HandoverRequest *hr_value;
OSS_X2AP_X2AP_ELEMENTARY_PROCEDURES_SuccessfulOutcome *ot_val;
OSS_X2AP_HandoverRequestAcknowledge_IEs_Value *ot_val2;
OSS_X2AP_E_RABs_Admitted_ItemIEs_Value *ot_val3;
struct OSS_X2AP_HandoverRequestAcknowledgeIEsVal *ie_field;
struct OSS_X2AP_AdmittedListVal *sing_cont;
OSS_X2AP_E_RABs_Admitted_Item *pba_item;
OSS_X2AP_E_RABs_Admitted_List pba_list;
if (req->choice != OSS_X2AP_initiatingMessage_chosen) {
ossPrint(world, "Unexpected type of message");
return -1;
}
if (req->u.initiatingMessage.value.pduNum == 0
|| req->u.initiatingMessage.value.encoded.value) {
ossPrint(world, "Unexpected HandoverRequest request data");
return -1;
}
hr_value = req->u.initiatingMessage.value.decoded.pdu_HandoverRequest;
if (NULL == (reqies = hr_value->protocolIEs)) {
ossPrint(world, "No IEs in HandoverRequest request");
return -1;
}
ossPrint(world, "\nCreate response\n");
printBorder();
/*
* Create successful outcome message from initiating message. Copy
* IE with id-Old-eNB-UE-X2AP-ID from input to output.
*/
*resp = ossGetInitializedMemory(world, sizeof(OSS_X2AP_PDU));
if (!*resp)
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_PDU", OUT_MEMORY, NULL);
(*resp)->choice = OSS_X2AP_successfulOutcome_chosen;
outcome_msg = &(*resp)->u.successfulOutcome;
outcome_msg->procedureCode = req->u.initiatingMessage.procedureCode;
outcome_msg->criticality = OSS_X2AP_reject;
ot_val = &outcome_msg->value;
ot_val->pduNum = OSS_X2AP_HandoverRequestAcknowledge_PDU;
ot_val->decoded.pdu_HandoverRequestAcknowledge =
ossGetInitializedMemory(world, sizeof(OSS_X2AP_HandoverRequestAcknowledge));
if (!ot_val->decoded.pdu_HandoverRequestAcknowledge) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_HandoverRequestAcknowledge", OUT_MEMORY, NULL);
}
ot_val->decoded.pdu_HandoverRequestAcknowledge->protocolIEs =
ossGetInitializedMemory(world, sizeof(struct OSS_X2AP_HandoverRequestAcknowledgeIEs));
if (NULL == (respies = ot_val->decoded.pdu_HandoverRequestAcknowledge->protocolIEs)) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_ProtocolIE_Container", OUT_MEMORY, NULL);
}
for (;reqies != NULL; reqies = reqies->next) {
if (reqies->value.id == OSS_X2AP_id_Old_eNB_UE_X2AP_ID) {
respies->next = NULL;
respies->value.criticality = OSS_X2AP_ignore;
respies->value.id = OSS_X2AP_id_Old_eNB_UE_X2AP_ID;
respies->value.value.decoded.pdu_UE_X2AP_ID = ossGetInitializedMemory(world, sizeof(OSS_X2AP_UE_X2AP_ID));
if (!respies->value.value.decoded.pdu_UE_X2AP_ID) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_UE_X2AP_ID", OUT_MEMORY, NULL);
}
respies->value.value.pduNum = OSS_X2AP_PDU_HandoverRequestAcknowledge_IEs_Value_UE_X2AP_ID;
*respies->value.value.decoded.pdu_UE_X2AP_ID = *reqies->value.value.decoded.pdu_UE_X2AP_ID;
}
}
/* Add IE with id-New-eNB-UE-X2AP-ID to outcome message */
respies->next = ossGetInitializedMemory(world, sizeof(struct OSS_X2AP_HandoverRequestAcknowledgeIEs));
if (!respies->next) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_ProtocolIE_Container", OUT_MEMORY, NULL);
}
respies= respies->next;
ie_field = &respies->value;
ie_field->id = OSS_X2AP_id_New_eNB_UE_X2AP_ID;
ie_field->criticality = OSS_X2AP_ignore;
ot_val2 = &ie_field->value;
ot_val2->decoded.pdu_UE_X2AP_ID = ossGetInitializedMemory(world, sizeof(OSS_X2AP_UE_X2AP_ID));
if (!ot_val2->decoded.pdu_UE_X2AP_ID) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_UE_X2AP_ID", OUT_MEMORY, NULL);
}
*ot_val2->decoded.pdu_UE_X2AP_ID = 456;
ot_val2->pduNum = OSS_X2AP_UE_X2AP_ID_PDU;
/* Add E_RABs-Admitted-List IE to outcome message */
respies->next = ossGetInitializedMemory(world, sizeof(struct OSS_X2AP_HandoverRequestAcknowledgeIEs));
if (!respies->next) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_ProtocolIE_Container", OUT_MEMORY, NULL);
}
respies = respies->next;
ie_field = &respies->value;
ie_field->id = OSS_X2AP_id_E_RABs_Admitted_List;
ie_field->criticality = OSS_X2AP_ignore;
ot_val2 = &ie_field->value;
ot_val2->decoded.pdu_E_RABs_Admitted_List =
ossGetInitializedMemory(world, sizeof(OSS_X2AP_E_RABs_Admitted_List));
if (!ot_val2->decoded.pdu_E_RABs_Admitted_List) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_E_RABs_Admitted_List", OUT_MEMORY, NULL);
}
*ot_val2->decoded.pdu_E_RABs_Admitted_List =
ossGetInitializedMemory(world, sizeof(struct OSS_X2AP_E_RABs_Admitted_List_));
if (!(*ot_val2->decoded.pdu_E_RABs_Admitted_List)) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_E_RABs_Admitted_List", OUT_MEMORY, NULL);
}
ot_val2->pduNum = OSS_X2AP_E_RABs_Admitted_List_PDU;
pba_list = *ot_val2->decoded.pdu_E_RABs_Admitted_List;
sing_cont = &pba_list->value;
sing_cont->id = OSS_X2AP_id_E_RABs_Admitted_Item;
sing_cont->criticality = OSS_X2AP_ignore;
ot_val3 = &sing_cont->value;
ot_val3->decoded.pdu_E_RABs_Admitted_Item = ossGetInitializedMemory(world, sizeof(OSS_X2AP_E_RABs_Admitted_Item));
if (!ot_val3->decoded.pdu_E_RABs_Admitted_Item) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_E_RABs_Admitted_Item", OUT_MEMORY, NULL);
}
ot_val3->pduNum = OSS_X2AP_E_RABs_Admitted_Item_PDU;
pba_item = ot_val3->decoded.pdu_E_RABs_Admitted_Item;
pba_item->e_RAB_ID = 999;
{
OSS_X2AP_GTPtunnelEndpoint *value_GTP_TunnelEndpoint =
&pba_item->uL_GTP_TunnelEndpoint;
value_GTP_TunnelEndpoint->transportLayerAddress.value =
copyBits(world, (unsigned char *)"\x55", 7);
value_GTP_TunnelEndpoint->transportLayerAddress.length = 7;
memcpy(&value_GTP_TunnelEndpoint->gTP_TEID, "\x01\x02\x03\x04", 4);
if (!value_GTP_TunnelEndpoint->transportLayerAddress.value) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "copyBits(Octets)", OUT_MEMORY, NULL);
}
pba_item->bit_mask |= OSS_X2AP_uL_GTP_TunnelEndpoint_present;
value_GTP_TunnelEndpoint = &pba_item->dL_GTP_TunnelEndpoint;
value_GTP_TunnelEndpoint->transportLayerAddress.value =
copyBits(world, (unsigned char *)"\x55\x55", 11);
value_GTP_TunnelEndpoint->transportLayerAddress.length = 11;
memcpy(value_GTP_TunnelEndpoint->gTP_TEID, "\x05\x06\x07\x08", 4);
if (!value_GTP_TunnelEndpoint->transportLayerAddress.value) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "copyBits(Octets)", OUT_MEMORY, NULL);
}
pba_item->bit_mask |= OSS_X2AP_dL_GTP_TunnelEndpoint_present;
}
/* Add TargeteNBtoSource-eNBTransparentContainer IE to outcome message */
respies->next = ossGetInitializedMemory(world, sizeof(struct OSS_X2AP_HandoverRequestAcknowledgeIEs));
if (!respies->next) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "ossGetInitializedMemory for OSS_X2AP_ProtocolIE_Container", OUT_MEMORY, NULL);
}
respies= respies->next;
ie_field = &respies->value;
ie_field->id = OSS_X2AP_id_TargeteNBtoSource_eNBTransparentContainer;
ie_field->criticality = OSS_X2AP_ignore;
ot_val2 = &ie_field->value;
ot_val2->decoded.pdu_TargeteNBtoSource_eNBTransparentContainer =
ossGetInitializedMemory(world, sizeof(OSS_X2AP_TargeteNBtoSource_eNBTransparentContainer));
if (!ot_val2->decoded.pdu_TargeteNBtoSource_eNBTransparentContainer) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world,
"ossGetInitializedMemory for OSS_X2AP_TargeteNBtoSource_eNBTransparentContainer", OUT_MEMORY, NULL);
}
ot_val2->pduNum = OSS_X2AP_TargeteNBtoSource_eNBTransparentContainer_PDU;
ot_val2->decoded.pdu_TargeteNBtoSource_eNBTransparentContainer->value =
copyOctets(world, (unsigned char*)"\x55", 1);
ot_val2->decoded.pdu_TargeteNBtoSource_eNBTransparentContainer->length = 1;
if (!ot_val2->decoded.pdu_TargeteNBtoSource_eNBTransparentContainer->value) {
/* Deallocate OSS_X2AP_PDU on error */
ossFreePDU(world, OSS_X2AP_PDU_PDU, *resp);
return reportError(world, "copyOctets", OUT_MEMORY, NULL);
}
return 0;
}
/*
* FUNCTION testX2AP() is used to test X2AP message, the input serialized
* pdu is deserialized and printed, then an outcome message
* is created, printed and encoded.
*
* PARAMETERS
* world OSS environment variable
* filename name of file containing serialized message
*
* RETURNS 0 on success, error code on failure
*/
static int testX2AP(OssGlobal *world, const char *filename)
{
OssBuf encoded_data;
int err, pdu_num = OSS_X2AP_PDU_PDU;
ossPrint(world, "======================================================"
"======================\n");
ossPrint(world, "Read encoding from file: %s\n\n", filename);
/* Read serialized message from file */
err = readEncodingFromFile(world, filename, &encoded_data);
if (!err) {
/*
* Zero deserilaized message pointer in order to allocate it
* in API call
*/
OSS_X2AP_PDU *req = NULL;
ossPrint(world, "Deserialize request\n");
printBorder();
/* Deserialize input message */
err = ossDecode(world, &pdu_num, &encoded_data, (void**)&req);
ossPrint(world, "\n");
/* Print error diagnostics if an error takes place */
if (err) {
reportError(world, "ossDecode()", err, NULL);
} else {
ossPrint(world, "Deserialized request\n");
printBorder();
/* Print deserialized message */
err = ossPrintPDU(world, OSS_X2AP_PDU_PDU, req);
ossPrint(world, "\n");
if (err)
reportError(world, "ossPrintPDU()", err, NULL);
/* Parse and print input message */
else if (0 == (err = printHandoverRequestMsg(world, req))) {
OSS_X2AP_PDU *resp;
/* Create successful outcome message */
err = createSuccessResponse(world, req, &resp);
if (!err) {
OssBuf msg = { 0, NULL };
/* Print outcome message */
err = ossPrintPDU(world, OSS_X2AP_PDU_PDU, resp);
ossPrint(world, "\n");
ossPrint(world, "Serialize response\n");
printBorder();
/* Serialize outcome message */
err = ossEncode(world, OSS_X2AP_PDU_PDU, resp, &msg);
ossPrint(world, "\n");
if (err) {
reportError(world, "OossEncode()", err, NULL);
/* Print serialized outcome message */
} else {
ossPrint(world, "Serialized response (%ld bytes):\n",
msg.length);
printBorder();
printHexString(world, msg.length, msg.value, 0);
ossFreeMemory(world, msg.value);
ossPrint(world, "\n\n");
}
/* Free memory allocated for outcome message */
ossFreePDU(world, OSS_X2AP_PDU_PDU, resp);
}
}
}
/*
* Free memory allocated for input message if it is deserialized
* successfully
*/
if (req)
ossFreePDU(world, OSS_X2AP_PDU_PDU, req);
/* Free memory allocated for reading of input message from file */
ossFreeMemory(world, encoded_data.value);
}
return err;
}
/*
* Deserializes and prints input messages, creates and prints successful
* outcome messages
*/
int main(int argc, char *argv[])
{
OssGlobal world_data, *world = &world_data;
int err = 0;
err = ossinit(world, _oss_x2ap_control_table);
if (err)
return reportError(NULL, "ossinit()", err, NULL);
filesDir = argc > 1 ? argv[1] : ".";
/* Set flags */
ossSetFlags(world, AUTOMATIC_ENCDEC);
/* Set debug mode */
ossSetDebugFlags(world, PRINT_ERROR_MESSAGES | PRINT_DECODING_DETAILS | PRINT_ENCODING_DETAILS);
err = testX2AP(world, "X2AP-PDU_HandoverRequest_bin.per");
/* Free all resources */
ossterm(world);
if (!err)
printf("Testing successful.\n");
return err;
}
This is the expected output when running the sample:
Encoding response message... Encoding successful.
-- Excerpt from x2ap.asn 3GPP TS 36.423 V19.0.0 (2025-09)
-- Elementary Procedure definitions
--
-- **************************************************************
X2AP-PDU-Descriptions {
itu-t (0) identified-organization (4) etsi (0) mobileDomain (0)
eps-Access (21) modules (3) x2ap (2) version1 (1) x2ap-PDU-Descriptions (0) }
DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
-- **************************************************************
--
-- IE parameter types from other modules.
--
-- **************************************************************
IMPORTS
Criticality,
ProcedureCode
FROM X2AP-CommonDataTypes
CellActivationRequest,
CellActivationResponse,
CellActivationFailure,
ENBConfigurationUpdate,
ENBConfigurationUpdateAcknowledge,
ENBConfigurationUpdateFailure,
ErrorIndication,
HandoverCancel,
HandoverReport,
HandoverPreparationFailure,
HandoverRequest,
HandoverRequestAcknowledge,
LoadInformation,
PrivateMessage,
ResetRequest,
ResetResponse,
ResourceStatusFailure,
ResourceStatusRequest,
ResourceStatusResponse,
ResourceStatusUpdate,
RLFIndication,
SNStatusTransfer,
UEContextRelease,
X2SetupFailure,
X2SetupRequest,
X2SetupResponse,
MobilityChangeRequest,
MobilityChangeAcknowledge,
MobilityChangeFailure,
X2Release,
X2APMessageTransfer,
SeNBAdditionRequest,
SeNBAdditionRequestAcknowledge,
SeNBAdditionRequestReject,
SeNBReconfigurationComplete,
SeNBModificationRequest,
SeNBModificationRequestAcknowledge,
SeNBModificationRequestReject,
SeNBModificationRequired,
SeNBModificationConfirm,
SeNBModificationRefuse,
SeNBReleaseRequest,
SeNBReleaseRequired,
SeNBReleaseConfirm,
SeNBCounterCheckRequest,
X2RemovalFailure,
X2RemovalRequest,
X2RemovalResponse,
RetrieveUEContextRequest,
RetrieveUEContextResponse,
RetrieveUEContextFailure,
SgNBAdditionRequest,
SgNBAdditionRequestAcknowledge,
SgNBAdditionRequestReject,
SgNBReconfigurationComplete,
SgNBModificationRequest,
SgNBModificationRequestAcknowledge,
SgNBModificationRequestReject,
SgNBModificationRequired,
SgNBModificationConfirm,
SgNBModificationRefuse,
SgNBReleaseRequest,
SgNBReleaseRequestAcknowledge,
SgNBReleaseRequestReject,
SgNBReleaseRequired,
SgNBReleaseConfirm,
SgNBCounterCheckRequest,
SgNBChangeRequired,
SgNBChangeConfirm,
SgNBChangeRefuse,
RRCTransfer,
ENDCX2SetupRequest,
ENDCX2SetupResponse,
ENDCX2SetupFailure,
ENDCConfigurationUpdate,
ENDCConfigurationUpdateAcknowledge,
ENDCConfigurationUpdateFailure,
SecondaryRATDataUsageReport,
ENDCCellActivationRequest,
ENDCCellActivationResponse,
ENDCCellActivationFailure,
ENDCPartialResetRequired,
ENDCPartialResetConfirm,
EUTRANRCellResourceCoordinationRequest,
EUTRANRCellResourceCoordinationResponse,
SgNBActivityNotification,
ENDCX2RemovalRequest,
ENDCX2RemovalResponse,
ENDCX2RemovalFailure,
DataForwardingAddressIndication,
GNBStatusIndication,
ENDCConfigurationTransfer,
DeactivateTrace,
TraceStart,
HandoverSuccess,
EarlyStatusTransfer,
ConditionalHandoverCancel,
ENDCResourceStatusRequest,
ENDCResourceStatusResponse,
ENDCResourceStatusFailure,
ENDCResourceStatusUpdate,
CellTrafficTrace,
F1CTrafficTransfer,
UERadioCapabilityIDMappingRequest,
UERadioCapabilityIDMappingResponse,
AccessAndMobilityIndication,
CPC-cancel,
RachIndication,
SCGFailureInformationReport,
SCGFailureTransfer
FROM X2AP-PDU-Contents
id-cellActivation,
id-eNBConfigurationUpdate,
id-errorIndication,
id-handoverCancel,
id-handoverReport,
id-handoverPreparation,
id-loadIndication,
id-privateMessage,
id-reset,
id-resourceStatusReporting,
id-resourceStatusReportingInitiation,
id-rLFIndication,
id-snStatusTransfer,
id-uEContextRelease,
id-x2Setup,
id-mobilitySettingsChange,
id-x2Release,
id-x2APMessageTransfer,
id-seNBAdditionPreparation,
id-seNBReconfigurationCompletion,
id-meNBinitiatedSeNBModificationPreparation,
id-seNBinitiatedSeNBModification,
id-meNBinitiatedSeNBRelease,
id-seNBinitiatedSeNBRelease,
id-seNBCounterCheck,
id-x2Removal,
id-retrieveUEContext,
id-sgNBAdditionPreparation,
id-sgNBReconfigurationCompletion,
id-meNBinitiatedSgNBModificationPreparation,
id-sgNBinitiatedSgNBModification,
id-meNBinitiatedSgNBRelease,
id-sgNBinitiatedSgNBRelease,
id-sgNBChange,
id-sgNBCounterCheck,
id-rRCTransfer,
id-endcX2Setup,
id-endcConfigurationUpdate,
id-secondaryRATDataUsageReport,
id-endcCellActivation,
id-endcPartialReset,
id-eUTRANRCellResourceCoordination,
id-SgNBActivityNotification,
id-endcX2Removal,
id-dataForwardingAddressIndication,
id-gNBStatusIndication,
id-endcConfigurationTransfer,
id-deactivateTrace,
id-traceStart,
id-handoverSuccess,
id-earlyStatusTransfer,
id-conditionalHandoverCancel,
id-endcresourceStatusReporting,
id-endcresourceStatusReportingInitiation,
id-cellTrafficTrace,
id-f1CTrafficTransfer,
id-UERadioCapabilityIDMapping,
id-accessAndMobilityIndication,
id-CPC-cancel,
id-rachIndication,
id-scgFailureInformationReport,
id-scgFailureTransfer
FROM X2AP-Constants;
-- **************************************************************
--
-- Interface Elementary Procedure Class
--
-- **************************************************************
X2AP-ELEMENTARY-PROCEDURE ::= CLASS {
&InitiatingMessage ,
&SuccessfulOutcome OPTIONAL,
&UnsuccessfulOutcome OPTIONAL,
&procedureCode ProcedureCode UNIQUE,
&criticality Criticality DEFAULT ignore
}
WITH SYNTAX {
INITIATING MESSAGE &InitiatingMessage
[SUCCESSFUL OUTCOME &SuccessfulOutcome]
[UNSUCCESSFUL OUTCOME &UnsuccessfulOutcome]
PROCEDURE CODE &procedureCode
[CRITICALITY &criticality]
}
-- **************************************************************
The gui/ subdirectory contains an ASN.1 Studio project for this sample. With ASN.1 Studio you can:
This sample is provided solely for illustration purposes, for example to demonstrate usage of the OSS ASN.1 Tools API with 3GPP LTE X2AP messages. It does not represent a complete application. To build and run this sample, you must use a trial or licensed version of the appropriate OSS ASN.1 Tools. The copyright and license statements included in each source file remain fully applicable.
If you have questions about using this sample, contact OSS Nokalva Support.