ASN.1 concepts are mapped to Protobuf to work with modern code tools and make the development of Service Models (and various E2* interfaces) easier. Go APER library works with these Protobuf definitions to encode and decode information in APER format. It is a fork of the free5gc project, which was significantly reworked and enhanced to work with protobuf driven Go structs, rather than plain Go structs.
When you integrate your SW with ONF’s RIC you may face some issues related to the encoding/decoding of the information sent over the wire. Here is a summary of potential problems you may face.
APER encoding is specified in ITU-T X.691 and may seem to be a bit vague. In O-RAN, encoding/decoding schema is generated with asn1c tool, which generates a C code. Reference asn1c tool recommended by O-RAN is Nokia’s distribution of asn1c.
Go APER library is fully compatible with Nokia’s asn1c tool (and thus compliant with O-RAN). It was proven with unit tests for E2AP and E2SMs (KPMv2, RC-PRE, MHO, Test-SM).
-
Make sure you've inserted all tags in Protobuf, so they correspond to the ASN.1 definition of your SM.
- Clarify if all tags are present in the Protobuf (wrapped with Golang, file extension is
.pb.go
). If you don’t see them (tags), please revisit tutorial on how to create your own SM.
- Clarify if all tags are present in the Protobuf (wrapped with Golang, file extension is
-
Enable DEBUG mode in Go APER library and do a bit by bit analysis by hand (see next section). It helps to determine what goes wrong in the encoding/decoding process, and helps to understand APER encoding/decoding flow better.
APER tags play a huge role in Go APER library. It is a key for the library to understand how to encode or decode certain structure to or from APER bytes. A brief summary on APER tags you may find in the README of the Go APER library. We will provide here some basic examples on APER tags usage.
Please note that APER tags are partially generated with ONF's distribution of asn1c tool. You still have to insert the majority of the tags manually. This is something to automate in the future.
CHOICE
encoding requires inserting indexes. It helps encoder to understand which option has to be encoded or was encoded (in case of the decoding).
message Choice {
// choice from tes_sm.asn1:65
oneof choice {
// @inject_tag: aper:"choiceIdx:1"
int32 choice_a = 1;
// @inject_tag: aper:"choiceIdx:2"
int32 choice_b = 2;
}
};
In E2AP (v1.0 and 2.0)
CHOICE
s are encoded with regard to the canonical ordering. That means that index is not the ordering number, but it is the other information from the ASN.1
definition (i.e., IDs which haveunique
tag). CHOICE index encoding is thus conditional and depends on the value you've put in aunique
ID. In this case a tagcanonicalOrder
is inserted on top of the CHOICE structure (see E2AP protobuf). For that purpose a CHOICE map is being created with protoc-gen-choice plugin.
CHOICE
structure can also be extensible and can contain items in its extension. This requires to put such tags as choiceExt
and fromChoiceExt
to indicate that the CHOICE
can be extended and indicated which items
belong to its extension. A good example of such structure could be found here.
SEQUENCE
encoding usually requires only single tag to be inserted in case it is needed. It isvalueExt
, which indicates that theSEQUENCE
can be extended. If items in the extension are defined, then tagfromValueExt
is used (see below).
message TopLevelPdu {
// @inject_tag: aper:"valueExt"
SequenceExtended value = 1;
}
message SequenceExtended {
bool se1 = 1;
// @inject_tag: aper:"optional"
optional bytes se2 = 2;
// @inject_tag: aper:"fromValueExt"
int32 se3 = 3;
// @inject_tag: aper:"fromValueExt,optional,sizeLB:2,sizeUB:6,sizeExt"
optional string se4 = 4;
}
INTEGER
structures in ASN.1 can have constraints. To apply them in encoding, such flags asvalueLB
,valueUB
andvalueExt
are used.valueLB:
is used to specify the lowerbound.valueUB:
is used to specify the upperbound.valueExt
is used to specify that theINTEGER
upperbound can be exceeded.
message TestUnconstrainedInt {
int32 attr_uci_a = 1 [json_name = "attrUciA"];
int32 attr_uci_b = 2 [json_name = "attrUciB"];
};
message TestConstrainedInt {
// @inject_tag: aper:"valueLB:10,valueUB:100"
int32 attr_ci_a = 1 [json_name = "attrCiA"];
// @inject_tag: aper:"valueLB:255,valueUB:65535"
int32 attr_ci_b = 2 [json_name = "attrCiB"];
// @inject_tag: aper:"valueLB:10,valueUB:4294967295"
int32 attr_ci_c = 3 [json_name = "attrCiC"];
// @inject_tag: aper:"valueUB:100"
int32 attr_ci_d = 4 [json_name = "attrCiD"];
// @inject_tag: aper:"valueLB:10,valueUB:20"
int32 attr_ci_e = 5 [json_name = "attrCiE"];
// @inject_tag: aper:"valueLB:10,valueUB:10"
int32 attr_ci_f = 6 [json_name = "attrCiF"];
// @inject_tag: aper:"valueLB:10,valueUB:10,valueExt"
int32 attr_ci_g = 7 [json_name = "attrCiG"];
};
ENUMERATOR
structures in ASN.1 are treated as a constrainedINTEGER
and thus require to specify lowerbound, upperbound and the extensibility (see above).
message TopLevelPdu {
// @inject_tag: aper:"valueLB:0,valueUB:5,valueExt"
TestEnumeratedExtensible value = 1;
};
enum TestEnumeratedExtensible {
TEST_ENUMERATED_EXTENSIBLE_ENUM1 = 0;
TEST_ENUMERATED_EXTENSIBLE_ENUM2 = 1;
TEST_ENUMERATED_EXTENSIBLE_ENUM3 = 2;
TEST_ENUMERATED_EXTENSIBLE_ENUM4 = 3;
TEST_ENUMERATED_EXTENSIBLE_ENUM5 = 4;
TEST_ENUMERATED_EXTENSIBLE_ENUM6 = 5;
};
PrintableString
,OCTET STRING
andBIT STRING
similarly toINTEGER
can require specifying constraints. Following tags are used:sizeLB:
is used to specify the lowerbound.sizeUB:
is used to specify the upperbound.sizeExt
is used to specify that thePrintableString
,OCTET STRING
orBIT STRING
upperbound can be exceeded.
message BitString {
asn1.v1.BitString attr_bs1 = 1;
// @inject_tag: aper:"sizeLB:20,sizeUB:20"
asn1.v1.BitString attr_bs2 = 2;
// @inject_tag: aper:"sizeLB:20,sizeUB:20,sizeExt"
asn1.v1.BitString attr_bs3 = 3;
// @inject_tag: aper:"sizeLB:0,sizeUB:18"
asn1.v1.BitString attr_bs4 = 4;
// @inject_tag: aper:"sizeLB:22,sizeUB:32"
asn1.v1.BitString attr_bs5 = 5;
// @inject_tag: aper:"sizeLB:28,sizeUB:32,sizeExt"
asn1.v1.BitString attr_bs6 = 6;
};
message PrintableString {
string attr_ps1 = 1;
// @inject_tag: aper:"sizeLB:2,sizeUB:2"
string attr_ps2 = 2;
// @inject_tag: aper:"sizeLB:2,sizeUB:2,sizeExt"
string attr_ps3 = 3;
// @inject_tag: aper:"sizeLB:0,sizeUB:3"
string attr_ps4 = 4;
// @inject_tag: aper:"sizeLB:2,sizeUB:3"
string attr_ps5 = 5;
// @inject_tag: aper:"sizeLB:1,sizeUB:3,sizeExt"
string attr_ps6 = 6;
};
message OctetString {
bytes v1 = 1;
// @inject_tag: aper:"sizeLB:2,sizeUB:2,sizeExt"
bytes v2 = 3;
// @inject_tag: aper:"sizeLB:2,sizeUB:5"
bytes v3 = 3;
}
SEQUENCE OF
is used to describe lists within ASN.1. It is interpreted in Protobuf as a message withrepeated
label. Lists in ASN.1 can also have constraints on its size. Similar tags as forPrintableString
,OCTET STRING
andBIT STRING
are used.
message List {
// @inject_tag: aper:"sizeLB:0,sizeUB:12"
repeated float value = 1 [json_name = "value"];
};
- ASN.1 definition may have items marked as
OPTIONAL
, which means that the presence of this item is not necessary. Such behavior is treated in a Protobuf withoptional
label andoptional
tag.
message Item {
// @inject_tag: aper:"optional"
optional int32 item1 = 1 [json_name = "item1"];
};
Once all tags are in correct place, produced with Go APER library bytes are guaranteed to be the same as bytes produced with asn1c tool.
Sometimes, doing a bit by bit analysis may help a lot. General rule is to go through the APER bytes and decode them by hand, i.e., verify that all bits correspond to ASN.1 definition. Here are few examples on how to do such analysis:
- An analysis of GlobalKPMnode-ID structure per KPM v2.0.3 specification can be found here
- It describes encoding of
CHOICE
structures.
- It describes encoding of
- An analysis of MeasurementLabel structure per KPM v2.0.3 specification can be found here
- It describes encoding of
SEQUENCE
structure with multipleOPTIONAL
items.
- It describes encoding of
- An analysis of MeasurementData (a part of IndicationMessage) per KPM v2.0.3 specification can be found here
- It describes encoding of lists, i.e.,
SEQUENCE OF
structures.
- It describes encoding of lists, i.e.,
- An analysis of a header of RANfunction-Description per KPMv2.0.3 specification can be found here
- It describes encoding of
SEQUENCE
structure headers.
- It describes encoding of
Please consider tacking a look at the links in "Some useful resources" section. It may help to understand the APER encoding flow.
- Read error message carefully. You may exceed the boundary or forgot to include an item in the message.
- You may refer to this guide to get a tip on what does certain error message mean.
- Revisit APER tags and make sure they're present in the
.pb.go
file. - Do a bit by bit analysis to understand where is the root cause of the problem.