oran code go through

CM xAPP discussion

荣涛 ASN.1编解码
 

Ransim go through

 

# connection process with E2T

cmd/ransim/ransim.go -> main()

    pkg/manager/manager.go -> Run() -> Start() -> startE2Agents()

        pkg/e2agent/agents/agents.go -> Start()

            pkg/e2agent/agent.go -> Start()

                pkg/controller/connection/controller.go -> NewController()

                    onos-lib-go/pkg/controller/watcher.go -> Start()

                pkg/e2agent/connection/connection.go -> NewE2Connection()

                pkg/e2agent/connection/connection.go -> Setup()

                pkg/e2agent/connection/connection.go -> connectAndSetup()  # connect to the E2T controller

                pkg/e2agent/connection/connection.go -> connect()  # connect to the E2T controller by STCP Connection

                pkg/e2agent/connection/connection.go -> setup()  # negotiate E2 setup procedure

                    pkg/utils/e2ap/setup/setup.go -> NewSetupRequest()

                    pkg/utils/e2ap/setup/setup.go -> Build()

                        api/e2ap/v2/e2ap-pdu-contents/e2ap_pdu_contents.pb.go -> E2SetupRequest struct

# RIC address 

cmd/ransim/ransim.go -> main() -> modelName -> cfg

    pkg/manager/manager.go -> NewManager()

    pkg/manager/manager.go -> Run() -> Start() -> model.Load()

        pkg/model/load.go -> Load() -> LoadConfig()

    pkg/manager/manager.go -> Run() -> Start() -> startE2Agents()

        pkg/e2agent/agents/agents.go -> NewE2Agents()

            pkg/e2agent/agent.go -> NewE2Agent()

                pkg/model/model.go -> GetController().Address

#overview of ransim

pkg/model/test.yaml show the overview define of ransim
 

# STCP Connection

onos-e2t/pkg/protocols/e2ap/client.go -> Connect()
    onos-lib-go/pkg/sctp/sctp.go -> DialSCTP()
onos-e2t/pkg/protocols/e2ap/client_conn.go -> ClientConn interface
onos-e2t/pkg/protocols/e2ap/client.go -> ClientInterface procedures.E2NodeProcedures
 

# Sevcie ModelRegistry (HMO example)

cmd/ransim/ransim.go -> main() -> modelName -> cfg 

    pkg/manager/manager.go -> NewManager() -> RegisterModelPlugin()

    pkg/modelplugins/registry.go -> RegisterModelPlugin()

    pkg/manager/manager.go -> Run() -> Start() -> model.Load()

        pkg/e2agent/agents/agents.go -> NewE2Agents()

            pkg/e2agent/agent.go -> NewE2Agent()

                pkg/servicemodel/registry/registry.go -> NewServiceModelRegistry()

                pkg/servicemodel/mho/mho.go -> NewServiceModel()

                    onos-e2-sm/servicemodels/e2sm_mho_go/servicemodel/servicemodel.go -> RanFuncDescriptionProtoToASN1()

                pkg/servicemodel/registry/registry.go -> RegisterServiceModel()

            pkg/e2agent/agent.go -> Start()

                pkg/e2agent/connection/connection.go -> NewE2Connection()

 

# Sevcie Model execute  (RICSubscription)

pkg/e2agent/connection/connection.go -> RICSubscription()

 

# startNorthboundServer

cmd/ransim/ransim.go -> main()

    pkg/manager/manager.go -> Run() -> Start() -> startNorthboundServer()  # Start gRPC server

 

# E2ConnectionUpdate

 

# processNodeEvents

 

 

 

 
 

ASN.1

 

  1. https://github.com/nokia/asn1c
  2. https://www.cxyzjd.com/article/peng_yw/22437251
  3. https://webcache.googleusercontent.com/search?q=cache:WJRr26A-Af8J:https://www.iitter.com/other/43889.html+&cd=6&hl=zh-CN&ct=clnk&gl=us

1. asn1c-s1ap

这个软件提供了三个应用程序,asn1c,unber,enber,他们和应用程序之间的关系为:

https://img-blog.csdnimg.cn/03ba7d85312a4715924c7dc9ece40625.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1JvbmdfVG9h,size_16,color_FFFFFF,t_70

2. E2AP-C语言

本章主要讲下面的内容:

  1. 如何使用 asn1c 工具将 ASN.1 编码编译成C语言?
  2. 如何开发 ASN.1 代码?【根据实际情况而定】
  3. 如何生成 ASN.1 二进制流?【不懂不会】

