LTE RRC 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 LTE RRC standard (TS 36.331 version 19.0.0).

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

The sample reads RRC messages from .uper files, decodes and prints them, accesses message components, 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 libraries.

To explore more samples (LTE RRC, 5G RRC, S1AP, X2AP, NGAP), visit the main Sample Code page.

Overview

This sample shows how to:

  • Initialize the OSS ASN.1/C++ runtime for the LTE RRC schema.
  • Read .uper files that contain valid PER-encoded LTE RRC messages.
  • Decode and print LTE RRC messages.
  • Access RRC message components.
  • Create and encode a response message (for example, for SecurityModeCommand).
  • 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 LTE RRC protocol (TS 36.331), used with this program example.
RRCConnectionRelease.uper Valid RRCConnectionRelease message encoded with PER Unaligned.
SecurityModeCommand.uper Valid SecurityModeCommand message encoded with PER Unaligned.
LoggedMeasurementConfiguration.uper Valid LoggedMeasurementConfiguration-r10 message encoded with PER Unaligned.
trrc.cpp Simple C++ program that shows how to work with LTE RRC protocol data. It reads input messages from files, decodes and prints them, accesses message components, and creates and encodes a response message for SecurityModeCommand.
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 .cpp 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/C++ 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: trrc.cpp

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

Show trrc.cpp source code
/*****************************************************************************/
/*  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.                                            */
/*****************************************************************************/
/*
 * $Id: e3ae625d284b62dac83194f4ed3c7cd6bb6715c8
 */

/*
 * A demonstrative example for handling 3GPP LTE RRC protocol data in ASN.1/C++
 */
#include <errno.h>
#include "rrc.h"

/*
 * Auxiliary function to make output prettier.
 */
void printBorder()
{
    printf("-------------------------------------------------------\n");
}

char *filesDir; /* 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.
		 */


/*
 * A simple class used to report non-ASN.1/C++ errors (such as a failure to
 * create a socket) to the application. You may use any alternative error
 * handling if you wish.
 */

class NonASN1Exception {
private:
    const char *message;	/* can contain C format specifications */
    const char *extra_data;	/* data handled by fmt. specifications (if any) */
    int errcode;
public:
    NonASN1Exception(const char *msg, int code = 0,
	    const char *extd = NULL);
    NonASN1Exception(const NonASN1Exception & that);
    const char *get_message() const;
    const char *get_extra_data() const;
    int get_errcode() const;
};

NonASN1Exception::NonASN1Exception(const char *msg, int code, const char *extd)
{
    message = msg;
    extra_data = extd;
    errcode = code;
}

NonASN1Exception::NonASN1Exception(const NonASN1Exception & that)
{
    message = that.message;
    errcode = that.errcode;
    extra_data = that.extra_data;
}

const char *NonASN1Exception::get_message() const
{
    return message;
}

const char *NonASN1Exception::get_extra_data() const
{
    return extra_data;
}

int NonASN1Exception::get_errcode() const
{
    return errcode;
}

/*
 * FUNCTION     printHexString() prints data (octet string) as a sequence of
 *              hexadecimal digits 'XXXX...'H
 *
 * PARAMETERS
 *    ctl       ASN.1/C++ control object
 *    value     reference to input data (OssString object)
 */
void printHexString(OssControl *ctl, const OssString &value)
{
    ctl->printHex(value.get_buffer(), value.length());
}

/*
 * FUNCTION     printHexBitString() prints data (bit string) as a sequence of
 *              hexadecimal digits 'XXXX...'H
 *
 * PARAMETERS
 *    ctl       ASN.1/C++ control object
 *    value     reference to input data (OssBitString object)
 */
void printHexBitString(OssControl *ctl, const OssBitString &value)
{
    ctl->printHex((const char *)value.get_buffer(), (value.length() + 7) / 8);
}

/*
 * FUNCTION     Helper function to open file in the specified directory.
 *
 * PARAMETERS
 *     directoryName    directory where fileName resides
 *     fileName         file to open
 *
 * RETURNS      pointer to file on success, NULL on failure
 */
