[关闭]
@natsumi 2015-04-29T00:27:57.000000Z 字数 3577 阅读 3280

[转]用ASN.1编译工具asn1c生成LTE-RRC消息解码程序

LTE ASN


整理自
http://blog.csdn.net/peng_yw/article/details/22437251

最近,从iphone手机拿到LTE-RRC通信数据包,需要对LTE-RRC通信数据包进行详细解码。RRC协议都是ASN.1文法描述的,并且是PER编码。用ASN.1描述的协议的解码工作是比较繁琐的。虽然曾经在3G信令系统项目中,开发过一套ASN.1文法转换为C++文法的编码规范,但还是比较复杂,让新人根据这套编码规范实现协议解码比较难上手,并且维护困难。最好是能够利用asn.1文法编译器,把LTE-RRC协议的asn.1描述文件,编译成相应的C/C++文件,然后再用C/C++编译器,编译出LTE-RRC协议编解码器程序。

asn1c就是这样一个开源的文法编译工具。
取最新版0.9.26用来生成LTE-RRC解码程序。步骤如下:

1. 下载asn1c源码、编译安装asn1c

2. 准备LTE-RRC协议的ASN.1描述文件36331-c50.asn

Eclipse + AsnLab初探中的2.1

3. 生成C代码:

执行命令$ asn1c -S /usr/local/share/asn1c -fcompound-names -fskeletons-copy -gen-PER -pdu=auto 36331-c00.asn
生成一系列C代码。

注意
如果不加-f compound-names,后续c代码编译时,会报很多枚举重定义错误。-fskeleton-copy是会从 -S指定的目录中,拷贝asn基础类型解码文件,为了不存在依赖必须加上。
不加-pdu=auto的话,不会产生pdu_colletion.c文件,这个文件中定义了所有的消息PDU。

4. 修改生成的C代码:

生成的文件目录下,有一个主程序文件:converter-sample.c和一个makefile文件:Makefile.am.sample。如果直接使用生成的主程序文件,需要在converter-sample.c中的include后面增加两行定义:#define PDU BCCH_BCH_Message#define ASN_PDU_COLLECTION

  1. //converter-sample.c
  2. #include <asn_application.h>
  3. #include <asn_internal.h> /* for _ASN_DEFAULT_STACK_MAX */
  4. #define PDU BCCH_BCH_Message // 这个可以从 pdu_collection.c中任选一个,选第一个就可以
  5. #define ASN_PDU_COLLECTION // 这个是为了在windows下编译,linux下在Makefile.am.sample中已经定义了
  6. /* Convert "Type" defined by -DPDU into "asn_DEF_Type" */
  7. #define ASN_DEF_PDU(t) asn_DEF_ ## t
  8. #define DEF_PDU_Type(t) ASN_DEF_PDU(t)
  9. #define PDU_Type DEF_PDU_Type(PDU)
  10. extern asn_TYPE_descriptor_t PDU_Type; /* ASN.1 type to be decoded */
  11. #ifdef ASN_PDU_COLLECTION /* Generated by asn1c: -pdu=... */
  12. extern asn_TYPE_descriptor_t *asn_pdu_collection[];
  13. #endif

另外还有一个注意点:

因为PER编码是基于位的,所以一个PER编码的PDU数据,可能不是正好以字节为单位开始和结束,如果不修改,对于大部分数据,可能会解码错误。找到文件per_opentype.c,120行左右。修改如下

  1. //per_opentype.c:
  2. FREEMEM(buf);
  3. if(padding >= 8) {
  4. ASN_DEBUG("Too large padding %d in open type", (int)padding);
  5. padding = padding % 8; // 加上这行
  6. // _ASN_DECODE_FAILED; // 注释掉这行
  7. } else {
  8. ASN_DEBUG("Non-zero padding");
  9. _ASN_DECODE_FAILED;
  10. }

5. 编译生成的C代码:

$ make -f Makefile.am.sample
就可以生成LTE-RRC的编解码程序,程序名为:progname。

也可以在windows下,创建一个空VC控制台项目,把所有文件加进去,编译windows版本。

windows下编译尝试

  1. /** This file is part of the Mingw32 package.
  2. * unistd.h maps (roughly) to io.h
  3. */
  4. #ifndef _UNISTD_H
  5. #define _UNISTD_H
  6. #include <io.h>
  7. #include <process.h>
  8. #endif /* _UNISTD_H */
  1. //#include <sysexits.h> /* for EX_* exit codes */注释掉
  2. #define EX_UNAVAILABLE 1//增加这几行~随便赋个值先~
  3. #define EX_USAGE 2
  4. #define EX_DATAERR 3
  5. #define EX_OSERR 4

6. 运行progname

可以对LTE-RRC数据进行解码了.
$ progname -h能显示帮助信息。
这个程序跟asn1c已经没有关系了。因为LTE-RRC定义了很多的PDU,所有解码是必须带参数 -p [PDU类型],用$ progname -p list可以查看支持的PDU类型。

例,有个数据,PDU是BCCH-DL-SCH-Message,数据文件[应该是二进制文件(不是文本文件),文件里是连续的二进制数据]为BCCH-DL-SCH.dat,内容为十六进制(00 80 1C 31 18 6F E1 22 B8 35 84 96 E2 D0 00 02 00 7D 0E 77 2C B5 50 9B 98 50 28 64 90 99 46 E9 3C 05 04 EE 94 8A 80 00 00):

$ ./progname -p BCCH-DL-SCH-Message BCCH-DL-SCH.dat 即能以XML格式输出详细的解码结果。

一般来讲,不需要使用自动生成的converter-sample.c主程序,需要自己重写一个。因为拿到的LTE-RRC层数据文件中,不可能就只包含一种PDU,而是含不同PDU类型的。需要调用不同的PDU类型对象来解码。这样的话,也就不需要定义#define PDU xxxx 了。例如,本人现在拿到的数据文件,是在每个RRC层PDU前,加了几个字节,分别标识PDU类型和PDU的数据长度。

这样,一个解析LTE-RRC的消息解码器就做成了。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注