2.1. 如何使用 asn1c 工具将 ASN.1 编码编译成C语言?

这个步骤是繁琐的,为了尽可能清晰,我将写个脚本,简化操作流程,同时,我也将用 CMake 简化编译流程。

2.1.1. 准备 ASN.1 文件

这里我直接使用下面的两个文件,

  • e2ap-v01.00.00.asn:E2 Termination中提供;
  • e2ap-v01.01.asn1:我从 O-Ran 文档中提取出的 ASN.1;

这两个文件内容较多,不贴出全部,只给出以小部分内容,以e2ap-v01.00.00.asn为例,文件中定义了一些枚举和结构体,以其中的InitiatingMessage为例:

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

使用 asn1c 编译InitiatingMessage后将生成两个文件 InitiatingMessage.c和 InitiatingMessage.h,看一下结构体

/* InitiatingMessage */
typedef struct InitiatingMessage {​​​​​​​​​​​​​​​​​​​
	ProcedureCode_t	 procedureCode;
	Criticality_t	 criticality;
	struct InitiatingMessage__value {​​​​​​​​​​​​​​​​​​​
		InitiatingMessage__value_PR present;
		union InitiatingMessage__value_u {​​​​​​​​​​​​​​​​​​​
			RICsubscriptionRequest_t	 RICsubscriptionRequest;
			RICsubscriptionDeleteRequest_t	 RICsubscriptionDeleteRequest;
			RICserviceUpdate_t	 RICserviceUpdate;
			RICcontrolRequest_t	 RICcontrolRequest;
			E2setupRequest_t	 E2setupRequest;
			ResetRequest_t	 ResetRequest;
			RICindication_t	 RICindication;
			RICserviceQuery_t	 RICserviceQuery;
			ErrorIndication_t	 ErrorIndication;
		}​​​​​​​​​​​​​​​​​​​ choice;
		
		/* Context for parsing across buffer boundaries */
		asn_struct_ctx_t _asn_ctx;
	}​​​​​​​​​​​​​​​​​​​ value;
	
	/* Context for parsing across buffer boundaries */
	asn_struct_ctx_t _asn_ctx;
}​​​​​​​​​​​​​​​​​​​ InitiatingMessage_t;

再过复杂的问题此处不再讲解,因为我也不懂。

2.1.2. 用ASN.1 文件生成C语言

大家想关注的是如何将 ASN.1 代码编译成 C语言文件的,直接上代码吧:

asn1c -fcompound-names \
		-fincludes-quoted \
		-fno-include-deps \
		-findirect-choice \
		-gen-PER -D. \
		e2ap-v01.00.00.asn

上面的参数,我也不适很懂,就这么用吧,需要注意的是,在执行上面的指令后,会生成很多的源文件,我们先来关注Makefile.am.asn1convert 和 Makefile.am.libasncodecMakefile.am.asn1convert和上面的指令的功能基本相同,这里不做解释,直接看下里面的内容:

# [rongtao@localhost e2ap]$ more Makefile.am.asn1convert 
include ./Makefile.am.libasncodec

bin_PROGRAMS += asn1convert
asn1convert_CFLAGS = $(ASN_MODULE_CFLAGS) -DASN_PDU_COLLECTION 
asn1convert_CPPFLAGS = -I$(top_srcdir)/./
asn1convert_LDADD = libasncodec.la
asn1convert_SOURCES = \
	./converter-example.c\
	./pdu_collection.c
regen: regenerate-from-asn1-source

regenerate-from-asn1-source:
	asn1c -fcompound-names -fincludes-quoted -fno-include-deps -findirect-choice -gen-PER -D. e2ap-v01.00.00.asn

在上面的makefile文件中可以看到文件Makefile.am.libasncodec,这个文件中定义了ASN_MODULE_SRCSASN_MODULE_HDRSASN_MODULE_CFLAGS以及下面的变量:

lib_LTLIBRARIES+=libasncodec.la
libasncodec_la_SOURCES=$(ASN_MODULE_SRCS) $(ASN_MODULE_HDRS)
libasncodec_la_CPPFLAGS=-I$(top_srcdir)/./
libasncodec_la_CFLAGS=$(ASN_MODULE_CFLAGS)
libasncodec_la_LDFLAGS=-lm

后续,我们如果再生成C语言,即可使用下面的命令:

make -f Makefile.am.asn1convert regen

上面的指令也是我再写这个文档的时候才发现的。

2.1.3. 生成的C语言源文件的编译

编译是个难题,我们可以先看一下生成的测试例文件converter-example.c,该源文件中有个宏定义,该宏定义指定,当前文件要测试哪个数据结构,宏定义的使用如下:

