昨晚同白总和adden提到抓包的问题。因为写过相关的代码,遇到过抓包的各种各样的坑,此文分享一些相关的经验。
1. Linux cooked capture
在用tcpdump或wireshark看数据包时,有时会看到数据链路层的的包头称为Linux cooked capture。这不是一个真实的包头,当用tcpdump指定从所有网络介质 -i any 抓包时,所有抓到的包的数据链路层被换成了Linux cooked capture。在wireshark官方网站对Linux cooked capture的解释中: 这是一个在linux上被libpcap抓包库使用的“假协议”(pseudo-protocal),当从“any”设备在抓包,或者本地的数据链路层包头无效时被使用。更多,见http://wiki.wireshark.org/SLL
2. IP over IP tunnel
wikipedia的示例图显示IP tunnel上的包的结构是:
但实战中用libpcap库从我们的线上机的IP隧道抓包时,pcap_loop或pcap_dispatch或pcap_next函数返回的数据包只有Inner IP header + IP payload,没有Outer IP headr,也没链路层的头部。多么幸运啊!
3. 混乱的RAW_SOCKET,PF_PACKET协议族
这不是工业标准定义的神秘领域,非常之混乱,谨慎进入。如果要写这方面的socket,可供参考。个人经验,用linux原生的socket抓包时,socket()的三的参数如下组合时的神奇效果.
_recv_fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
从此socket中读所有的IP包,返回的IP包没有链路层包头,因为第二个参数是SOCK_DGRAM,链路层包头返回时被去掉。第三个参数指定的是IP包。奇怪的是,如果出站的IP包的目的地址不是本机,则出站的IP包不会被抓到。
_recv_fd = socket(AF_PACKET , SOCK_RAW , htons(ETH_P_IP));
和上一个socket的区别是,这个会返回链路层包头。和上一个socket一样,同样只能装到部分出站的包。
_recv_fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
这行代码只可以读入站的包,抓不到出站的包。
_recv_fd = socket(AF_PACKET , SOCK_RAW , htons(ETH_P_ALL));
这行代码可以抓到所有出站,入站的包,数据链路层包头也被返回。
4. 外网进来的IP包有可能很大。
N多年前一般的路由的MTU只有1K多,意味着4K大小的IP会被切成小IP包。现在的网络环境与几年前确实大大提高。从外网入来的IP包会大过4096,当时写程序时放IP包的buffer大小为4096,觉得应该无压力,结果观察到程序崩溃,后定位到是放IP包的缓冲区溢出。后来改成8192,暂时没问题。
copyright ykyi.net