背景

由于业务情况,需要抓取sip数据进行分析,使用gopacket的pcap库进行抓包时,当有大批量数据时会出现抓包数据不完全的情况。

调研情况

参考该文章: https://blog.csdn.net/rong_to...
发现解决该问题可以有两种方案: 1. mmap 2.pf_ring

  1. pf_ring 该方案需要安装指定的网卡驱动程序,在我这边的业务场景中不适用。
  2. mmap libcap在1.1版本时默认支持mmap, 但是在调整了各种参数,抓包还是有丢失的情况,所以退而求其次,使用afpacket,该包底层使用的unix.mmap,直接使用mmap抓包。afpacket有个问题是抓127.0.0.1地址的包,抓到的数据会是双份的,抓外网ip的包OK。

afPacket使用调优

  1. 网卡开启混杂模式,抓包使用混杂模式
  2. 调整snapshot大小最大为65535
  3. 抓包超时时间为-1.

具体的代码为:
参数设置:

    szFrame, szBlock, numBlocks, err := afpacket.AfpacketComputeSize(1024, 65535, os.Getpagesize())
    if err != nil {
        return err
    }
    server.handle, err = afpacket.NewAfpacketHandle("eth0",
        szFrame, szBlock, numBlocks, false, -1*time.Millisecond)
    if err != nil {
        return err
    }

抓包代码:

    source := gopacket.ZeroCopyPacketDataSource(server.handle)
    dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerMap(nil))

for {
select {
 case <-ctx.Done():
return 
default: 
  d, _, err := source.ZeroCopyReadPacketData()
            if err != nil {
                fmt.Println("ReadPacketData err: ", err)
                continue
            }
            data := make([]byte, len(d))
            copy(data, d)
            var eth layers.Ethernet
            var ip4 layers.IPv4
            var udp layers.UDP
            dlc = dlc.Put(&eth)
            dlc = dlc.Put(&ip4)
            dlc = dlc.Put(&udp)
            dlc = dlc.Put(layers.NewSIP()) 
            decoder := dlc.LayersDecoder(layers.LayerTypeEthernet, gopacket.NilDecodeFeedback)
            decodedLayers := make([]gopacket.LayerType, 0, 10)
            _, err = decoder(data, &decodedLayers)
           if udp.NextLayerType() == gopacket.LayerTypePayload {
              // udp处理
           }else if udp.NextLayerType() == layers.LayerTypeSIP {
               // sip处理
           }
      }
}  

这里有几个注意点:

  1. 如果把 dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerMap(nil))
    放到default内,每个包都重新生成一下编码对象,会影响数据处理性能,抓包还是会丢失。
    这里使用的DecodingLayerMap是因为能够更新编码结构体内的对象。
  2. dlc = dlc.Put(layers.NewSIP()) 如果换成dlc = dlc.Put(&layers.SIP),会报错,原因是sip结构体内的Hearder是map类型,未初始化。
  3. udp业务处理时要及时,业务处理较慢时会导致抓包阻塞,抓包数据丢失。
  4. ZeroCopyReadPacketData 该结构体返回的结果不安全,要copy一份避免数据被修改。如果换成ReadPacketData, 还是会出现丢包的情况,目前没找到原因。

白沙云影
1 声望2 粉丝

一个专注于voip的频道