/* Convert "Type" defined by -DPDU into "asn_DEF_Type" */
#ifdef PDU
#define    ASN_DEF_PDU(t)    asn_DEF_ ## t
#define    DEF_PDU_Type(t)    ASN_DEF_PDU(t)
#define    PDU_Type    DEF_PDU_Type(PDU)
extern asn_TYPE_descriptor_t PDU_Type;    /* ASN.1 type to be decoded */
#define PDU_Type_Ptr (&PDU_Type)
#else   /* !PDU */
#define PDU_Type_Ptr    NULL
#endif  /* PDU */

我以RICcontrolRequest举例,该结构在e2ap-v01.00.00.asn中的定义为:

RICcontrolRequest ::= SEQUENCE {​​​​​​​​​​​​​​​​​​​
	protocolIEs					ProtocolIE-Container	{​​​​​​​​​​​​​​​​​​​{​​​​​​​​​​​​​​​​​​​RICcontrolRequest-IEs}​​​​​​​​​​​​​​​​​​​}​​​​​​​​​​​​​​​​​​​,
	...
}​​​​​​​​​​​​​​​​​​​

在生成的头文件RICcontrolRequest.h中定义了这个数据结构

/* RICcontrolRequest */
typedef struct RICcontrolRequest {​​​​​​​​​​​​​​​​​​​
	ProtocolIE_Container_1544P7_t	 protocolIEs;
	/*
	 * This type is extensible,
	 * possible extensions are below.
	 */
	/* Context for parsing across buffer boundaries */
	asn_struct_ctx_t _asn_ctx;
}​​​​​​​​​​​​​​​​​​​ RICcontrolRequest_t;

同时,该文件下方有一些声明

/* Implementation */
extern asn_TYPE_descriptor_t asn_DEF_RICcontrolRequest;
extern asn_SEQUENCE_specifics_t asn_SPC_RICcontrolRequest_specs_1;
extern asn_TYPE_member_t asn_MBR_RICcontrolRequest_1[1];

其中asn_DEF_RICcontrolRequest即为PDU定义为RICcontrolRequest的宏定义ASN_DEF_PDU展开值,

#define    ASN_DEF_PDU(t)    asn_DEF_ ## t
#define    DEF_PDU_Type(t)    ASN_DEF_PDU(t)
#define    PDU_Type    DEF_PDU_Type(PDU)

所以,我在 Makefile /CMakeLists.txt 中添加宏定义 -DPDU=RICcontrolRequest,接着进行正常的编译即可,我使用的 CMakeLists.txt,文件内容如下:

###################################################
# 编译使用 asn1c 编译 ASN.1 文件而生成的 C语言 程序
#
# 作者:荣涛 
# 时间:2021年8月
###################################################

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(e2ap)

aux_source_directory(./ DIR_SRCS)

include_directories(./)

find_library(CONFIG config /usr/lib64)
link_libraries(${​​​​​​​​​​​​​​​​​​​CONFIG}​​​​​​​​​​​​​​​​​​​)

add_definitions( -MD -Wall -DPDU=RICcontrolRequest)

ADD_EXECUTABLE(test ${​​​​​​​​​​​​​​​​​​​DIR_SRCS}​​​​​​​​​​​​​​​​​​​)

接下来进行编译即可:

[rongtao@localhost e2ap]$ cd build/
[rongtao@localhost build]$ cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: /home/rongtao/test/ASN.1/asn1c/jt_sran/e2ap/build
[rongtao@localhost build]$ make 
[  0%] Building C object CMakeFiles/test.dir/ANY.c.o
[  1%] Building C object CMakeFiles/test.dir/BIT_STRING.c.o
[  2%] Building C object CMakeFiles/test.dir/BIT_STRING_oer.c.o
[此处省略一万行。。。]
[ 98%] Building C object CMakeFiles/test.dir/xer_encoder.c.o
[ 99%] Building C object CMakeFiles/test.dir/xer_support.c.o
[100%] Linking C executable test
[100%] Built target test
[rongtao@localhost build]$ 

看一眼当前目录中,生成了可执行文件test,至此,我已经讲完了由 asn1c 生成的C语言文件的编译过程。置于如何进行测试,下一章再说。这里,我把上面的步骤谢了一个脚本,可以用,也可以不用。

#!/bin/bash
# 
# 将 O-RAN E2AP ASN.1 转化为 C语言
# 理论上,这个脚本并不限于 E2AP,其他 由 Nokia 发布的
# O-RAN 文档 中的 ASN.1 均可由 此脚本进行C语言的生成,
# 
# 荣涛 rongtao@sylincom.com
# 2021年8月
# 