FILE * openInputFile(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 = (char *)asn1Malloc(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"))) {
	printf("Failed to open the '%s' file. Restart the sample program using the file "
		"location as the input parameter.\n", path);
    }

    if (path != fileName)
	asn1Free(path);

    return fv;
}

/*
 * FUNCTION     readEncodingFromFile() reads serialized message from specified
 *              file
 *
 * PARAMETERS
 *    filename  name of file containing serialized message
 *
 * RETURNS      a pointer to a newly allocated EncodedBuffer object containing
 *              the encoding read from the file
 */
static EncodedBuffer *readEncodingFromFile(const char *filename)
{
    long length;
    FILE *in = NULL;
    unsigned char *data = NULL;
    EncodedBuffer *result = new EncodedBuffer();

    try {
	/* Open the file and determine its length */
	in = openInputFile(filesDir, filename);
	if (!in)
	    throw NonASN1Exception(strerror(errno), errno);
	if (fseek(in, 0, SEEK_END))
	    throw NonASN1Exception(strerror(errno), errno);
	length = ftell(in);
	if (length < 0)
	    throw NonASN1Exception(strerror(errno), errno);

	/*
	 * Allocate memory. We need to use asn1Malloc so we can later pass
	 * the ownership of the allocated memory to the EncodedBuffer object.
	 */
	data = (unsigned char *)asn1Malloc(length);
	if (!data)
	    throw NonASN1Exception("No memory", OUT_MEMORY);

	/* Read the file */
	rewind(in);
	if (length != (long)fread(data, 1, (size_t)length, in))
	    throw NonASN1Exception("Error reading the file", 0);
	fclose(in);

	/* Pass the read buffer to the EncodedBuffer */
	result->grab_buffer(length, (char *)data);
	return result;
    } catch (...) {
	if (in)
	    fclose(in);
	if (data)
	    asn1Free(data);
	delete result;
	throw;
    }
}

/*
 * FUNCTION     createSecurityModeCommandResponse() creates an RRC successful
 *              outcome message for a given SecurityModeCommand request.
 *
 * PARAMETERS
 *    ctl       OSS control object
 *    sec_mode  input message (request)
 *    response  pointer to the variable that is to hold the resulting
 *              successful outcome message created by the function
 */
void createSecurityModeCommandResponse(OssControl *ctl,
	SecurityModeCommand *sec_mode, UL_DCCH_Message **resp)
{
    SecurityModeCommand::criticalExtensions & crit_ext =
	    sec_mode->get_criticalExtensions();
    SecurityModeCommand::criticalExtensions::c1 *crit_ext_c1;
    SecurityModeCommand_r8_IEs *ies;
    SecurityAlgorithmConfig::integrityProtAlgorithm ipalg;
    SecurityAlgorithmConfig::cipheringAlgorithm calg;
    UL_DCCH_Message *response;

    if (!(crit_ext_c1 = crit_ext.get_c1()))
	throw NonASN1Exception("Unsupported SecurityModeCommand", -1, NULL);
    if (!(ies = crit_ext_c1->get_securityModeCommand_r8()))
	throw NonASN1Exception("Unsupported SecurityModeCommand", -1, NULL);

    /* Getting integrity protection algorithm and ciphering algorithm */
    SecurityAlgorithmConfig & sec_conf =
	    ies->get_securityConfigSMC().get_securityAlgorithmConfig();
    ipalg = sec_conf.get_integrityProtAlgorithm();
    calg = sec_conf.get_cipheringAlgorithm();

    /* Checking validity of the values */
    if (ipalg != eia0_v920 && ipalg != eia1 && ipalg != eia2)
	throw NonASN1Exception("Unknown integrity protection", -1, NULL);
    if (calg != eea0 && calg != eea1 && calg != eea2)
	throw NonASN1Exception("Unknown ciphering", -1, NULL);

    try {
	/* Allocate response message */
	response = new UL_DCCH_Message();

	UL_DCCH_MessageType content;
	UL_DCCH_MessageType::c1 inner_content;
	SecurityModeComplete sec_mode_comp;

	/* Copy transaction identifier from the request */
	sec_mode_comp.set_rrc_TransactionIdentifier(
		sec_mode->get_rrc_TransactionIdentifier());

	/*
	 * SecurityModeComplete_r8_IEs has no mandatory components. Here
	 * we are creating an empty SecurityModeComplete_r8_IEs object.
	 */
	sec_mode_comp.get_criticalExtensions().set_securityModeComplete_r8(
		SecurityModeComplete_r8_IEs());

	/* Construct a response message */
	inner_content.set_securityModeComplete(sec_mode_comp);
	content.set_c1(inner_content);
	response->set_message(content);
	*resp = response;
    } catch (...) {
	/* In case of any error, delete the response object */
	delete response;
    }
}

