巧用pcap 在linux下开发嵌入式网络协议栈
lm_tom@163.com
转载时请注明出处:http://blog.csdn.net/lm_tom
1,引言
相对pc软件的开发,嵌入式软件的开发可供使用的调试资源非常有限.
嵌入式系统差异很大,有的嵌入式处理器是32bit,有的可能是8bit,16bit;
有的可以运行操作系统,比如(linux,ucos...),有的由于资源所限以及应用
实际,没有必要或者说无法运行操作系统;大多嵌入式系统只有有限的调试设备,
比如仿真器,串口输出等,有的甚至只有仿真器。
网络协议(比如tcpip)以及基于其的一些应用有一定复杂性,一般需要一定
的调试手段才能保证正常的开发。但网络协议还有一个特点就是,和硬件无关
(当然,字节序问题可以通过宏的方式解决)。那么自然而然,我们可以想到在
调试资源丰富的pc环境下将协议栈调试通过,然后再移植到嵌入式系统上去。无疑
可以大大降低开发难度,缩短开发周期(你可以在没有拿到任何硬件单板时候就
可以动工了,呵呵)。
笔者在pc-linux下调试通过了uip[2]/lwip[3]等tcpip协议栈,实现了dhcp/dns/http
等应用,同时很方便的将在pc-linux下调试通过的uip在z80(8-bit)上跑通(大概
只用了半天时间)。
2,思路
tcpip协议栈主要依靠数据(来自网络接口)和定时器来驱动。定时器在pc-linux
下很容易模拟,但如何模拟网络数据呢?需要从文件读取和写入吗?这是一个办法,
但就是太不方便。 这时候想到了tcpdump软件,该软件可以抓到连接到hub上所有机
器的以太网packet,那么自然想到了可以通过该软件模拟一个虚拟网口,通过该网口
可以向网络上任何机器发送packet,同时可以接收属于自己的packet)。这个网口有
自己的mac地址。
tcpdump基于pcap库[1]实现,hub也很容易满足(50RMB就可以买到一个4-port的,
呵呵).
3,使用pcap模拟以太网虚拟网口
3.1 虚拟网口初始化
1) 主要是初始化pcap,参考如下代码:
dev = "eth0";
/* Find the properties for the device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, 65500, 1, 1, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return(2);
}
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
2)如何设置filter_exp
sprintf(filter_exp,"ether dst %2x:%2x:%2x:%2x:%2x:%2x or ether dst ff:ff:ff:ff:ff:ff",
mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
mac[6]代表虚拟网口的mac地址,由于pc通过hub接入以太网络,可以抓包mac[]对应的包。
3.2 如何读取ethernet frame
res = pcap_next_ex(handle, &header, &packet);
if (res != 1)
{
readSize = 0;
goto exit;
}
readSize = (int)header->len >= 1514 ? 1514 : (int)header->len;
packet地址保存了收到包的内容,readSize保存了收到包的长度。
3.3 如何发送ethernet frame
ret = pcap_sendpacket(handle, buf, buf_len);
即可将buf,buf_len对应的数据包发送出去。
即可将buf,buf_len对应的数据包发送出去。 即可将buf,buf_len对应的数据包发送出去。 即可将buf,buf_len对应的数据包发送出去。
3.4 一些说明
以上部分模拟了一个虚拟网口的驱动,完成了调试协议栈所需的数据驱动条件。
利用上面的驱动,笔者成功调试通过了基于uip/lwip的arp/icmp/dhcp/dns/tcp/udp
协议等。支持动态设置ip/network mask/gateway等,可以接收和发送广播包等。
理论上通过设置合适的filter expresion,也可以支持组波协议的调试。可以直接和
外部网络通信,是一个网络世界标准的citizen.
另外,从目前来看,不足之处主要是:无法动态设置单板mac地址,但这应该不会
影响到调试。
4,结束语
本文给出了一种在pc-linux下调试用于嵌入式设备的网络协议栈的方法,该方法容易实现,
可以大大降低了开发调试难度和缩短了开发时间。非常值得大家一试。
5,参考文献
[1],http://www.tcpdump.org/
[2],http://www.sics.se/~adam/uip/
[3],http://www.sics.se/~adam/lwip/