# 默认的 ASN.1 文件
file_asn1=""

DEFAULT_GEN_DEMO="converter-example.c"

function INFO() {​​​​​​​​​​​​​​​​​​​
	echo -e "\033[1;34m $1 \033[0m"
}​​​​​​​​​​​​​​​​​​​
function ERROR() {​​​​​​​​​​​​​​​​​​​
	echo -e "\033[1;31m $1 \033[0m"
}​​​​​​​​​​​​​​​​​​​


# 帮助信息
function usage() {​​​​​​​​​​​​​​​​​​​
	echo ""
	echo Usage: ./genc.sh [ASN.1 file]
	echo ""
	echo "	[ASN.1 file] is ASN.1 file from some where that i dont know."
	echo ""
	echo "	You must install asn1c-s1ap, download in https://github.com/nokia/asn1c, version is v0.9.29"
	echo "	asn1c's version must be v0.9.29"
	echo ""
}​​​​​​​​​​​​​​​​​​​

# 检查软件是否安装,版本是否对应
function check_asn1c() {​​​​​​​​​​​​​​​​​​​
	which asn1c > /dev/null
	if [ $? != 0 ]; then
		ERROR "FATAL: asn1c not install"
		exit 1
	fi

	# 必须使用 Nokia 的 https://github.com/nokia/asn1c ,也就是 asn1c-s1ap
	if [ $(asn1c -v 2>&1 | grep ASN | awk '{​​​​​​​​​​​​​​​​​​​print $3}​​​​​​​​​​​​​​​​​​​') != "v0.9.29" ]; then
		ERROR "FATAL: wrong asn1c version, must v0.9.29(https://github.com/nokia/asn1c)"
		exit 1
	fi

}​​​​​​​​​​​​​​​​​​​