/*
 * FUNCTION     testDL_DCCH_Message() is used to test an RRC message received
 *		by the downlink DCCH channel. The input serialized
 *              pdu is deserialized and printed, then an outcome message
 *              is created, printed and encoded. This version processes
 *              only securityModeCommand messages and rrcConnectionRelease
 *              messages (only partially, just printing the input message).
 *              However, it can be easily extended to other message types.
 *
 * PARAMETERS
 *    ctl       OSS control object
 *    filename  name of file containing serialized message
 */
static void testDL_DCCH_Message(OssControl *ctl, const char *filename)
{
    EncodedBuffer *encoded_request;
    EncodedBuffer encoded_response;
    DL_DCCH_Message_PDU req_pdu;
    UL_DCCH_Message_PDU resp_pdu;
    DL_DCCH_Message *request;
    UL_DCCH_Message *response;

    printf("======================================================"
	    "======================\n");
    printf("Read encoding from file: %s\n\n", filename);
    /* Read serialized message from file */
    encoded_request = readEncodingFromFile(filename);
    try {
	DL_DCCH_MessageType::c1 *content;
	RRCConnectionRelease *conn_rel;
	SecurityModeCommand *sec_mode;
	LoggedMeasurementConfiguration_r10 *meas_conf;

	printf("Decoding the input downlink DCCH message\n");
	printBorder();

	/* Decode the input message */
	req_pdu.decode(*ctl, *encoded_request);

	/* Free memory allocated for encoded message */
	delete encoded_request;
	encoded_request = NULL;

	request = req_pdu.get_data();
	content = request->get_message().get_c1();
	if (!content)
	    throw NonASN1Exception("Incorrect message", -1, NULL);

	printf("PDU decoded\n");
	printBorder();

	/* Depending on the message type... */
	if ((conn_rel = content->get_rrcConnectionRelease())) {
	    /* rrcConnectionRelease */

	    /* Print deserialized message */
	    req_pdu.print(*ctl);
	} else if ((sec_mode = content->get_securityModeCommand())) {
	    /* securityModeCommand */

	    /* Print deserialized message */
	    req_pdu.print(*ctl);
	    printf("Creating response\n");
	    printBorder();

	    /* Create response */
	    createSecurityModeCommandResponse(ctl, sec_mode, &response);
	    printf("\nResponse created successfully\n");

	    /* Print the response message */
	    resp_pdu.set_data(*response);
	    resp_pdu.print(*ctl);

	    /* Serialize response */
	    printf("\nEncoding response\n");
	    printBorder();
	    resp_pdu.encode(*ctl, encoded_response);
	    printf("Encoded response (%lu bytes):\n",
		    encoded_response.get_length());
	    printBorder();
	    encoded_response.print_hex(*ctl);
	    printf("\n");

	    /* Free memory allocated for outcome message */
	    delete response;
	} else if ((meas_conf = content->get_loggedMeasurementConfiguration_r10())) {
	    /* LoggedMeasurementConfiguration_r10 */

	    /* Print deserialized message */
	    req_pdu.print(*ctl);
	} else {
	    /* Other message types */

	    printf("Unimplemented\n");
	}

	/* Free the decoded input message */
	req_pdu.free_data(*ctl);
    } catch (...) {
	/* Cleanup in case of any error */
	delete encoded_request;
	throw;
    }
}
/*
 * Main application routine
 */
