由于ROHC类库与操作系统、编译器、交叉编译环境、libc标准库存在兼容性等各种问题,因此选择目前最新版本ubuntu-gnome-15.10-desktop-i386.iso
选择其他,以源码形式创建并安装
注:本程序只支持1.6.0及其以上版本
(2)检查系统准备好安装ROHC library
(3)检索来源
最佳方法:下载ROHC的最新版本源代码(一次性下载打包压缩文件)
源代码被分成了三个库:
1)压缩和解压在一起的库
2)压缩库
3)解压库:对于.xz压缩格式的包,先执行xz -d 解压成.gz格式的在执行tar解压命令
$ wget https://rohc-lib.org/download/rohc-latest.tar.xz
$ wget https://rohc-lib.org/download/rohc-latest.tar.xz.sha256
$ wget https://rohc-lib.org/download/rohc-latest.tar.xz.asc
//上三步手动下载
$ sha256sum -c rohc-latest.tar.xz.sha256//hash校验文件完整性
rohc-latest.tar.xz.sha256: OK//这样显示完整
$ gpg --recv-keys 008E8DAD //只在第一次检索GPG秘钥
$ gpg --verify rohc-latest.tar.xz.asc //检验文件真实性
$ tar xvJf rohc-latest.tar.xz //OpenBSD 注意:使用gtar代替tar
$ cd rohc-X.Y.Z //用版本编号替换X.Y.Z
或者:查找开发版本
$ git clone --depth 10 https://github.com/didier-barvaux/rohc.git
$ cd rohc
(4)配置 library
$ ./autogen.sh --prefix=/usr //设置安装路径:/usr
出现错误(原因:未使用xz解压,解压的包异常):
xcy@xcy:~/Desktop/rohc-1.7.0$ sudo ./autogen.sh --prefix=/usr
Running aclocal... failed
Command aclocal not found, please install it
解决办法:
Sudo apt-get install aclocal
又出现如下问题:
xcy@xcy:~/Desktop/rohc-1.7.0$ sudo apt-get install aclocal
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package aclocal
解决办法:
Sudo apt-get update //用apt-get install 这个命令安装软件包时,系统检查软件包目录,apt-get update 更新软件目录树
注意:如果操作系统为64位,添加--libdir=/usr/lib64参数
(5)创建库和工具
$ make all
(6)运行non-regression tests(检测test程序)//我并未使用
$ make check
(7)生成说明文档
$ ./configure --enable-doc
$ make -C doc/
打开./doc/html/index.html即可查看,说明文档包含了对应的应用函数接口、结构体等数据说明信息
(8)以root权限安装library和工具
6.查看在/usr/lib路径下是否存在关于ROHC的动态库
Librohc.soLibrohc_common.so
Librohc_comp.so
Librohc_decomp.so
7.将动态库添加到编译时的搜索路径(本程序make install的时候已经将动态库添加到/usr/lib)
动态库的搜索路径的顺序:
1)编译目标代码时指定的动态库搜索路径;
2)环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
3)配置文件/etc/ld.so.conf中指定的动态库搜索路径;
4)默认的动态库搜索路径/lib、 /usr/lib。
8.阅读例子文件
进入当前解压目录文件下example/的文件
Simple_rohc_program.c //IP报头压缩
print_rohc_version //打印rohc动态库版本
rtp_detection //带回调检测函数的例子
example_rohc_decomp //解压程序例子
解压端共有三种工作模式:NC状态((No Context)、SC状态(Static Context)和FU状态(Full Context)。
NC状态是解压缩端的最低状态,在ROHC初始化或者当解压缩端连续几次进行错误解压缩数据之后由高状态降到NC状态。NC状态下,只能接收并处理由压缩端发送的IR包。
SC状态是解压缩端的中间状态,与压缩端FO状态相对应,当解压缩端连续对信息头中的动态域进行错误解压缩之后,解压缩端由最高状态降到SC状态。理论上也可以由NC状态升到SC状态,但是实际实施中,我们并没有设计此功能。
Fu状态是解压缩端的最高状态。当解压缩端正确解压缩并成功建立Context之后,状态由低状态升到FC状态。FC状态下,可以进行高压缩率的解压缩处理。
解压缩端在ROHC初始化时处于NC状态。当正确解压缩数据并成功建立Context之后,立刻从NC状态升到FC状态。当解压缩端在连续发生几次解压缩失败之后,根据信息头解压缩错误的部分来决定迁移到SC状态或者NC状态。当低状态再次成功解压缩信息头之后,解压缩端状态迁移到最高状态。
数据包结构体
struct rohc_buf{
struct rohc_ts time; //接收时间,为0表示未知
uint8_t * data; //数据包
size_t max_len; //存储数据包的buffer的最大空间
size_t offset; //有效数据相对data的偏移
size_t len; //data数据的有效长度
}
压缩器指针
struct rohc_comp;
解压器指针
struct rohc_decomp
ROHC_LARGE_CID 和ROHC_SMALL_CID
ROHC关于压缩方式,采用large cids方式压缩,压缩结果比small方式多添加一个字节来表示CID
Rohc_buf_init_empty(struct rohc_buf packet, size_t max_len)
bool rohc_buf_is_empty (const struct rohc_buf buf);
rohc_buf_init_full(__data, __len, __time);
rohc_buf_byte_at(__buf, __offset)
Rohc_status_t rohc_compress4(struct rohc_comp * const comp,
Const struct rohc_buf uncomp_packet,
Struct rohc_buf* const rohc_packet)
参数1:压缩器指针
参数2:未压缩的包的结构体
参数3:rohc_packet结构体
返回值:ROHC_STATUS_OK,成功
ROHC_STATUS_SEGMENT,返回第一段
ROHC_STATUS_OUTPUT_TOO_SMALL,给的rohc_packet的buffer太小
ROHC_STATUS_ERROR,错误
Rohc_status_t rohc_compress4(struct rohc_comp * const comp,
Const struct rohc_buf uncomp_packet,
Struct rohc_buf* const rohc_packet)
参数1:压缩器指针
参数2:未压缩的包的结构体
参数3:rohc_packet结构体
返回值:ROHC_STATUS_OK,成功
ROHC_STATUS_SEGMENT,返回第一段
ROHC_STATUS_OUTPUT_TOO_SMALL,给的rohc_packet的buffer太小
ROHC_STATUS_ERROR,错误
Bool rohc_comp_enable_profile(struct rohc_comp * const comp,
Const rohc_profile_t profile)
使能配置文件
参数1:压缩器指针
参数2:配置文件
返回值:
True:配置文件存在且使能
False:配置文件无效不存在或者禁用
Bool rohc_comp_enable_profiles(struct rohc_comp *const comp,
...
)
使能多个配置文件
参数1:压缩器指针
...: 到-1终止
返回值:
True:全都存在
False:至少有一个不存在
ohc_status_t rohc_comp_get_segment2(struct rohc_comp * const comp,
Struct rohc_buf *const segment)
参数1:压缩器指针
参数2:存储segment的结构体
返回值:ROHC_NEED_SEGMENT,还有其他分段
ROHC_OK,分段取完
ROHC_ERROR,错误
Bool rohc_comp_set_rtp_detection_cb(struct rohc_comp *const comp,
Rohc_rtp_detection_callback_t callback,
Void * const rtp_private)
设置RTP回调函数,在UDP流中检测RTP流
UDP包压缩的时候,callback函数就被调用一次,若返回结果为true,RTP配置文件就被用到压缩上,否则就使用IP/UDP配置文件
参数1:压缩器指针
参数2:检测RTP包的回调函数,如果为NULL则回调函数无效
参数3: 为回调提供的外部内存区域指针
返回值:成功返回true,失败返回false
void rohc_comp_free(struct rohc_buf*comp);
Struct rohc_decomp* rohc_decomp_new2(const rohc_cid_type_t cid_type,
Const rohc_cid_t max_cid,
Const rohc_mode_t mode)
创建解压器
参数cid_type:ROHC_LARGE_CID 或 ROHC_SMALL
参数max_cid:解压处理流数,ROHC_SMALL_CID ,[0,ROHC_SMALL_CID]
ROHC_LARGE_CID,[0,ROHC_LARGE_CID]
参数mode: ROHC_U_MODE,单向模式
ROHC_O_MODE,双向优化模式
ROHC_R_MODE,双向可靠模式
返回值:
成功返回解压器指针
失败返回NULL
bool rohc_decomp_enable_profile ( struct rohc_decomp *const decomp,
const rohc_profile_t profile
)
使能配置文件
bool rohc_decomp_enable_profiles ( struct rohc_decomp *const decomp,
...
)
使能多个配置文件
Rohc_status_t rohc_decompress3(struct rohc_decomp* const decomp,
Const struct rohc_buf rohc_packet,
Struct rohc_buf * const uncomp_packet,
Struct rohc_buf * const rcvd_feedback,
Struct rohc_buf * const feedback_send)
解压缩
参数decomp:解压器指针
参数rohc_packet:ROHC包结构体
参数uncomp_packet:输出结构体,存放解压得到的包的结构体
参数rcv_feedback:输出参数,存放通过反馈通道接收到的远端同侧压缩器的反馈
NULL:忽略反馈信息
非NULL:存储反馈
参数feedback_send:输出参数,存放要通过反馈通道发送给远端压缩器的反馈信息
NULL:解压器不给压缩器传递反馈
非NULL:传递
返回值:
ROHC_STATUS_OK: 解压成功
ROHC_STATUS_NO_CONTEXT: 没有与ROHC包中相适合的CID(上下文)而且
ROHC不是一个IR包
ROHC_STATUS_OUTPUT_TOO_SMALL: 给的buffer太小
ROHC_STATUS_MALFORMED:给的ROHC有错误
ROHC_STATUS_BAD_CRC:CRC检测错误
ROHC_STATUS_ERROR:其他
Void rohc_free_decompressor(struct rohc_decmop* decomp)
销毁ROHC压缩器
参数:要销毁的压缩器
功能描述:
监听网卡设备,读取经过网卡的数据包,将读取的目的端口号是设定端口号的数据包压缩,使用UDP套接字传输压缩后的数据。
流程图:
自定义函数:
int handle_read_eth0(int sockfd);//通过eth1发送数据
int gen_random_num(const struct rohc_comp* const comp,
void * const user_context); //用来产生CID(上下文编号)
struct rohc_comp * create_compressor(void); //创建压缩器
void dump_packet(const struct rohc_buf packet); //遍历数据包
bool compress_with_callback(struct rohc_comp *const compressor,
struct rohc_buf uncomp_packet,
struct rohc_buf *const packet); //压缩数据包
bool rtp_detect(const unsigned char *const ip, //判断UDP的RTP专用传输端口号
const unsigned char *const udp,
const unsigned char *const payload,
const unsigned int payload_size,
void *const rtp_private);
//根据UDP端口号判断是不是RTP包
int getifinfo(void); //获取本地网卡信息
int set_promisc(char *interface, int fd); //将对应网卡设置为混杂模式
int create_listen_eth0(void); //监听网卡eth0
int create_write_eth0(void); //通过网卡eth0发送数据
int filter_packet(const struct rohc_buf packet,int f_port);//通过端口号过滤收到的包
int check_length(const struct rohc_buf packet);//检验是否带有CRC
具体实现步骤:
创建压缩器用来压缩原始包
compressor = create_compressor();
获取本地网卡信息
getifinfo();
create_listen_eth0();
包含:
1) 创建原始套接字
用来监听网卡设备,接收将要用来压缩的数据包
listen_eth0 = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))
2) 设置网卡为混杂模式
接收全部类型的数据包
set_promisc(localeth.name[DATA_ETH0], listen_eth0);
3) Bind网络设备
bind(listen_eth0, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll))
通过eth0发送数据做准备
create_write_eth0();
包含:
1) 创建UDP套接字
send_eth0 = socket( AF_INET,SOCK_DGRAM, 0))
2) 设置设备输出
setsockopt(send_eth0,SOL_SOCKET,SO_BINDTODEVICE,(char *)&ifq, sizeof(ifq))
3) 初始化UDP目的结构体
目的地址、地址协议族、目的端口号等
调用函数handle_read_eth1(listen_eth0),处理数据
包含:
1) 通过网卡0接收并过滤数据
调用函数fiter_packet函数过滤数据包
2) 压缩包
compress_with_callback(compressor,ip_packet,&rohc_packet);
包含:
1) 设置回调函数检测RTP包
自定义回调函数检测UDP包还是RTP包(判断UDP端口号)
rohc_comp_set_rtp_detection_cb(compressor, rtp_detect, NULL);
2) 保存MAC头和CRC校验
//截取MAC头
memcpy(mac,uncomp_packet.data,MAC_LEN);
判断是否带有CRC校验,如果有
将标志flag置为1,没有就置为0
//将flag加入MAC
memset(mac,flag,sizeof(uint_8));
//截取MAC校验
memcpy(check,
uncomp_packet.data+uncomp_packet.len - CHECK_MAC,
CHECK_MAC);
3) 修改偏移量
rohc_buf_pull(&uncomp_packet,MAC_LEN);
4) 压缩数据包
rohc_compress4(compressor, uncomp_packet, &rohc_packet);
5) 将MAC头和CRC校验还原
//将MAC头插入rohc_packet包的头部
rohc_buf_prepend(&rohc_packet,mac,(const size_t)MAC_LEN + 1);
如果有CRC校验
//将MAC校验尾部追加
memcpy(rohc_packet.data+rohc_packet.len,check,CHECK_MAC);
rohc_packet.len += CHECK_MAC;
3) 使用UDP套接字发送压缩包
sendto(send_eth0,buff_to_eth0,length_to_eth0,0,(struct sockaddr*)&sin_adr,sizeof(struct sockaddr_in));
功能描述
实现对接收到UDP数据包进行解压还原。
流程图:
自定义函数:
int handle_read_eth1(int sockfd); //处理数据包
void dump_packet(const struct rohc_buf packet); //遍历数据包
bool decompress_with_callback(struct rohc_decomp *const decomp,
struct rohc_buf rohc_packet,
struct rohc_buf *const ip_packet); //解压ROHC数据包
int filter_packet(const struct rohc_buf packet); //过滤数据包,将UDP目的端口号
不是指定的过滤
struct rohc_decomp* create_decomp(void); //创建压缩器
int getifinfo(void); //获取网卡信息
int set_promisc(char *interface, int fd); //设置混杂模式
int create_listen_eth1(void); //设置eth1原始套接字监听
int create_write_eth0(void); //设置send_eth0套接字
int filter_packet(const struct rohc_buf packet,int f_port);//过滤数据包
int check_length(const struct rohc_buf packet);//检查校验
具体实现步骤:
Decompressor = rohc_decomp_new2(ROHC_LARGE_CID,
ROHC_LARGE_CID_MAX,
ROHC_O_MODE);
getifinfo();
create_listen_eth1();
包含:
1) 创建原始套接字
listen_eth1 = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)))
2) 设置混杂模式
set_promisc(localeth.name[DATA_ETH1], listen_eth1);
3) Bind网络设备
bind(listen_eth1, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll));
4) 准备发送的目的结构体(暂时不用)
handle_read_eth1(listen_eth0);
包含:
1) 接收数据包
recvfrom(sockfd, buff, 1520 , 0, (struct sockaddr*)&remote_ll, (socklen_t*) &socksize);
2) 过滤数据包
根据UDP包的目的端口号过滤收到的数据包
filter_packet(rohc_packet);
端口号符合就进行下一步的处理,不符合就丢弃
3) 解压
decompress_with_callback(decompressor,rohc_packet,&ip_packet)
包含:
1) 保存MAC头和CRC校验
//保存MAC头
memcpy(mac,rohc_packet.data+42,MAC_LEN);
//保存校验位
flag = (uint8_t)rohc_packet.data[42] ;
判断收到的UDP包是否带有CRC校验,若有
//去除CRC
memset(rohc_packet.data+
rohc_packet.len - CHECK_MAC,
0,
CHECK_MAC);
rohc_packet.len -= CHECK_MAC;
2) 设置偏移量
//拉开ROHC包的报头
rohc_buf_pull(&rohc_packet,42 + MAC_LEN + 1);
3) 判断压缩数据是否带有CRC
如果flag = 1,
//去除压缩数据的CRC
memset(rohc_packet.data+rohc_packet.len-CHECK_MAC,
0,CHECK_MAC);
rohc_packet.len -= CHECK_MAC;
4) 解压
rohc_decompress3(decomp,rohc_packet,&packet,NULL,NULL);
5) 还原数据
//添加MAC头
rohc_buf_prepend(&packet,mac,MAC_LEN);
packet.len += CHECK_MAC;
使用的虚拟机系统是ruiva-ubuntu11-dm8148newflowOK
以下均在Linux下完成
Xz -d *.xz //解压成.tar格式
Tar -xvf *.tar //解压文件
Tar -zcvf rohc-1.7.0.tar.gz rohc-1.7.0/ //因为在开发板上解压出现
tar: warning: skipping header 'x'
tar: warning: skipping header 'x' 错误
1. Cd rohc-1.7.0
2. ./configure --prefix=/usr --host=arm-none-linux-gnueabi
执行./configure --prefix=/usr,出现
configure: error: cannot run C compiled programs.
If you meant to cross compile, use `--host'.
See `config.log' for more details
3. 进入src源码目录下
Common //压缩解压公共库源码文件
Comp //创建压缩库所需的源码库文件
Decomp //创建解压库所需的文件
1. 在common目录中创建include目录和src目录,将原来的common下的.h头文件移动到include目录下,将所有的 .c源文件移动到src目录
缺少的config.h文件在解压的源目录下
2. 修改.h和.c文件代码中头文件包含的路径
3. Make编译环境
执行(Linux软件开发工具包)
a. source ~/.bashrc
b. Export EZSDK="/home/ruiva/project/ruiva-dm8148-dvsdk5/ti-ezsdk_dm814x-evm_5_05_01_04"
c. source ${EZSDK}/linux-devkit/environment-setup
执行以上几步,目的是为了构建交叉编译环境
4. 交叉编译
分别交叉编译librohc_common.so、librohc_comp.so、librohc_decomp.so库
例:arm-none-linux-gnueabi-gcc --shared *.c -o librohc_common.so
注意:编译解压缩库librohc_decomp.so时会遇到缺少__rohc_comp_piggyback_feedback和__rohc_comp_deliver_feedback的定义,需要在源码文件中截取函数定义手动添加,我自己单独添加在add_comp.c文件中
5. 移植动态库
将生成的ROHC库通过tftp工具下载到开发板
Tftp -g -r *.so /usr/lib
将对应的头文件下载到/usr/include下的,自己创建的rohc目录里
Mkdir /usr/include/rohc
Tftp -g -r rohc.h/rohc_comp.h/rohc_decomp.h /usr/icnlude/rohc
6. 编译程序
与一般动态库的使用一致
l 新建项目目录
l 在项目目录下创建include头文件,存放所需头文件
n Config.h
n Rohc.h
n Rohc_buf.h
n Rohc_comp.h
n Rohc_decomp.h
n Rohc_packet.h
n Rohc_time.h
n Rohc_traces.h
l 在项目目录下存放动态库文件
n Librohc_common.so
n Librohc_comp.so
n Librohc_decomp.so
l 交叉编译
l 问题描述
第一次发送压缩包时,压缩端需要先检查自己的压缩上下文,看看是否存在上下文,如不存在,发送一个完整的信息头,等到解压端收到后发送确认,压缩端再进行后续的压缩。那么,压缩与解压之间的信息反馈是通过library来完成的,还是需要程序员字节介入完成
l 验证方法
由于压缩端的状态变化是由解压端的信息反馈决定的,因此在解压端创建解压器的时候,选择工作模式是双向还是单向。
因此,先让解压器选择U模式,再选择O模式,每种模式下都连续发送数据包,比较每次压缩轨迹以及压缩后的包的大小。
结果每次压缩端均发生状态迁移
l 验证结果
压缩端的状态迁移接收的反馈信息,来自于解压端但是并不需要手动输入
l 问题描述
实际项目中肯定存在很多需要网络通讯的应用进程,那我们是不是要自己修改内核中关于网络包传输部分,在根源处实现岁数据包的压缩和解压。
l 暂时解决方案
根据《基于Linux平台的ROHC报头压缩系统的研究与实现》论文描述,ROHC的执行触发点有两个:一是发送数据包的时候,对数据包进行压缩;二是对接收到的压缩包进行解压,再提交给上层。
因此,对于发送端,先调用函数返回网络设备的结构体指针,将结构体中的关于数据包发送的函数指针修改为自己写的专门压缩的函数,之后再调用原来的数据包发送函数。
对于接收端,添加ROHC协议(0x8020)到数组packet_ptype_base中,代表ROHC协议包,并添加自己写的,对应ROHC的处理函数进行解压,将解压的数据包交付cpu输入队列,等候再次的中断处理。
l 解决
本项目暂时在Linux系统下,编写应用程序实现报头的压缩,至于是否编写内核模块是今后情形而定。
l 问题描述
我们在压缩和解压的代码中都调用了ROHC库中的函数,那么如何将自己写的压缩解压代码在内核源码中编译
l 解决
本项目暂时在Linux系统下,编写应用程序实现报头的压缩,至于是否编写内核模块是今后情形而定。
l 问题描述
设置MRRU实现包长控制的域值
l 问题描述
在Linux-3.7版本之后,添加了内核模块签名验证功能,用来防止运行环境不一样时,禁止加载模块
l 原因
在普通权限下编译模块,在root权限下运行,环境不一样
l 解决
使用root权限,执行Makefile
l 问题描述
调用函数,得到设备结构体,linux可以使用dev_get_by_name函数取得设备指针,但是使用是需要注意,使用过dev_get_by_name函数后一定要使用dev_put(pDev)函数取消设备引用,不然可能导致GET的设备无法正常卸载。一般在将设备指针赋值给SKB->dev后就可以dev_put(pDev)
但使用__dev_get_by_name就不需要调用dev_put(pDev)
struct net_device * __dev_get_by_name(struct net * net, const char * name);
习惯了使用之前的dev_get_by_name,现在多出个struct net *net参数,感觉不知所措 了。 后来发现可以使用init_net,来用做第一个参数。init_net为全局变量 [Linux/net/core/net_namespace.c]
l 解决
例如
#include
__dev_get_by_name(&init_net,”dev_name”);
得到设备的结构体
l 问题描述
新版网络设备结构体的成员函数指针hard_start_xmit取消了。
l 解决
使用netdev_ops结构体指针,在netdev_ops结构体里面使用了ndo_start_xmit()函数。
l 问题描述
./snd 192.168.237.128(本机IP) 1234
出现recvfrom()函数接收到杂包
l 问题描述
根据ROHC库的说明,在ROHC库中通过UDP端口号来区分是UDP包还是RTP包,因为UDP提供了专门用于传输RTP包的UDP专用端口列表