# 检查 入参,以及 ASN.1 文件是否存在
function check_asn1_file() {​​​​​​​​​​​​​​​​​​​
	if [ $# -lt 1 ]; then
		usage
		exit 1
	fi
	file_asn1=$1
	if [ ! -f $file_asn1 ]; then
		ERROR "FATAL: file \"$file_asn1\" not exist."
		exit 1
	fi
}​​​​​​​​​​​​​​​​​​​

# 使用 asn1c 编译
function compile_asn1_file() {​​​​​​​​​​​​​​​​​​​

	# 编译成 C语言
	asn1c -fcompound-names \
			-fincludes-quoted \
			-fno-include-deps \
			-findirect-choice \
			-gen-PER -D. \
			$file_asn1
}​​​​​​​​​​​​​​​​​​​

# 修改 生成的 C语言测试文件
function modify_test_demo() {​​​​​​​​​​​​​​​​​​​
	if [ ! -f $DEFAULT_GEN_DEMO ]; then
		WARNING "WARNING: file \"$DEFAULT_GEN_DEMO\" not exist."
		return 1
	fi
	echo "#include <stdio.h>" > $DEFAULT_GEN_DEMO
	echo "" >> $DEFAULT_GEN_DEMO
	echo "int main(int argc, char *argv[])" >> $DEFAULT_GEN_DEMO
	echo "{​​​​​​​​​​​​​​​​​​​" >> $DEFAULT_GEN_DEMO
	echo "    printf(\"ASN.1 test running.\n\");" >> $DEFAULT_GEN_DEMO
	echo "    return 0;" >> $DEFAULT_GEN_DEMO
	echo "}​​​​​​​​​​​​​​​​​​​" >> $DEFAULT_GEN_DEMO
}​​​​​​​​​​​​​​​​​​​


# 检查软件是否安装,版本是否对应
INFO "Check asn1c software"
check_asn1c
INFO "Check asn1c software, OK"

# 检查 输入的文件
INFO "Check ASN.1 file"
check_asn1_file $*
INFO "Check ASN.1 file, OK"

# 使用 asn1c 编译
INFO "Compile ASN.1 file"
compile_asn1_file $file_asn1
INFO "Compile ASN.1 file, DONE"


# 修改 自动生成的 测试代码
#INFO "Modify C file"
#modify_test_demo
#INFO "Modify C file, DONE"

INFO ""
INFO "Now, you can do some thing like:"
INFO "$ mkdir build"
INFO "$ cd build"
INFO "$ cmake .."
INFO "$ make"
INFO "$ ./test"

2.1.4. 分析生成的可执行文件

  1. 先看看能执行吗?
[rongtao@localhost e2ap]$ ./test 
./test: No input files specified. Try '-h' for more information
[rongtao@localhost e2ap]$ ./test -h
Usage: ./test [options] <datafile> ...
Where options are:
  -iber        Input is in BER (Basic Encoding Rules) or DER
  -ioer        Input is in OER (Octet Encoding Rules)
  -iper        Input is in Unaligned PER (Packed Encoding Rules) (DEFAULT)
  -iaper        Input is in Aligned PER (Packed Encoding Rules)
  -ixer        Input is in XER (XML Encoding Rules)
  -oder        Output as DER (Distinguished Encoding Rules)
  -ooer        Output as Canonical OER (Octet Encoding Rules)
  -oper        Output as Unaligned PER (Packed Encoding Rules)
  -oaper       Output as Aligned PER (Packed Encoding Rules)
  -oxer        Output as XER (XML Encoding Rules) (DEFAULT)
  -otext       Output as plain semi-structured text
  -onull       Verify (decode) input, but do not output
  -per-nopad   Assume PER PDUs are not padded (-iper)
  -1           Decode only the first PDU in file
  -b <size>    Set the i/o buffer size (default is 8192)
  -c           Check ASN.1 constraints after decoding
  -d           Enable debugging (-dd is even better)
  -n <num>     Process files <num> times
  -s <size>    Set the stack usage limit (default is 30000)
  1. 再看看有没有额外的依赖

我最关心的是,生成的可执行文件有没有额外的依赖关系,虽然我没有在 CMakeLists.txt中加任何的动态库链接,但是为了验证,还是看看:

[rongtao@localhost e2ap]$ ldd test 
	linux-vdso.so.1 =>  (0x00007ffdcf9df000)
	libconfig.so.9 => /usr/lib64/libconfig.so.9 (0x00007f2e2a8bf000)
	libc.so.6 => /usr/lib64/libc.so.6 (0x00007f2e2a4f1000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f2e2aacb000)

GOOD,没有任何其他的依赖,我很开心。

  1. 使用nm查看符号表
[rongtao@localhost e2ap]$ nm test
000000000044bd2b t add_bytes_to_buffer
000000000040b714 t ANY__consume_bytes
000000000040be42 T ANY_decode_aper
000000000040b82b T ANY_decode_uper
000000000040c0fa T ANY_encode_aper
【此处省略一万行。。。】
0000000000452af6 t xer__print2fp
00000000004526e8 T xer_skip_unknown
0000000000451d7d t xer__token_cb
000000000045267a T xer_whitespace_span
0000000000681cd0 b zeros.3886
0000000000681cc0 b zeros.4008

2.1.5. 运行生成的可执行文件

上面已经给出了该测试文件的帮助信息,但是这里再给出一遍吧

[rongtao@localhost e2ap]$ ./test -h
Usage: ./test [options] <datafile> ...
Where options are:
  -iber        Input is in BER (Basic Encoding Rules) or DER
  -ioer        Input is in OER (Octet Encoding Rules)
  -iper        Input is in Unaligned PER (Packed Encoding Rules) (DEFAULT)
  -iaper        Input is in Aligned PER (Packed Encoding Rules)
  -ixer        Input is in XER (XML Encoding Rules)
  -oder        Output as DER (Distinguished Encoding Rules)
  -ooer        Output as Canonical OER (Octet Encoding Rules)
  -oper        Output as Unaligned PER (Packed Encoding Rules)
  -oaper       Output as Aligned PER (Packed Encoding Rules)
  -oxer        Output as XER (XML Encoding Rules) (DEFAULT)
  -otext       Output as plain semi-structured text
  -onull       Verify (decode) input, but do not output
  -per-nopad   Assume PER PDUs are not padded (-iper)
  -1           Decode only the first PDU in file
  -b <size>    Set the i/o buffer size (default is 8192)
  -c           Check ASN.1 constraints after decoding
  -d           Enable debugging (-dd is even better)
  -n <num>     Process files <num> times
  -s <size>    Set the stack usage limit (default is 30000)

这个可执行文件是比较复杂的,具体怎么使用,后续文档中再做解释,本文先到这。

 

REF:  

[4G&5G专题-60]:L3 RRC层 - 定义数据类型与数据结构的超级神器:ASN.1抽象语法标记 

ASN.1 -- 使用asn1c完成ASN encode/decode 

ASN.1 C++编译器使用入门  

Ubuntu ASN1C实例分析  

posted @ 2022-04-21 20:17  lvmxh  阅读(113)  评论(0编辑  收藏  举报