int main(int argc, char *argv[])
{
    int retcode;
    filesDir = argc > 1 ? argv[1] : (char *) ".";

    try {
	rrc_Control ctl;

	/* Set the encoding rules and encoding/decoding flags */
	ctl.setEncodingRules(OSS_PER_UNALIGNED);
	ctl.setEncodingFlags(AUTOMATIC_ENCDEC | STRICT_PER_ENCODING_OF_DEFAULT_VALUES);
	ctl.setDecodingFlags(AUTOMATIC_ENCDEC);
	ctl.setDebugFlags(PRINT_ERROR_MESSAGES | PRINT_DECODING_DETAILS |
		PRINT_ENCODING_DETAILS);

	testDL_DCCH_Message(&ctl, "RRCConnectionRelease.uper");
	testDL_DCCH_Message(&ctl, "SecurityModeCommand.uper");
	testDL_DCCH_Message(&ctl, "LoggedMeasurementConfiguration.uper");
	retcode = 0;
    } catch (ASN1RuntimeException &exc) {
        retcode = exc.get_code();
	printf("An error occurred: code = %d.\n", retcode);
    } catch (NonASN1Exception &exc) {
	printf("NonASN1Exception: ");
	retcode = exc.get_errcode();
	printf(exc.get_message(), exc.get_extra_data(), retcode);
        printf("\n");
        if (!retcode)
	    retcode = -1;
    } catch (...) {
	printf("An unexpected exception occurred.\n");
	retcode =  -1;
    }
    if (!retcode)
	printf("\nTesting successful.\n");

    return retcode;
}

Expected Output (Excerpt)

This is the expected output when running the sample:

Decoded RRCConnectionRelease:
  rrc-TransactionId = 1
  criticalExtensions = ...
Encoding response message...
Encoding successful.

ASN.1 Schema Excerpt (rrc.asn)

Show excerpt from rrc.asn
-- Excerpt from rrc.asn 3GPP TS 36.331 V19.0.0 (2025-10) 

EUTRA-RRC-Definitions DEFINITIONS AUTOMATIC TAGS ::=

BEGIN



BCCH-BCH-Message ::= SEQUENCE {
	message					BCCH-BCH-MessageType
}

BCCH-BCH-MessageType ::=						MasterInformationBlock



BCCH-BCH-Message-MBMS::= SEQUENCE {
	message					BCCH-BCH-MessageType-MBMS-r14
}

BCCH-BCH-MessageType-MBMS-r14 ::=					MasterInformationBlock-MBMS-r14



BCCH-DL-SCH-Message ::= SEQUENCE {
	message					BCCH-DL-SCH-MessageType
}

BCCH-DL-SCH-MessageType ::= CHOICE {
	c1						CHOICE {
		systemInformation						SystemInformation,
		systemInformationBlockType1				SystemInformationBlockType1
	},
	messageClassExtension	SEQUENCE {}
}



BCCH-DL-SCH-Message-BR ::= SEQUENCE {
	message					BCCH-DL-SCH-MessageType-BR-r13
}

BCCH-DL-SCH-MessageType-BR-r13 ::= CHOICE {
	c1						CHOICE {
		systemInformation-BR-r13				SystemInformation-BR-r13,
		systemInformationBlockType1-BR-r13		SystemInformationBlockType1-BR-r13
	},
	messageClassExtension	SEQUENCE {}
}



BCCH-DL-SCH-Message-MBMS ::= SEQUENCE {
	message						BCCH-DL-SCH-MessageType-MBMS-r14
}

