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.
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 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.c | 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. |
(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, trrc.c. 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.
/*****************************************************************************/
/* 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: lte-rrc-c.html 3327 2026-01-06 10:07:48Z macra $
*/
/*
* Demonstrates part of RRC protocol layer of an LTE UE stack.
*/
#include <errno.h>
#include <string.h>
#ifdef TOED
#include "orrc_r19.h"
#else
#include "rrc_r19.h"
#endif
#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)) {
ossFreeMemory(world, data);
fclose(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;
}
/*
* FUNCTION createSecurityModeCommandResponse() creates an
* OSS_RRC_UL_DCCH_Message PDU (security mode complete) for given
* OSS_RRC_DL_DCCH_Message PDU (security mode command).
*
* PARAMETERS
* world OSS environment variable
* req input message
* resp pointer to the response created by the function
*
* RETURNS 0 on success, error code on failure
*/
static int createSecurityModeCommandResponse(OssGlobal *world,
OSS_RRC_DL_DCCH_Message *req,
OSS_RRC_UL_DCCH_Message **resp)
{
OSS_RRC_SecurityAlgorithmConfig_integrityProtAlgorithm ipalg;
OSS_RRC_CipheringAlgorithm_r12 calg;
if (req->message.choice != OSS_RRC_DL_DCCH_MessageType_c1_chosen ||
req->message.u.c1.choice != OSS_RRC_securityModeCommand_chosen ||
req->message.u.c1.u.securityModeCommand.criticalExtensions.choice !=
OSS_RRC_SecurityModeCommand_criticalExtensions_c1_chosen ||
req->message.u.c1.u.securityModeCommand.criticalExtensions.u.c1.choice !=
OSS_RRC_securityModeCommand_r8_chosen) {
ossPrint(world, "cannot handle ASN.1 data\n");
return -1;
}
ipalg =
req->message.u.c1.u.securityModeCommand.criticalExtensions.u.c1.u.securityModeCommand_r8.securityConfigSMC.securityAlgorithmConfig.integrityProtAlgorithm;
calg =
req->message.u.c1.u.securityModeCommand.criticalExtensions.u.c1.u.securityModeCommand_r8.securityConfigSMC.securityAlgorithmConfig.cipheringAlgorithm;
if (ipalg != OSS_RRC_integrityProtAlgorithm_reserved &&
ipalg != OSS_RRC_integrityProtAlgorithm_eia1 &&
ipalg != OSS_RRC_integrityProtAlgorithm_eia2) {
ossPrint(world, "unknown integrity protection\n");
return -1;
}
if (calg != OSS_RRC_eea0 &&
calg != OSS_RRC_eea1 &&
calg != OSS_RRC_eea2) {
ossPrint(world, "unknown ciphering\n");
return -1;
}
*resp = ossGetInitializedMemory(world, sizeof(OSS_RRC_UL_DCCH_Message));
if (!*resp)
return reportError(world, "ossGetInitializedMemory for OSS_RRC_UL_DCCH_Message", OUT_MEMORY, NULL);
(*resp)->message.choice = OSS_RRC_UL_DCCH_MessageType_c1_chosen;
(*resp)->message.u.c1.choice = OSS_RRC_securityModeComplete_chosen;
(*resp)->message.u.c1.u.securityModeComplete.rrc_TransactionIdentifier =
req->message.u.c1.u.securityModeCommand.rrc_TransactionIdentifier;
(*resp)->message.u.c1.u.securityModeComplete.criticalExtensions.choice =
OSS_RRC_securityModeComplete_r8_chosen;
return 0;
}
/*
* FUNCTION testDL_DCCH_Message() represents an incomplete implementation
* of UE-side function processing a downlink DCCH message. It
* decodes the message, prints it, and creates a response for some
* particular alternatives. The function can be used to implement
* a complete LTE RRC UE-side dispatcher.
*
* PARAMETERS
* world OSS environment variable
* filename name of the file containing the input message
*
* RETURNS 0 on success, error code on failure
*/
static int testDL_DCCH_Message(OssGlobal *world, const char *filename)
{
OssBuf encoded_data;
int err, pdu_num = OSS_RRC_DL_DCCH_Message_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 deserialized message pointer in order to allocate it
* in API call
*/
OSS_RRC_DL_DCCH_Message *req = NULL;
OSS_RRC_UL_DCCH_Message *resp = NULL;
OSS_RRC_DL_DCCH_MessageType_c1 *msg_typ;
ossPrint(world, "Decoding the input downlink DCCH message\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 {
if (req->message.choice != OSS_RRC_DL_DCCH_MessageType_c1_chosen) {
ossPrint(world, "Incorrect message\n");
} else {
msg_typ = &req->message.u.c1;
ossPrint(world, "PDU decoded\n");
printBorder();
switch (msg_typ->choice) {
/* You can take unimplemented CHOICE alternatives one by
* one and add support for them */
case OSS_RRC_csfbParametersResponseCDMA2000_chosen:
ossPrint(world, "Unimplemented \n");
break;
case OSS_RRC_dlInformationTransfer_chosen:
ossPrint(world, "Unimplemented \n");
break;
case OSS_RRC_handoverFromEUTRAPreparationRequest_chosen:
ossPrint(world, "Unimplemented \n");
break;
case OSS_RRC_mobilityFromEUTRACommand_chosen:
ossPrint(world, "Unimplemented \n");
break;
case OSS_RRC_rrcConnectionReconfiguration_chosen:
ossPrint(world, "Unimplemented \n");
break;
case OSS_RRC_rrcConnectionRelease_chosen:
/* Print deserialized message */
err = ossPrintPDU(world, OSS_RRC_DL_DCCH_Message_PDU, req);
if (err)
reportError(world, "ossPrintPDU()", err, NULL);
break;
case OSS_RRC_securityModeCommand_chosen:
/* Print deserialized message */
err = ossPrintPDU(world, OSS_RRC_DL_DCCH_Message_PDU, req);
if (err) {
reportError(world, "ossPrintPDU()", err, NULL);
} else {
ossPrint(world, "\n");
ossPrint(world, "Creating response\n");
printBorder();
/* Create response */
err = createSecurityModeCommandResponse(world, req, &resp);
if (err) {
reportError(world, "createSecurityModeCommandResponse()", err, NULL);
} else {
OssBuf msg = { 0, NULL };
ossPrint(world, "\n");
ossPrint(world, "Response created successfully\n");
printBorder();
/* Print outcome message */
err = ossPrintPDU(world, OSS_RRC_UL_DCCH_Message_PDU, resp);
ossPrint(world, "\n");
ossPrint(world, "Encoding response\n");
printBorder();
/* Serialize outcome message */
err = ossEncode(world, OSS_RRC_UL_DCCH_Message_PDU, resp, &msg);
ossPrint(world, "\n");
if (err) {
reportError(world, "ossEncode()", err, NULL);
/* Print serialized outcome message */
} else {
ossPrint(world, "Encoded response (%ld bytes):\n",
msg.length);
printBorder();
ossPrintHex(world, (char *)msg.value, msg.length);
ossFreeBuf(world, msg.value);
ossPrint(world, "\n\n");
}
/* Free memory allocated for outcome message */
ossFreePDU(world, OSS_RRC_UL_DCCH_Message_PDU, resp);
}
}
break;
case OSS_RRC_ueCapabilityEnquiry_chosen:
ossPrint(world, "Unimplemented \n");
break;
case OSS_RRC_counterCheck_chosen:
ossPrint(world, "Unimplemented \n");
break;
case OSS_RRC_ueInformationRequest_r9_chosen:
ossPrint(world, "Unimplemented \n");
break;
/* The following messages have been added in the 10-th release */
case OSS_RRC_loggedMeasurementConfiguration_r10_chosen:
err = ossPrintPDU(world, OSS_RRC_DL_DCCH_Message_PDU, req);
if (err)
reportError(world, "ossPrintPDU()", err, NULL);
break;
case OSS_RRC_rnReconfiguration_r10_chosen:
ossPrint(world, "The message is not intended for UE \n");
break;
default:
ossPrint(world, "Unknown CHOICE alternative selected\n");
break;
}
}
}
if (req)
ossFreePDU(world, OSS_RRC_DL_DCCH_Message_PDU, req);
/* Free memory allocated for reading of input message from file */
ossFreeBuf(world, encoded_data.value);
}
return err;
}
/*
* Decodes and prints the input messages; creates, encodes and prints
* the output (response) messages.
*/
int main(int argc, char *argv[])
{
OssGlobal world_data, *world = &world_data;
int err = 0;
err = ossinit(world, rrc);
if (err)
return reportError(NULL, "ossinit()", err, NULL);
filesDir = argc > 1 ? argv[1] : ".";
/* Set flags */
ossSetFlags(world, AUTOMATIC_ENCDEC | STRICT_PER_ENCODING_OF_DEFAULT_VALUES);
/* Set debug mode */
ossSetDebugFlags(world, PRINT_ERROR_MESSAGES | PRINT_DECODING_DETAILS | PRINT_ENCODING_DETAILS);
err = testDL_DCCH_Message(world, "RRCConnectionRelease.uper");
if (!err)
err = testDL_DCCH_Message(world, "SecurityModeCommand.uper");
/* The message LoggedMeasurementConfiguration-r10 has been added to the DL-DCCH-Message
* in the 10th release.*/
if (!err)
err = testDL_DCCH_Message(world, "LoggedMeasurementConfiguration.uper");
/* Free all resources */
ossterm(world);
if (!err)
printf("Testing successful.\n");
return err;
}
This is the expected output when running the sample:
Decoded RRCConnectionRelease: rrc-TransactionId = 1 criticalExtensions = ... Encoding response message... Encoding successful.
-- 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 {}
}
}
}
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 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.
If you have questions about using this sample, contact OSS Nokalva Support.