抓包程序的一些分享

昨晚同白总和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