BCCH-DL-SCH-MessageType-MBMS-r14 ::= CHOICE {
	c1									CHOICE {
		systemInformation-MBMS-r14					SystemInformation-MBMS-r14,
		systemInformationBlockType1-MBMS-r14		SystemInformationBlockType1-MBMS-r14
	},
	messageClassExtension	SEQUENCE {}
}



MCCH-Message ::=		SEQUENCE {
	message					MCCH-MessageType
}

MCCH-MessageType ::= CHOICE {
	c1							CHOICE {
		mbsfnAreaConfiguration-r9		MBSFNAreaConfiguration-r9
	},
	later						CHOICE {
		c2								CHOICE{
			mbmsCountingRequest-r10			MBMSCountingRequest-r10
		},
		messageClassExtension	SEQUENCE {}
	}
}



PCCH-Message ::= SEQUENCE {
	message					PCCH-MessageType
}

PCCH-MessageType ::= CHOICE {
	c1						CHOICE {
		paging									Paging
	},
	messageClassExtension	SEQUENCE {}
}



DL-CCCH-Message ::= SEQUENCE {
	message					DL-CCCH-MessageType
}

DL-CCCH-MessageType ::= CHOICE {
	c1						CHOICE {
		rrcConnectionReestablishment			RRCConnectionReestablishment,
		rrcConnectionReestablishmentReject		RRCConnectionReestablishmentReject,
		rrcConnectionReject						RRCConnectionReject,
		rrcConnectionSetup						RRCConnectionSetup
	},
	messageClassExtension	CHOICE {
		c2						CHOICE {
			rrcEarlyDataComplete-r15			RRCEarlyDataComplete-r15,
			spare3	NULL, spare2 NULL, spare1 NULL
		},
		messageClassExtensionFuture-r15			SEQUENCE {}
	}
}



DL-DCCH-Message ::= SEQUENCE {
	message					DL-DCCH-MessageType
}

DL-DCCH-MessageType ::= CHOICE {
	c1						CHOICE {
		csfbParametersResponseCDMA2000			CSFBParametersResponseCDMA2000,
		dlInformationTransfer					DLInformationTransfer,
		handoverFromEUTRAPreparationRequest		HandoverFromEUTRAPreparationRequest,
		mobilityFromEUTRACommand				MobilityFromEUTRACommand,
		rrcConnectionReconfiguration			RRCConnectionReconfiguration,
		rrcConnectionRelease					RRCConnectionRelease,
		securityModeCommand						SecurityModeCommand,
		ueCapabilityEnquiry						UECapabilityEnquiry,
		counterCheck							CounterCheck,
		ueInformationRequest-r9					UEInformationRequest-r9,
		loggedMeasurementConfiguration-r10		LoggedMeasurementConfiguration-r10,
		rnReconfiguration-r10					RNReconfiguration-r10,
		rrcConnectionResume-r13					RRCConnectionResume-r13,
		dlDedicatedMessageSegment-r16			DLDedicatedMessageSegment-r16,
		spare2 NULL, spare1 NULL
	},
	messageClassExtension	SEQUENCE {}
}



UL-CCCH-Message ::= SEQUENCE {
	message					UL-CCCH-MessageType
}

UL-CCCH-MessageType ::= CHOICE {
	c1						CHOICE {
		rrcConnectionReestablishmentRequest		RRCConnectionReestablishmentRequest,
		rrcConnectionRequest					RRCConnectionRequest
	},
	messageClassExtension	CHOICE {
		c2						CHOICE {
			rrcConnectionResumeRequest-r13		RRCConnectionResumeRequest-r13
		},
		messageClassExtensionFuture-r13	CHOICE {
			c3						CHOICE {
				rrcEarlyDataRequest-r15			RRCEarlyDataRequest-r15,
				spare3	NULL, spare2	NULL, spare1	NULL
			},
			messageClassExtensionFuture-r15		SEQUENCE {}
		}
	}
}

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 LTE RRC ASN.1 modules.
  • Generate code from the ASN.1 schema.
  • Create and edit sample encoded LTE RRC 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 LTE RRC 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