5G XnAP C Sample Code Using OSS ASN.1 Tools


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 5G XnAP standard (TS 38.423 version 19.0.0).

It runs on Linux on x86-64 as an example and illustrates how to decode, inspect, and create PER-encoded 5G XnAP messages using the OSS ASN.1 Tools API.

The sample reads XnAP messages from .per files, decodes and prints them, and creates and encodes a response message.

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, XnAP), visit the main Sample Code page.

Overview

This sample shows how to:

  • Initialize the OSS ASN.1/C runtime for the 5G XnAP schema.
  • Read .per files that contain valid PER-encoded 5G XnAP messages.
  • Decode and print 5G XnAP messages.
  • Create and encode a response message.
  • Build and run the sample test program with a makefile on Linux.

Files in This Sample

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 5G XnAP protocol (TS 38.423) used with this program example.
XNAP-PDU_HandoverRequest.per Valid PER-encoded XnAP message. It should pass testing.
txnap.c Simple C program that shows how to work with 5G XnAP protocol data. It reads input messages from files, decodes and prints them, and creates and encodes a response message.
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.

Build and Run Instructions

(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.

1. Generate C source and header files (runtime-only shipments)

If your shipment is runtime-only, generate the .c and .h files by running:

make cross

This creates a samples/cross directory with:

  • the required .asn files
  • scripts to run the OSS ASN.1/C compiler (asn1cpl.sh or asn1cpl.bat)
2. Build and run the sample
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
3. Clean up generated files
make clean
4. Other Platforms

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.

Code Listing: txnap.c

The following listing shows the main C source file for this sample test program, txnap.c. It demonstrates how to read PER-encoded 5G XnAP messages from files, decode and print them, and create and encode a response message using the OSS ASN.1 Tools API.

Show txnap.c source code
/*****************************************************************************/
/*  Copyright (C) ###RELEASE_YEAR### 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 XNAP protocol
 */

#include <errno.h>
#include <string.h>
#include "xnap_r19.h"

/* Names of Criticality values */
static const char *Criticalities[] = { "reject", "ignore", "notify" };

/* 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 XNAP
 * 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     printUEContextInfoHORequestExtensions() prints UEContextInfoHORequestExtensions
 *              data. ossPrintPDU() is called to print PDU stored in
 *              an OpenType value.
 *
 * PARAMETERS
 *    world     OSS environment variable
 *    ie_ext    pointer to OSS_XNAP_UECtxInfoHORequestIEs_ data to print
 *    indent    indentation
 *
 * RETURNS      0 on success, error code on failure
 */
static int printUEContextInfoHORequestExtensions(OssGlobal *world, struct OSS_XNAP_UECtxInfoHORequestIEs *ie_ext,
				int indent)
{
    size_t      i;

    ossPrint(world, "\n%*s%s includes the following IEs:\n", indent++, "",
				"iE-Extensions");
    /* Print each IE in the list */
    for (i = 1; ie_ext; ie_ext = ie_ext->next, i++) {
	OSS_XNAP_UEContextInfoHORequest_ExtIEs_Extension *extval = &ie_ext->value.extensionValue;

	ossPrint(world, "%*s#%d: id = %2u, criticality = %s\n", indent, "",
		i, ie_ext->value.id, Criticalities[ie_ext->value.criticality]);
	if (extval->pduNum && extval->decoded.other.pdu_XnAP_PDU)
	    ossPrintPDU(world, extval->pduNum, extval->decoded.other.pdu_XnAP_PDU);
	else
	    ossPrint(world, "PDU is not decoded\n");
    }
    return 0;
}

/*
 * FUNCTION     printProtocolExtensions() prints ProtocolExtensionContainer
 *              data. ossPrintPDU() is called to print PDU stored in
 *              an OpenType value.
 *
 * PARAMETERS
 *    world     OSS environment variable
 *    ie_ext    pointer to ProtocolExtensionContainer data to print
 *    indent    indentation
 *
 * RETURNS      0 on success, error code on failure
 */
static int printProtocolExtensions(OssGlobal *world, struct OSS_XNAP_ProtocolExtensionContainer_ *ie_ext,
				int indent)
{
    size_t      i;

    ossPrint(world, "\n%*s%s includes the following IEs:\n", indent++, "",
				"iE-Extensions");
    /* Print each IE in the list */
    for (i = 1; ie_ext; ie_ext = ie_ext->next, i++) {
	OSS_XNAP_UESecurityCapabilities_ExtIEs_Extension *extval = &ie_ext->value.extensionValue;

	ossPrint(world, "%*s#%d: id = %2u, criticality = %s\n", indent, "",
		i, ie_ext->value.id, Criticalities[ie_ext->value.criticality]);
	if (extval->pduNum && extval->decoded.other.pdu_XnAP_PDU)
	    ossPrintPDU(world, extval->pduNum, extval->decoded.other.pdu_XnAP_PDU);
	else
	    ossPrint(world, "PDU is not decoded\n");
    }
    return 0;
}

/*
 * FUNCTION     printProtocolExtensionsGTPtunnelTransportLayer() prints ProtocolExtensionContainer
 *              data. ossPrintPDU() is called to print PDU stored in
 *              an OpenType value.
 *
 * PARAMETERS
 *    world     OSS environment variable
 *    ie_ext    pointer to ProtocolExtensionContainer data to print
 *    indent    indentation
 *
 * RETURNS      0 on success, error code on failure
 */
static int printProtocolExtensionsGTPtunnelTransportLayer(OssGlobal* world, struct OSS_XNAP_GTPtunnelTransportLayerContainer* ie_ext,
	int indent)
{
	size_t      i;

	ossPrint(world, "\n%*s%s includes the following IEs:\n", indent++, "",
		"iE-Extensions");
	/* Print each IE in the list */
	for (i = 1; ie_ext; ie_ext = ie_ext->next, i++) {
		OSS_XNAP_GTPtunnelTransportLayerInformation_ExtIEs_Extension* extval = &ie_ext->value.extensionValue;

		ossPrint(world, "%*s#%d: id = %2u, criticality = %s\n", indent, "",
			i, ie_ext->value.id, Criticalities[ie_ext->value.criticality]);
		if (extval->pduNum && extval->decoded.other.pdu_XnAP_PDU)
			ossPrintPDU(world, extval->pduNum, extval->decoded.other.pdu_XnAP_PDU);
		else
			ossPrint(world, "PDU is not decoded\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 XNAP 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_XNAP_HRequestIEs *ies, int indent)
{
    int err = 0;
    int      i, j, k;

    ossPrint(world, "%*s%s includes the following IEs:\n", indent++, "",
					"protocolIEs");
    /* Print each IE */
    for (i = 1; ies; ies = ies->next, i++) {
	struct OSS_XNAP_HRequestIE *field = &ies->value;

	ossPrint(world, "\n%*s#%d: id = %2u, criticality = %s\n", indent, "",
		    i, field->id, Criticalities[field->criticality]);

	++indent;

	if (field->id == OSS_XNAP_id_sourceNG_RANnodeUEXnAPID) {
	    ossPrint(world, "%*svalue %s: %u", indent, "", "NG-RANnodeUEXnAPID",
					*field->value.decoded.pdu_NG_RANnodeUEXnAPID);
	} else if (field->id == OSS_XNAP_id_Cause) {
	    OSS_XNAP_Cause  *pcause = field->value.decoded.pdu_Cause;

	    /*
	     * Named values can be printed below instead of numbers for all
	     * Cause components
	     */
	    ossPrint(world, "%*svalue %s: ", indent, "", "Cause");
	    switch (pcause->choice) {
	    case OSS_XNAP_radioNetwork_chosen:
		ossPrint(world, "%s : %d", "radioNetwork",
						pcause->u.radioNetwork);
		break;
	    case OSS_XNAP_transport_chosen:
		ossPrint(world, "%s : %d", "transport", pcause->u.transport);
		break;
	    case OSS_XNAP_protocol_chosen:
		ossPrint(world, "%s : %d", "protocol", pcause->u.protocol);
		break;
	    case OSS_XNAP_misc_chosen:
		ossPrint(world, "%s : %d", "misc", pcause->u.misc);
		break;
	    case OSS_XNAP_Cause_choice_extension_chosen:
		ossPrint(world, "choice-extension: -- Not implemented --");
		break;
	    }
	} else if (field->id == OSS_XNAP_id_targetCellGlobalID) {
	    OSS_XNAP_Target_CGI *tvalue = field->value.decoded.pdu_Target_CGI;

	    ossPrint(world, "%*svalue %s: ", indent, "", "Target-CGI");
	    switch (tvalue->choice) {
	    case OSS_XNAP_Target_CGI_nr_chosen: {
		    OSS_XNAP_NR_CGI *nr = &tvalue->u.nr;

		    ossPrint(world, "%s:\n", "nr");
		    ++indent;
		    ossPrint(world, "%*s%s: ", indent, "", "plmn-id");
		    printHexString(world, sizeof(nr->plmn_id),
				    nr->plmn_id, indent);
		    ossPrint(world, "\n%*s%s: ", indent, "", "nr-CI");
		    printHexBitString(world, nr->nr_CI.length,\
				    nr->nr_CI.value, indent);
		    if (nr->bit_mask & OSS_XNAP_NR_CGI_iE_Extension_present) {
			;
		    }
		    --indent;
		}
		break;
	    case OSS_XNAP_Target_CGI_e_utra_chosen: {
		     OSS_XNAP_E_UTRA_CGI *e_utra = &tvalue->u.e_utra;

		    ossPrint(world, "%s: \n", indent, "", "e-utra");
		    ++indent;
		    ossPrint(world, "%*s%s: ", indent, "", "plmn-id");
		    printHexString(world, sizeof(e_utra->plmn_id),
				    e_utra->plmn_id, indent);
		    ossPrint(world, "\n%*s%s: ", indent, "", "e-utra-CI");
		    printHexBitString(world, e_utra->e_utra_CI.length,\
				    e_utra->e_utra_CI.value, indent);
		    if (e_utra->bit_mask & OSS_XNAP_E_UTRA_CGI_iE_Extension_present) {
			ossPrint(world, "\n%*s%s: ", indent, "", "iE-Extension");
			printProtocolExtensions(world, e_utra->iE_Extension, indent);
		    }
		    --indent;
		}
		break;
	    case OSS_XNAP_Target_CGI_choice_extension_chosen: {
		    ossPrint(world, "choice-extension: -- Not implemented --");
		}
		break;
	    }
	} else if (field->id == OSS_XNAP_id_GUAMI) {
	    OSS_XNAP_GUAMI *gvalue = field->value.decoded.pdu_GUAMI;

	    ossPrint(world, "%*svalue %s: ", indent, "", "GUAMI");
	    ++indent;
	    ossPrint(world, "%*s%s: ", indent, "", "plmn-ID");
	    printHexString(world, sizeof(gvalue->plmn_ID),
				    gvalue->plmn_ID, indent);
	    ossPrint(world, "\n%*s%s: ", indent, "", "amf-region-if");
	    printHexBitString(world, gvalue->amf_region_id.length,\
				    gvalue->amf_region_id.value, indent);
	    ossPrint(world, "\n%*s%s: ", indent, "", "amf-set-id");
	    printHexBitString(world, gvalue->amf_set_id.length,\
				    gvalue->amf_set_id.value, indent);
	    ossPrint(world, "\n%*s%s: ", indent, "", "amf-pointer");
	    printHexBitString(world, gvalue->amf_pointer.length,\
				    gvalue->amf_pointer.value, indent);
	    if (gvalue->bit_mask & OSS_XNAP_GUAMI_iE_Extensions_present) {
		ossPrint(world, "\n%*s%s: ", indent, "", "iE-Extensions");
		printProtocolExtensions(world, gvalue->iE_Extensions, indent);
	    }
	    --indent;
	} else if (field->id == OSS_XNAP_id_UEContextInfoHORequest) {
	    OSS_XNAP_UEContextInfoHORequest *rvalue = field->value.decoded.pdu_UEContextInfoHORequest;
	    OSS_XNAP_CPTransportLayerInformation *el;
	    OSS_XNAP_UESecurityCapabilities *secc;
	    struct OSS_XNAP_PDUSessionResourcesToBeSetup_List_ *list;
	    struct OSS_XNAP_QoSFlowsToBeSetup_List_ *qos_list;
	    ossPrint(world, "%*svalue %s: ", indent, "", "UEContextInfoHORequest");
	    ++indent;
	    ossPrint(world, "\n%*s%s: " LLONG_FMT, indent, "", "ng-c-UE-reference",
		    rvalue->ng_c_UE_reference);
	    ossPrint(world, "\n%*s%s: ", indent, "", "cp-TNL-info-source");
	    el = &rvalue->cp_TNL_info_source;
	    switch (el->choice) {
	    case OSS_XNAP_endpointIPAddress_chosen:
		ossPrint(world, "%s : ", "endpointIPAddress");
		printHexBitString(world, el->u.endpointIPAddress.length,\
				    el->u.endpointIPAddress.value, indent);
		break;
	    case OSS_XNAP_CPTransportLayerInformation_choice_extension_chosen:
		ossPrint(world, "choice-extension: -- Not implemented --");
		break;
	    }
	    ossPrint(world, "\n%*s%s: ", indent, "", "ueSecurityCapabilities");
	    ++indent;
	    secc = &rvalue->ueSecurityCapabilities;
	    ossPrint(world, "\n%*s%s: ", indent, "", "nr-EncyptionAlgorithms");
	    printHexBitString(world, secc->nr_EncyptionAlgorithms.length,\
				    secc->nr_EncyptionAlgorithms.value, indent);

	    ossPrint(world, "\n%*s%s: ", indent, "", "nr-IntegrityProtectionAlgorithms");
	    printHexBitString(world, secc->nr_IntegrityProtectionAlgorithms.length,\
				    secc->nr_IntegrityProtectionAlgorithms.value, indent);

	    ossPrint(world, "\n%*s%s: ", indent, "", "e-utra-EncyptionAlgorithms");
	    printHexBitString(world, secc->e_utra_EncyptionAlgorithms.length,\
				    secc->e_utra_EncyptionAlgorithms.value, indent);

	    ossPrint(world, "\n%*s%s: ", indent, "", "e-utra-IntegrityProtectionAlgorithms");
	    printHexBitString(world, secc->e_utra_IntegrityProtectionAlgorithms.length,\
				    secc->e_utra_IntegrityProtectionAlgorithms.value, indent);
	    if (secc->bit_mask
		    & OSS_XNAP_UESecurityCapabilities_iE_Extension_present) {
		ossPrint(world, "\n%*s%s: ", indent, "", "iE-Extension");
		printProtocolExtensions(world, secc->iE_Extension, indent);
	    }
	    --indent;
	    ossPrint(world, "\n%*s%s: ", indent, "", "securityInformation");
	    ++indent;
	    ossPrint(world, "\n%*s%s: ", indent, "", "key-NG-RAN-Star");
	    printHexBitString(world, rvalue->securityInformation.key_NG_RAN_Star.length,\
				    rvalue->securityInformation.key_NG_RAN_Star.value, indent);
	    ossPrint(world, "\n%*s%s: %d", indent, "", "ncc",
			rvalue->securityInformation.ncc);
	    if (rvalue->securityInformation.bit_mask
		    & OSS_XNAP_AS_SecurityInformation_iE_Extensions_present) {
		ossPrint(world, "\n%*s%s: ", indent, "", "iE-Extensions");
		printProtocolExtensions(world, rvalue->securityInformation.iE_Extensions, indent);
	    }
	    --indent;
	    if (rvalue->bit_mask
		    & OSS_XNAP_UEContextInfoHORequest_indexToRatFrequencySelectionPriority_present) {
		ossPrint(world, "\n%*s%s: %d", indent, "", "indexToRatFrequencySelectionPriority",
			rvalue->indexToRatFrequencySelectionPriority);
	    }
	    ossPrint(world, "\n%*s%s: ", indent, "", "ue-AMBR");
	    ++indent;
	    ossPrint(world, "\n%*s%s: "LLONG_FMT, indent, "", "dl-UE-AMBR",
		    rvalue->ue_AMBR.dl_UE_AMBR);
	    ossPrint(world, "\n%*s%s: "LLONG_FMT, indent, "", "ul-UE-AMBR",
		    rvalue->ue_AMBR.ul_UE_AMBR);
	    if (rvalue->ue_AMBR.bit_mask
		    & OSS_XNAP_UEAggregateMaximumBitRate_iE_Extension_present) {
		ossPrint(world, "\n%*s%s: ", indent, "", "iE_Extension");
		printProtocolExtensions(world, rvalue->ue_AMBR.iE_Extension, indent);
	    }
	    --indent;
	    list = rvalue->pduSessionResourcesToBeSetup_List;
	    ossPrint(world, "\n%*s%s: ", indent, "", "pduSessionResourcesToBeSetup-List");
	    ++indent;
	    for (j = 1; list; j++, list = list->next) {
		ossPrint(world, "\n%*s#%d:", indent, "", j);
		++indent;
		ossPrint(world, "\n%*s%s: %hu", indent, "", "pduSessionId",
				    list->value.pduSessionId);
		ossPrint(world, "\n%*s%s: ", indent, "", "s-NSSAI");
		++indent;
		ossPrint(world, "\n%*s%s: ", indent, "", "sst");
		printHexString(world, sizeof(list->value.s_NSSAI.sst),
				    list->value.s_NSSAI.sst, indent);
		if (list->value.s_NSSAI.bit_mask & OSS_XNAP_sd_present) {
		    ossPrint(world, "\n%*s%s: ", indent, "", "sd");
		    printHexString(world, sizeof(list->value.s_NSSAI.sd),
				    list->value.s_NSSAI.sd, indent);
		}
		if (list->value.s_NSSAI.bit_mask &OSS_XNAP_S_NSSAI_iE_Extensions_present) {
		    ossPrint(world, "\n%*s%s: ", indent, "", "iE-Extensions");
		    printProtocolExtensions(world, list->value.s_NSSAI.iE_Extensions, indent);
		}
		--indent;
		ossPrint(world, "\n%*s%s: ", indent, "", "pduSessionAMBR");
		++indent;
		ossPrint(world, "\n%*s%s: "LLONG_FMT, indent, "", "downlink-session-AMBR",
				list->value.pduSessionAMBR.downlink_session_AMBR);
		ossPrint(world, "\n%*s%s: "LLONG_FMT, indent, "", "uplink-session-AMBR",
				list->value.pduSessionAMBR.uplink_session_AMBR);
		if (list->value.pduSessionAMBR.bit_mask
			& OSS_XNAP_PDUSessionAggregateMaximumBitRate_iE_Extensions_present) {
		    ossPrint(world, "\n%*s%s: ", indent, "", "iE-Extensions");
		    printProtocolExtensions(world, list->value.pduSessionAMBR.iE_Extensions, indent);
		}
		--indent;
		ossPrint(world, "\n%*s%s: ", indent, "", "uL-NG-U-TNLatUPF");
		++indent;
		switch (list->value.uL_NG_U_TNLatUPF.choice) {
		    case OSS_XNAP_gtpTunnel_chosen:
			ossPrint(world, "%s: ", "gtpTunnel");
			++indent;
			ossPrint(world, "\n%*s%s: ", indent, "", "tnl-address");
			printHexBitString(world, list->value.uL_NG_U_TNLatUPF.u.gtpTunnel.tnl_address.length,\
				    list->value.uL_NG_U_TNLatUPF.u.gtpTunnel.tnl_address.value, indent);
			ossPrint(world, "\n%*s%s: ", indent, "", "gtp-teid");
			printHexString(world, sizeof(list->value.uL_NG_U_TNLatUPF.u.gtpTunnel.gtp_teid),
				list->value.uL_NG_U_TNLatUPF.u.gtpTunnel.gtp_teid, indent);
			if (list->value.uL_NG_U_TNLatUPF.u.gtpTunnel.bit_mask
				& OSS_XNAP_GTPtunnelTransportLayerInformation_iE_Extensions_present) {
			    ossPrint(world, "\n%*s%s: ", indent, "", "iE-Extensions");
				printProtocolExtensionsGTPtunnelTransportLayer(world,
			    list->value.uL_NG_U_TNLatUPF.u.gtpTunnel.iE_Extensions, indent);
			}
			--indent;
			break;
		    case OSS_XNAP_UPTransportLayerInformation_choice_extension_chosen:
			ossPrint(world, "choice-extension: -- Not implemented --");
			break;
		}
		--indent;
		if (list->value.bit_mask
			& OSS_XNAP_PDUSessionResourcesToBeSetup_Item_securityIndication_present) {
		    ossPrint(world, "\n%*s%s", indent, "", "securityIndication: -- Not implemented --");
		}
		ossPrint(world, "\n%*s%s: %d", indent, "", "pduSessionType",
				list->value.pduSessionType);
		ossPrint(world, "\n%*s%s:", indent, "", "qosFlowsToBeSetup-List");
		qos_list = list->value.qosFlowsToBeSetup_List;
		++indent;
		for (k = 1; qos_list; k++, qos_list = qos_list->next) {
		    ossPrint(world, "\n%*s#%d:", indent, "", k);
		    ++indent;
		    ossPrint(world, "\n%*s%s: %d", indent, "", "qfi",
				    qos_list->value.qfi);
		    ossPrint(world, "\n%*s%s", indent, "", "qosFlowLevelQoSParameters: -- Not implemented --");
		    --indent;
		}
		--indent;
	    }
	    --indent;
	    --indent;
	    ossPrint(world, "\n%*s%s: ", indent, "", "rrc-Context");
	    printHexString(world, rvalue->rrc_Context.length,
				    rvalue->rrc_Context.value, indent);
	    if (rvalue->bit_mask
		    & OSS_XNAP_locationReportingInformation_present) {
		ossPrint(world, "\n%*s%s", indent, "", "locationReportingInformation: -- Not implemented --");
	    }
	    if (rvalue->bit_mask
		    & OSS_XNAP_mrl_present) {
		ossPrint(world, "\n%*s%s: ", indent, "", "mrl: -- Not implemented --");
	    }
	    if (rvalue->bit_mask
		    & OSS_XNAP_UEContextInfoHORequest_iE_Extensions_present) {
		ossPrint(world, "\n%*s%s: ", indent, "", "iE-Extensions");
		printUEContextInfoHORequestExtensions(world, rvalue->iE_Extensions, indent);
	    }
	    --indent;
	} else if (field->id == OSS_XNAP_id_TraceActivation) {
	    OSS_XNAP_TraceActivation *tvalue = field->value.decoded.pdu_TraceActivation;

	    ossPrint(world, "%*svalue %s:", indent, "", "TraceActivation");
	    ++indent;
	    ossPrint(world, "\n%*s%s: ", indent, "", "ng-ran-TraceID");
	    printHexString(world, sizeof(tvalue->ng_ran_TraceID),\
				    tvalue->ng_ran_TraceID, indent);
	    ossPrint(world, "\n%*s%s: ", indent, "", "interfaces-to-trace");
	    printHexBitString(world, tvalue->interfaces_to_trace.length,\
				    tvalue->interfaces_to_trace.value, indent);
	    ossPrint(world, "\n%*s%s: %d", indent, "", "trace-depth",
			tvalue->trace_depth);
	    ossPrint(world, "\n%*s%s: ", indent, "", "trace-coll-address");
	    printHexBitString(world, tvalue->trace_coll_address.length,\
				    tvalue->trace_coll_address.value, indent);
	    if (tvalue->bit_mask & OSS_XNAP_TraceActivation_ie_Extension_present) {
		ossPrint(world, "\n%*s%s: ", indent, "", "ie-Extension");
		printProtocolExtensions(world, (struct OSS_XNAP_ProtocolExtensionContainer_ *)tvalue->ie_Extension, indent);
	    }
	    --indent;
	} else if (field->id == OSS_XNAP_id_MaskedIMEISV) {
	    OSS_XNAP_MaskedIMEISV *mvalue = field->value.decoded.pdu_MaskedIMEISV;

	    ossPrint(world, "%*svalue %s:\n", indent, "", "MaskedIMEISV");
	    ++indent;
	    printHexBitString(world, mvalue->length,\
				    mvalue->value, indent);
	    --indent;
	} else if (field->id == OSS_XNAP_id_UEHistoryInformation) {
	    OSS_XNAP_UEHistoryInformation uvalue = *field->value.decoded.pdu_UEHistoryInformation;
	    int i;

	    ossPrint(world, "%*svalue %s:\n", indent, "", "UEHistoryInformation");
	    ++indent;
	    for (i = 1; uvalue; uvalue = uvalue->next, i++) {
		switch (uvalue->value.choice) {
		case OSS_XNAP_LastVisitedCell_Item_nG_RAN_Cell_chosen:
		    ossPrint(world, "%*s#%d %s: ", indent, "", i, "nG-RAN-Cell");
		    printHexString(world, uvalue->value.u.nG_RAN_Cell.length,\
				    uvalue->value.u.nG_RAN_Cell.value, indent);
		    break;
		case OSS_XNAP_e_UTRAN_Cell_chosen:
		    ossPrint(world, "%*s#%d %s: ", indent, "", i, "e-UTRAN-Cell");
		    printHexString(world, uvalue->value.u.e_UTRAN_Cell.length,\
				    uvalue->value.u.e_UTRAN_Cell.value, indent);
		    break;
		case OSS_XNAP_uTRAN_Cell_chosen:
		    ossPrint(world, "%*s#%d %s: ", indent, "", i, "uTRAN-Cell");
		    printHexString(world, uvalue->value.u.uTRAN_Cell.length,\
				    uvalue->value.u.uTRAN_Cell.value, indent);
		    break;
		case OSS_XNAP_gERAN_Cell_chosen:
		    ossPrint(world, "%*s#%d %s: ", indent, "", i, "gERAN-Cell");
		    printHexString(world, uvalue->value.u.gERAN_Cell.length,\
				    uvalue->value.u.gERAN_Cell.value, indent);
		    break;
		case OSS_XNAP_LastVisitedCell_Item_choice_extension_chosen:
		    ossPrint(world, "%*s#%d %s", indent, "", i, "choice-extension: -- Not implemented --");
		    break;
		}
		ossPrint(world, "\n");
	    }
	    --indent;
	} else if (field->id == OSS_XNAP_id_UEContextRefAtSN_HORequest) {
	    OSS_XNAP_UEContextRefAtSN_HORequest *uvalue = field->value.decoded.pdu_UEContextRefAtSN_HORequest;
	    OSS_XNAP_GlobalNG_RANNode_ID *el;

	    ossPrint(world, "%*svalue %s: ", indent, "", "UEContextRefAtSN-HORequest");
	    ++indent;
	    ossPrint(world, "\n%*s%s: ", indent, "", "globalNG-RANNode-ID");
	    el = &uvalue->globalNG_RANNode_ID;
	    switch (el->choice) {
	    case OSS_XNAP_GlobalNG_RANNode_ID_gNB_chosen:
		ossPrint(world, "gNB: -- Not implemented --");
		break;
	    case OSS_XNAP_GlobalNG_RANNode_ID_ng_eNB_chosen:
		ossPrint(world, "ng-eNB: -- Not implemented --");
		break;
	    case OSS_XNAP_GlobalNG_RANNode_ID_choice_extension_chosen:
		ossPrint(world, "choice-extension: -- Not implemented --");
		break;
	    }
	    ossPrint(world, "\n%*s%s: %d", indent, "", "sN-NG-RANnodeUEXnAPID",
			uvalue->sN_NG_RANnodeUEXnAPID);
	    if (uvalue->bit_mask
		    & OSS_XNAP_UEContextRefAtSN_HORequest_iE_Extensions_present) {
		ossPrint(world, "\n%*s%s: ", indent, "", "iE-Extensions");
		printProtocolExtensions(world, uvalue->iE_Extensions, indent);
	    }
	    --indent;
	}
	ossPrint(world, "\n");
	--indent;
    }
    return err;
}

/*
 * FUNCTION     printHandoverRequestMsg() prints XNAP pdu which contains
 *              HandoverRequest message.  The function is not intended to
 *              handle other types of messages.
 *
 * PARAMETERS
 *    world     OSS enviroonment variable
 *    pdu       input XNAP PDU which data should be printed
 *
 * RETURNS      0 on success, error code on failure
 */
static int printHandoverRequestMsg(OssGlobal *world, OSS_XNAP_PDU *pdu)
{
    int         msg_id;
    OSS_XNAP_XNAP_ELEMENTARY_PROCEDURES_InitiatingMessage  *msg_value;
    OSS_XNAP_HandoverRequest	*hr_value;

    /* Get identifier of message stored in PDU */
    if (pdu->choice != OSS_XNAP_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_XNAP_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 XNAP 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_XNAP_PDU *req,
							    OSS_XNAP_PDU **resp)
{
    OSS_XNAP_HandoverRequest  *hr_value;
    OSS_XNAP_SuccessfulOutcome *outcome_msg;
    OSS_XNAP_XNAP_ELEMENTARY_PROCEDURES_SuccessfulOutcome *ot_val;
    OSS_XNAP_HandoverRequestAcknowledge_IEs_Value         *ot_val2;
    struct OSS_XNAP_HRequestIEs *reqies;
    struct OSS_XNAP_HAcknowledgeIEs *respies;
    struct OSS_XNAP_HAcknowledgeIE *ie_field;
    OSS_XNAP_PDUSessionResourcesAdmitted_List a_list;
    OSS_XNAP_PDUSessionResourcesAdmitted_Item *a_list_item;

    if (req->choice != OSS_XNAP_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;
    }

    /*
     * Create successful outcome message from initiating message. Copy
     * some IEs from input to output.
     */
    *resp = ossGetInitializedMemory(world, sizeof(OSS_XNAP_PDU));
    if (!*resp)
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_PDU",
			OUT_MEMORY, NULL);

    (*resp)->choice = OSS_XNAP_successfulOutcome_chosen;

    outcome_msg = &(*resp)->u.successfulOutcome;
    outcome_msg->procedureCode = req->u.initiatingMessage.procedureCode;
    outcome_msg->criticality = OSS_XNAP_reject;
    ot_val = &outcome_msg->value;
    ot_val->pduNum = OSS_XNAP_HandoverRequestAcknowledge_PDU;
    ot_val->decoded.pdu_HandoverRequestAcknowledge =
	ossGetInitializedMemory(world, sizeof(OSS_XNAP_HandoverRequestAcknowledge));
    if (!ot_val->decoded.pdu_HandoverRequestAcknowledge) {
	/* Deallocate OSS_XNAP_PDU on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_HandoverRequestAcknowledge",
			OUT_MEMORY, NULL);
    }
    ot_val->decoded.pdu_HandoverRequestAcknowledge->protocolIEs =
			ossGetInitializedMemory(world, sizeof(struct OSS_XNAP_HAcknowledgeIEs));
    if (NULL == (respies = ot_val->decoded.pdu_HandoverRequestAcknowledge->protocolIEs)) {
	/* Deallocate OSS_XNAP_PDU on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_ProtocolIE_Container",
			OUT_MEMORY, NULL);
    }

    for (; reqies; reqies = reqies->next) {
	if (reqies->value.id == OSS_XNAP_id_sourceNG_RANnodeUEXnAPID) {
	    respies->next = NULL;
	    respies->value.criticality = OSS_XNAP_ignore;
	    respies->value.id = reqies->value.id;
	    respies->value.value.decoded.pdu_NG_RANnodeUEXnAPID = ossGetInitializedMemory(world, sizeof(OSS_XNAP_NG_RANnodeUEXnAPID));
	    if (!respies->value.value.decoded.pdu_NG_RANnodeUEXnAPID) {
		/* Deallocate OSS_XNAP_PDU on error */
		ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
		return reportError(world, "ossGetInitializedMemory for OSS_XNAP_NG_RANnodeUEXnAPID)", OUT_MEMORY, NULL);
	    }
	    respies->value.value.pduNum = OSS_XNAP_PDU_HandoverRequestAcknowledge_IEs_Value_NG_RANnodeUEXnAPID;
	    *respies->value.value.decoded.pdu_NG_RANnodeUEXnAPID = *reqies->value.value.decoded.pdu_NG_RANnodeUEXnAPID;
	}
    }

    /* Add IE with OSS_XNAP_id_targetNG_RANnodeUEXnAPID to outcome message */
    respies->next = ossGetInitializedMemory(world, sizeof(struct OSS_XNAP_HAcknowledgeIEs));
    if (!respies->next) {
	/* Deallocate OSS_XNAP_PDU on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_ProtocolIE_Container", OUT_MEMORY, NULL);
    }

    respies= respies->next;
    ie_field = &respies->value;
    ie_field->id = OSS_XNAP_id_targetNG_RANnodeUEXnAPID;
    ie_field->criticality = OSS_XNAP_ignore;
    ot_val2 = &ie_field->value;

    ot_val2->decoded.pdu_NG_RANnodeUEXnAPID = ossGetInitializedMemory(world, sizeof(OSS_XNAP_NG_RANnodeUEXnAPID));
    if (!ot_val2->decoded.pdu_NG_RANnodeUEXnAPID) {
	/* Deallocate OSS_XNAP_PDU on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_NG_RANnodeUEXnAPID", OUT_MEMORY, NULL);
    }
    *ot_val2->decoded.pdu_NG_RANnodeUEXnAPID = 456;
    ot_val2->pduNum = OSS_XNAP_PDU_HandoverRequestAcknowledge_IEs_Value_NG_RANnodeUEXnAPID;

    /* Add OSS_XNAP_PDUSessionResourcesAdmitted_List IE to outcome message */
    respies->next = ossGetInitializedMemory(world, sizeof(struct OSS_XNAP_HAcknowledgeIEs));
    if (!respies->next) {
	/* Deallocate OSS_XNAP_PDU on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_ProtocolIE_Container", OUT_MEMORY, NULL);
    }
    respies = respies->next;
    ie_field = &respies->value;
    ie_field->id = OSS_XNAP_id_PDUSessionResourcesAdmitted_List;
    ie_field->criticality = OSS_XNAP_ignore;
    ot_val2 = &ie_field->value;
    ot_val2->decoded.pdu_PDUSessionResourcesAdmitted_List =
	ossGetInitializedMemory(world, sizeof(OSS_XNAP_PDUSessionResourcesAdmitted_List));
    if (!ot_val2->decoded.pdu_PDUSessionResourcesAdmitted_List) {
	/* Deallocate OSS_XNAP_PDU  on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_PDUSessionResourcesAdmitted_List", OUT_MEMORY, NULL);
    }
    *ot_val2->decoded.pdu_PDUSessionResourcesAdmitted_List =
	    ossGetInitializedMemory(world, sizeof(struct OSS_XNAP_PDUSessionResourcesAdmitted_List_));
    if (!(*ot_val2->decoded.pdu_PDUSessionResourcesAdmitted_List)) {
	/* Deallocate OSS_XNAP_PDU  on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_PDUSessionResourcesAdmitted_List", OUT_MEMORY, NULL);
    }
    ot_val2->pduNum = OSS_XNAP_PDUSessionResourcesAdmitted_List_PDU;
    a_list = *ot_val2->decoded.pdu_PDUSessionResourcesAdmitted_List;
    a_list_item = &a_list->value;
    a_list_item->pduSessionId = 1;
    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsAdmitted_List = ossGetInitializedMemory(world, sizeof(struct OSS_XNAP_QoSFlowsAdmitted_List_));
    if (!a_list_item->pduSessionResourceAdmittedInfo.qosFlowsAdmitted_List) {
	/* Deallocate OSS_XNAP_PDU  on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_QoSFlowsAdmitted_List", OUT_MEMORY, NULL);
    }
    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsAdmitted_List->value.qfi = 3;

    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List = ossGetInitializedMemory(world, sizeof(struct OSS_XNAP_QoSFlows_List_withCause_));
    if (!a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List) {
	/* Deallocate OSS_XNAP_PDU  on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_QoSFlowsNotAdmitted_List", OUT_MEMORY, NULL);
    }
	a_list_item->pduSessionResourceAdmittedInfo.bit_mask |= OSS_XNAP_qosFlowsNotAdmitted_List_present;
    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List->value.qfi = 2;
    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List->value.cause.choice = OSS_XNAP_misc_chosen;
    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List->value.cause.u.misc = OSS_XNAP_CauseMisc_unspecified;


    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsAdmitted_List->next = ossGetInitializedMemory(world, sizeof(struct OSS_XNAP_QoSFlowsAdmitted_List_));
    if (!a_list_item->pduSessionResourceAdmittedInfo.qosFlowsAdmitted_List->next) {
	/* Deallocate OSS_XNAP_PDU  on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_QoSFlowsAdmitted_List", OUT_MEMORY, NULL);
    }
    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsAdmitted_List->next->value.qfi = 4;

    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List->next = ossGetInitializedMemory(world, sizeof(struct OSS_XNAP_QoSFlows_List_withCause_));
    if (!a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List->next) {
	/* Deallocate OSS_XNAP_PDU  on error */
	ossFreePDU(world, OSS_XNAP_PDU_PDU, *resp);
	return reportError(world, "ossGetInitializedMemory for OSS_XNAP_QoSFlowsNotAdmitted_List", OUT_MEMORY, NULL);
    }
    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List->next->value.qfi = 5;
    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List->next->value.cause.choice = OSS_XNAP_misc_chosen;
    a_list_item->pduSessionResourceAdmittedInfo.qosFlowsNotAdmitted_List->next->value.cause.u.misc = OSS_XNAP_CauseMisc_unspecified;

    return 0;
}

/*
 * FUNCTION     testXNAP() is used to test XNAP 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 testXNAP(OssGlobal *world, const char *filename)
{
    OssBuf	encoded_data;
    int		err, pdu_num = OSS_XNAP_PDU_PDU;

    ossPrint(world, "======================================================"
		"======================\n");
    ossPrint(world, "### Reading serialized request message from file: \"%s\"... ", filename);
    /* Read serialized message from file */
    err = readEncodingFromFile(world, filename, &encoded_data);
    if (!err) {
	OSS_XNAP_PDU    *req = NULL;

	ossPrint(world, "OK.\n\n");
	/*
	 * Zero deserilaized message pointer in order to allocate it
	 * in API call
	 */
	ossPrint(world, "### Deserializing the message...\n");
	/* 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, "### Printing the deserialized message by ossPrintPDU():\n");
	    /* Print deserialized message */
	    err = ossPrintPDU(world, OSS_XNAP_PDU_PDU, req);
	    ossPrint(world, "\n");
	    if (err) {
		reportError(world, "ossPrintPDU()", err, NULL);
		if (req)
		/* Deallocate input message */
		    ossFreePDU(world,  OSS_XNAP_PDU_PDU, req);
		return err;
	    }
	    ossPrint(world, "### Printing the deserialized message by a handwritten code"
		    " demonstrating how to access data:\n");
	    if (0 == (err = printHandoverRequestMsg(world, req))) {
		OSS_XNAP_PDU    *resp;

		ossPrint(world, "\n### Creating Success Response message... ");
		/* Create successful outcome message */
		err = createSuccessResponse(world, req, &resp);
		if (!err) {
		    OssBuf     msg = { 0, NULL };

		    ossPrint(world, "OK.\n\n### Printing the created responce message by ossPrintPDU():\n");
		    err = ossPrintPDU(world, OSS_XNAP_PDU_PDU, resp);
		    ossPrint(world, "\n");
		    ossPrint(world, "\n### Serializing the response message... \n");
		    /* Serialize outcome message */
		    err = ossEncode(world, OSS_XNAP_PDU_PDU, resp, &msg);
		    ossPrint(world, "\n");
		    if (err) {
			reportError(world, "ossEncode()", err, NULL);
		    /* Print serialized outcome message */
		    } else {
			ossPrint(world, "Serialized response (%ld bytes):\n",
				    msg.length);
			printHexString(world, msg.length, msg.value, 0);
			ossFreeMemory(world, msg.value);
			ossPrint(world, "\n\n");
		    }
		    /* Free memory allocated for outcome message */
		    ossFreePDU(world, OSS_XNAP_PDU_PDU, resp);
		}
	    }
	}
	/*
	 * Free memory allocated for input message if it is deserialized
	 * successfully
	 */
	if (req)
	    ossFreePDU(world, OSS_XNAP_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_xnap_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 = testXNAP(world, "XNAP-PDU_HandoverRequest.per");
    /* Free all resources */
    ossterm(world);

    if (!err)
	printf("Testing successful.\n");

    return err;
}

Expected Output (Excerpt)

This is the expected output when running the sample:

Decoded XnAP message:
  ...
Encoding response message...
Encoding successful.

ASN.1 Schema Excerpt (xnap.asn)

Show excerpt from xnap.asn
-- Excerpt from ngap.asn 3GPP TS 38.423 V19.0.0 (2025-09) 
-- **************************************************************
--
-- Elementary Procedure definitions
--
-- **************************************************************

XnAP-PDU-Descriptions {
itu-t (0) identified-organization (4) etsi (0) mobileDomain (0)
ngran-access (22) modules (3) xnap (2) version1 (1) xnap-PDU-Descriptions (0) }

DEFINITIONS AUTOMATIC TAGS ::=

BEGIN

-- **************************************************************
--
-- IE parameter types from other modules.
--
-- **************************************************************

IMPORTS
	Criticality,
	ProcedureCode

FROM XnAP-CommonDataTypes

	HandoverRequest,
	HandoverRequestAcknowledge,
	HandoverPreparationFailure,
	SNStatusTransfer,
	UEContextRelease,
	HandoverCancel,
	NotificationControlIndication,
	RANPaging,
	RetrieveUEContextRequest,
	RetrieveUEContextResponse,
	RetrieveUEContextConfirm,
	RetrieveUEContextFailure,
	XnUAddressIndication,
	SecondaryRATDataUsageReport,
	SNodeAdditionRequest,
	SNodeAdditionRequestAcknowledge,
	SNodeAdditionRequestReject,
	SNodeReconfigurationComplete,
	SNodeModificationRequest,
	SNodeModificationRequestAcknowledge,
	SNodeModificationRequestReject,
	SNodeModificationRequired,
	SNodeModificationConfirm,
	SNodeModificationRefuse,
	SNodeReleaseRequest,
	SNodeReleaseRequestAcknowledge,
	SNodeReleaseReject,
	SNodeReleaseRequired,
	SNodeReleaseConfirm,
	SNodeCounterCheckRequest,
	SNodeChangeRequired,
	SNodeChangeConfirm,
	SNodeChangeRefuse,
	RRCTransfer,
	XnRemovalRequest,
	XnRemovalResponse,
	XnRemovalFailure,
	XnSetupRequest,
	XnSetupResponse,
	XnSetupFailure,
	NGRANNodeConfigurationUpdate,
	NGRANNodeConfigurationUpdateAcknowledge,
	NGRANNodeConfigurationUpdateFailure,
	E-UTRA-NR-CellResourceCoordinationRequest,
	E-UTRA-NR-CellResourceCoordinationResponse,
	ActivityNotification,
	CellActivationRequest,
	CellActivationResponse,
	CellActivationFailure,
	ResetRequest,
	ResetResponse,
	ErrorIndication,
	PrivateMessage,
	DeactivateTrace,
	TraceStart,
	HandoverSuccess,
	ConditionalHandoverCancel,
	EarlyStatusTransfer,
	FailureIndication,
	HandoverReport,
	SCGFailureIndication,
	ResourceStatusRequest,
	ResourceStatusResponse,
	ResourceStatusFailure,
	ResourceStatusUpdate,
	MobilityChangeRequest,
	MobilityChangeAcknowledge,
	MobilityChangeFailure,
	AccessAndMobilityIndication,
	CellTrafficTrace,
	RANMulticastGroupPaging,
	ScgFailureInformationReport,
	ScgFailureTransfer,
	F1CTrafficTransfer,
	IABTransportMigrationManagementRequest,
	IABTransportMigrationManagementResponse,
	IABTransportMigrationManagementReject,
	IABTransportMigrationModificationRequest,
	IABTransportMigrationModificationResponse,
	IABResourceCoordinationRequest,
	IABResourceCoordinationResponse,
	CPCCancel,
	PartialUEContextTransfer,
	PartialUEContextTransferAcknowledge,
	PartialUEContextTransferFailure,
	RachIndication,
	DataCollectionRequest,
	DataCollectionResponse,
	DataCollectionFailure,
	DataCollectionUpdate,
	ODSIB1ConfigurationProvisionRequest,
	ODSIB1ConfigurationProvisionResponse,
	ODSIB1ConfigurationProvisionFailure,
	ODSIB1ConfigurationProvisionStatus,
	LTMConfigurationUpdate,
	LTMConfigurationUpdateAcknowledge,
	LTMConfigurationUpdateFailure,
	CSIRSCoordinationRequest,
	CSIRSCoordinationResponse,
	CellSwitchNotification,
	TAInformationTransfer,
	LTMCancel,
	CLI-Indication





FROM XnAP-PDU-Contents

	id-handoverPreparation,
	id-sNStatusTransfer,
	id-handoverCancel,
	id-notificationControl,
	id-retrieveUEContext,
	id-rANPaging,
	id-xnUAddressIndication,
	id-uEContextRelease,
	id-secondaryRATDataUsageReport,
	id-sNGRANnodeAdditionPreparation,
	id-sNGRANnodeReconfigurationCompletion,
	id-mNGRANnodeinitiatedSNGRANnodeModificationPreparation,
	id-sNGRANnodeinitiatedSNGRANnodeModificationPreparation,
	id-mNGRANnodeinitiatedSNGRANnodeRelease,
	id-sNGRANnodeinitiatedSNGRANnodeRelease,
	id-sNGRANnodeCounterCheck,
	id-sNGRANnodeChange,
	id-activityNotification,
	id-rRCTransfer,
	id-xnRemoval,
	id-xnSetup,
	id-nGRANnodeConfigurationUpdate,
	id-e-UTRA-NR-CellResourceCoordination,
	id-cellActivation,
	id-reset,
	id-errorIndication,
	id-privateMessage,
	id-deactivateTrace,
	id-traceStart,
	id-handoverSuccess,
	id-conditionalHandoverCancel,
	id-earlyStatusTransfer,
	id-failureIndication,
	id-handoverReport,
	id-scgFailureIndication,
	id-resourceStatusReportingInitiation,
	id-resourceStatusReporting,
	id-mobilitySettingsChange,
	id-accessAndMobilityIndication,
	id-cellTrafficTrace,
	id-RANMulticastGroupPaging,
	id-scgFailureInformationReport,
	id-scgFailureTransfer,
	id-f1CTrafficTransfer,
	id-iABTransportMigrationManagement,
	id-iABTransportMigrationModification,
	id-iABResourceCoordination,
	id-retrieveUEContextConfirm,
	id-cPCCancel,
	id-partialUEContextTransfer,
	id-rachIndication,
	id-dataCollectionReportingInitiation,
	id-dataCollectionReporting,
	id-ODSIB1ConfigurationProvision,
	id-ODSIB1ConfigurationProvisionStatus,
	id-lTMConfigurationUpdate,
	id-cSIRSCoordination,
	id-cellSwitchNotification,
	id-tAInformationTransfer,
	id-lTMCancel,
	id-cLI-Indication





FROM XnAP-Constants;

-- **************************************************************
--
-- Interface Elementary Procedure Class
--
-- **************************************************************

XNAP-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]
}

-- **************************************************************
--
-- Interface PDU Definition
--
-- **************************************************************

XnAP-PDU ::= CHOICE {
	initiatingMessage	InitiatingMessage,
	successfulOutcome	SuccessfulOutcome,
	unsuccessfulOutcome	UnsuccessfulOutcome,
	...
}

InitiatingMessage ::= SEQUENCE {
	procedureCode	XNAP-ELEMENTARY-PROCEDURE.&procedureCode		({XNAP-ELEMENTARY-PROCEDURES}),
	criticality		XNAP-ELEMENTARY-PROCEDURE.&criticality			({XNAP-ELEMENTARY-PROCEDURES}{@procedureCode}),
	value			XNAP-ELEMENTARY-PROCEDURE.&InitiatingMessage	({XNAP-ELEMENTARY-PROCEDURES}{@procedureCode})
}

SuccessfulOutcome ::= SEQUENCE {
	procedureCode	XNAP-ELEMENTARY-PROCEDURE.&procedureCode		({XNAP-ELEMENTARY-PROCEDURES}),
	criticality		XNAP-ELEMENTARY-PROCEDURE.&criticality			({XNAP-ELEMENTARY-PROCEDURES}{@procedureCode}),
	value			XNAP-ELEMENTARY-PROCEDURE.&SuccessfulOutcome	({XNAP-ELEMENTARY-PROCEDURES}{@procedureCode})
}

UnsuccessfulOutcome ::= SEQUENCE {
	procedureCode	XNAP-ELEMENTARY-PROCEDURE.&procedureCode		({XNAP-ELEMENTARY-PROCEDURES}),
	criticality		XNAP-ELEMENTARY-PROCEDURE.&criticality			({XNAP-ELEMENTARY-PROCEDURES}{@procedureCode}),
	value			XNAP-ELEMENTARY-PROCEDURE.&UnsuccessfulOutcome	({XNAP-ELEMENTARY-PROCEDURES}{@procedureCode})
}

-- **************************************************************
--
-- Interface Elementary Procedure List
--
-- **************************************************************

XNAP-ELEMENTARY-PROCEDURES XNAP-ELEMENTARY-PROCEDURE ::= {
	XNAP-ELEMENTARY-PROCEDURES-CLASS-1			|
	XNAP-ELEMENTARY-PROCEDURES-CLASS-2			,
	...
}

XNAP-ELEMENTARY-PROCEDURES-CLASS-1 XNAP-ELEMENTARY-PROCEDURE ::= {
	handoverPreparation										|
	retrieveUEContext										|
	sNGRANnodeAdditionPreparation							|
	mNGRANnodeinitiatedSNGRANnodeModificationPreparation	|
	sNGRANnodeinitiatedSNGRANnodeModificationPreparation	|
	mNGRANnodeinitiatedSNGRANnodeRelease					|
	sNGRANnodeinitiatedSNGRANnodeRelease					|
	sNGRANnodeChange										|
	xnRemoval												|
	xnSetup													|
	nGRANnodeConfigurationUpdate							|
	e-UTRA-NR-CellResourceCoordination						|
	cellActivation											|
	reset													|
	resourceStatusReportingInitiation						|
	mobilitySettingsChange									|
	iABTransportMigrationManagement							|
	iABTransportMigrationModification						|
	iABResourceCoordination									|
	partialUEContextTransfer								|
	dataCollectionReportingInitiation						|
	oDSIB1ConfigurationProvision							|
	lTMConfigurationUpdate									|
	cSIRSCoordination										|
	cLI-Indication,
	...
}

Using ASN.1 Studio (Optional)

The gui/ subdirectory contains an ASN.1 Studio project for this sample. With ASN.1 Studio you can:

  • Open the 5G XnAP ASN.1 modules.
  • Generate code from the ASN.1 schema.
  • Create and edit sample encoded 5G XnAP messages.
  • Export projects to gmake makefiles.

Related Samples

Disclaimer

This sample is provided solely for illustration purposes, for example to demonstrate usage of the OSS ASN.1 Tools API with 3GPP 5G XnAP 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.

Need Help?

If you have questions about using this sample, contact OSS Nokalva Support.

See Also