最近使用winpcap在win7下编写网络抓包程序,记录一下。

winpcap是比较老的网络抓包库,在win7和win10上依赖都不是很好,看网上有针对的win10版本的winpcap安装包,这里分享了win10下的winpcap安装包和编译好的库以及winpcap的源程序,连接如下win10pcap安装包
关于win10下重新编译wpcap可参考另外一篇,主要记录了遇到的问题win7编译wpcap

言归正传,本文目的是为了记录下如何提高winpcap的抓包效率,先描述下问题。
问题描述:在PC端使用winpcap编写抓UDP包程序,千兆网卡,另一端是FPGA编写的数据发送端,带宽可达到800M/s,安装官方给的用例实现,存在以下问题。

  • 数据发送失败
  • 丢包非常严重

1、数据发送失败
使用winpcap编写的发送UDP数据的功能,在本地使用wireshark可以抓到包,但在另一端无法收到,wireshark也抓不到,分析的原因是数据包没有送出网卡,无论调用pcap_sendpacket函数还是pcap_sendqueue_transmit都发送不成功,最终将winpcap替换为Npcap,发送数据成功。有次看来还是winpcap的驱动在win7或者win10上依然存在bug。关于Npcap的详细信息可以查看官网Npcap,其实就是优化后的winpcap。
关于组UDP包的代码

//以太网数据头结构如下
#define ETHER_ADDR_LEN 6
typedef struct ether_header{u_char ether_dhost[ETHER_ADDR_LEN];u_char ether_shost[ETHER_ADDR_LEN];u_short ether_type;   //如果上一层为IP协议。则ether_type的值就是0x0800
}ether_header;
/* 4字节的IP地址 */
typedef struct ip_address{u_char byte1;u_char byte2;u_char byte3;u_char byte4;
}ip_address;/* IPv4 首部 */
typedef struct ip_header{unsigned   char        ihl : 4;                //ip   header   length    unsigned   char       version : 4;            //version   u_char  tos;            // 服务类型(Type of service) u_short tot_len;           // 总长(Total length) u_short id;             // 标识(Identification)u_short flag_off;       // 标志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)u_char  ttl;            // 存活时间(Time to live)u_char  protocol;          // 协议(Protocol)u_short check;            // 首部校验和(Header checksum)in_addr  saddr;      // 源地址(Source address)in_addr  daddr;      // 目的地址(Destination address)
//  u_int   op_pad;         // 选项与填充(Option + Padding)
}ip_header;/* UDP 首部*/
typedef struct udp_header{u_short sport;          // 源端口(Source port)u_short dport;          // 目的端口(Destination port)u_short len;            // UDP数据包长度(Datagram length)u_short crc;            // 校验和(Checksum)
}udp_header;
struct Psd_Header {ULONG sourceip; //源IP地址ULONG destip; //目的IP地址BYTE mbz; //置空(0)BYTE ptcl; //协议类型USHORT plen; //TCP/UDP数据包的长度(即从TCP/UDP报头算起到数据包结束的长度 单位:字节)
};

组UDP包的过程

int  makeframe_udp(u_char*frame, char*data, int datalen){int UserDataLen = datalen;int TotolLen = UserDataLen+sizeof(ether_header) + sizeof(ip_header) + sizeof(udp_header);char buffer[1024] = { 0 };ether_header* pether_header = (ether_header*)buffer;ip_header* pip_herder = (ip_header*)(buffer + sizeof(ether_header));udp_header* pudp_herder = (udp_header*)(buffer + sizeof(ether_header) + sizeof(ip_header));//以太网帧头memcpy(pether_header->ether_dhost, _eheader.ether_dhost, ETHER_ADDR_LEN);memcpy(pether_header->ether_shost, _eheader.ether_shost, ETHER_ADDR_LEN);USHORT TmpType = 8;pether_header->ether_type = TmpType;//IP头pip_herder->ihl = sizeof(ip_header) / 4;pip_herder->version = 4;pip_herder->tos = 0;pip_herder->tot_len = htons(TotolLen - sizeof(ether_header));pip_herder->id = htons(0x370d);//????pip_herder->flag_off = htons(0);pip_herder->ttl = 0x40;pip_herder->protocol = IPPROTO_UDP;pip_herder->check = 0;pip_herder->saddr.S_un.S_addr = inet_addr(_sourceaddress);pip_herder->daddr.S_un.S_addr = inet_addr(_dstaddress);pip_herder->check = in_cksum((u_short*)pip_herder, sizeof(ip_header));//构建UDP数据头;pudp_herder->dport = htons(_ndPort);pudp_herder->sport = htons(_nsPort);pudp_herder->len = htons(UserDataLen + 8);pudp_herder->crc = 0;int headlen = sizeof(ether_header) + sizeof(ip_header) + sizeof(udp_header);char buffer2[1024] = { 0 };Psd_Header* psd = (Psd_Header*)buffer2;psd->sourceip = inet_addr(_sourceaddress);psd->destip = inet_addr(_dstaddress);psd->ptcl = IPPROTO_UDP;psd->plen = htons(UserDataLen + 8);psd->mbz = 0;memcpy(buffer2 + sizeof(Psd_Header), (void*)pudp_herder, sizeof(udp_header));memcpy(buffer2 + sizeof(Psd_Header) + sizeof(udp_header), data, UserDataLen);pudp_herder->crc = in_cksum((u_short *)buffer2, UserDataLen + 8 + sizeof(Psd_Header));memcpy(frame, buffer, headlen);memcpy((void*)(frame + headlen), (void*)data, UserDataLen);return TotolLen;}

在这其中需要计算校验和,计算代码

u_short in_cksum(u_short * addr, int len){int     nleft = len;u_int sum = 0;u_short *w = addr;u_short answer = 0;/** Our algorithm is simple, using a 32 bit accumulator (sum), we add* sequential 16 bit words to it, and at the end, fold back all the* carry bits from the top 16 bits into the lower 16 bits.*/while (nleft > 1) {sum += *w++;nleft -= 2;}/* mop up an odd byte, if necessary */if (nleft == 1) {*(unsigned char *)(&answer) = *(unsigned char *)w;sum += answer;}/* add back carry outs from top 16 bits to low 16 bits */sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */sum += (sum >> 16);     /* add carry */answer = ~sum;     /* truncate to 16 bits */return (answer);}

最终的以太网帧还需要加上CRC校验和,和头部的数据,但是在使用winpcap的发送接口时,这些数据段都不需要添加计算。

2、提高抓包效率
npcap的抓包过程包含两级缓存,即内核缓存和用户缓存,内核将网卡接收到的数据拷贝到内核缓存中,wpcap又讲内核缓存中的数据拷贝到用户缓存中,应用层再调用pcap_loop或者pcap_next_ex获取每一包的数据。
通过分析npcap的抓包过程,由于千兆网下瞬时速度太快,当发送数据较多如1M,PC端总是会出现不同程度的丢包现象,使用wireshark也无法抓到,wireshark和自己写的抓包程序现象一样。
想过修改源码,但看到源码,实在没有时间细嚼慢咽了,还是通过经验慢慢摸索下吧。

  • 尝试一
    增大两级缓存,内核缓存默认为1M,用户缓存默认为512K,按道理说内核缓存大小为1M,我发送1M的数据,缓存是够的,在使用默认缓存和增大缓存后,现象却不如人意,依然丢了很多包。初步分析该数量级上的数据包,丢包并不是因为缓存不足引起的,可能数据量大的时候需要更大缓存,因为上层处理数据总是比较慢的。但是大的缓存总比小的好,在内存允许的情况下缓存越大越好了。
  • 尝试二
    尝试改变接收方式,使用pcap_next_ex代替回调pcap_loop,总觉得函数的频繁调用是要消耗系统资源的,在接收线程中,只做了数据包个数累计,不做任何费时操作,但是丢包现象依旧。看来也不是这的原因。
  • 尝试三
    网上有人说将pcap_open_live中的接收数据长度设置的小一点(一般都写成65535),这样加快内核存储空间的分配,加快内核对接受数据的处理,猛一听有点道理,尝试一把,黯然神伤!理论上可能会加快,但不足以解决丢包的问题
  • 尝试四
    将pcap_open_live中的timeout设置的尽量小,比如1ms,这样虽然会有一定程度上占用cpu资源,但是对数据包的响应速度会比较快。
  • 尝试五
    还是要分析原理啊,通过各种尝试,初步认为数据丢包发生在内核处理过程,即送网卡取数据的过程慢了,可如何加快内核的处理过程呢,这个npcap并没有提供接口,但是,npcap提供了内核拷贝最小数据设置的接口pcap_setmintocopy,这个接口很关键,默认值是16k,适当的增加这个设置,就会减少内核数据的拷贝次数,换言之就是减少了内核的操作次数,这就变相的加快了内存处理速度,通过增加这个参数,再配合增加两级缓存,丢包的问题终于得到了有效的缓解。当然我说的是缓解,因为千兆网下UPD的数据包,有要求实时性,怎么敢保证一包不丢呢。

winpcap/npcap 提高抓包效率 发UDP包失败相关推荐

  1. 以太网数据包、IP包、TCP/UDP 包的结构(转)

    源:以太网数据包.IP包.TCP/UDP 包的结构 版本号(Version):长度4比特.标识目前采用的IP协议的版本号.一般的值为0100(IPv4),0110(IPv6). IP包头长度(Head ...

  2. python心跳包原理_心跳包机制设计详解 转载

    存在下面两种情形: 情形一:一个客户端连接服务器以后,如果长期没有和服务器有数据来往,可能会被防火墙程序关闭连接,有时候我们并不想要被关闭连接.例如,对于一个即时通讯软件,如果服务器没有消息时,我们确 ...

  3. 如何快速优化 Linux 内核 UDP 收包效率? | CSDN 博文精选

    作者 | dog250 责编 | 郭芮 出品 | CSDN 博客 现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈 ...

  4. tcpdump显示udp包_TCPdump抓包命令详解

    TCPdump抓包命令 tcpdump是一个用于截取网络分组,并输出分组内容的工具.tcpdump凭借强大的功能和灵活的截取策略,使其成为类UNIX系统下用于网络分析和问题排查的首选工具. tcpdu ...

  5. Wireshark数据抓包分析之UDP协议

    目录 预备知识 1.UDP协议概述 2.什么是UDP协议 3.UDP协议的特点 实验目的 实验环境 实验步骤一 1.配置TCP&UDP测试工具 2.配置服务器端 3.配置客户端 4.获取UDP ...

  6. UDP收/发广播包原理及步骤

    UDP收/发广播包原理及步骤 如果网络中两个主机上的应用程序要相互通信,其一要知道彼此的IP,其二要知道程序可监听的端口.因为同一主机上的程序使用网络是通过端口号来区分的. UDP Socket的使用 ...

  7. linux udp 端口 抓包,tcpdump之UDP抓包

    摘要 使用tcpdump抓UDP包,过滤过滤IP和port,并且自动拆分片段. 安装tcpdump yum install -y tcpdump 使用方法 tcpdump -i bond0 udp p ...

  8. qt定时连续发送udp数据包_TCP和UDP

    首先强调一点,TCP/IP协议是一个协议簇.里面包括很多协议的,UDP只是其中的一个, 之所以命名为TCP/IP协议,因为TCP.IP协议是两个很重要的协议,就用他两命名了. 两个协议的区别实际使用时 ...

  9. 转载:tcpdump抓包和Wireshark解包

    转自:https://www.cnblogs.com/domestique/p/8028405.html 简介 用简单的话来定义tcpdump,就是:dump the traffic on a net ...

  10. 网络安全学习第10篇 - ping程序的实现,抓包分析ping数据包以及ping工具对于网络安全方面的威胁

    请结合附件:Ping的实现原理与ping.cpp的内容,编写一个程序,使其能够实现简单的ping的功能,即判断目标网站是否可以连接,然后通过Wireshark进行抓包分析其ICMP协议,指出哪个数据包 ...

最新文章

  1. String与StringBuffer的区别
  2. 多台服务器通过ssh 无密钥直接登陆主机
  3. 【Anaconda】InvalidVersionSpecError: Invalid version spec: =2.7
  4. 【Attention】Visual Attention Network
  5. YbtOJ-相似子串【SA,RMQ,二分】
  6. 全文检索工具包Lucene
  7. MySQL常见的存储引擎的区别?
  8. 基础网络函数介绍及其Cpp实例(C++)
  9. 超有用的word宏代码——批量裁剪图片
  10. Xilinx平台SRIO介绍(四)SRIO IP核配置
  11. VScode 光标乱跳 光标自动跳动问题
  12. python文件打包为deb_DEB包详解
  13. 如何区别聚合支付“一清二清”?
  14. 高德地图api比例尺
  15. L1-034 点赞 (20分)
  16. 基于Graphhopper的路线导航方案
  17. H 指数 (citations[index] > h 才能符合条件)
  18. 系统架构设计师教程-学习-记录(1)系统架构师知识结构
  19. 天馈线测试仪具备什么功能
  20. SEO让小白头疼一辈子做不好的事情

热门文章

  1. itools苹果录屏大师_屏幕录制软件有哪些?找对合适录屏软件
  2. IPv6组播技术原理
  3. Flask 框架学习1
  4. 【HUST】公选抢课|用Auto.js模拟蹲课过程
  5. 农业银行联行号怎么查询_中国农业银行大额联行号12位是什么,怎么查找
  6. 汇总统计数据工具—你所不知道的arcgis工具
  7. Unity3D asset bundle 格式简析
  8. Unity3D研究院之mac上从.ipa中提取unity3D游戏资源
  9. 机器人足球比赛linux,机器人足球实验报告.doc
  10. lnmp 一键安装详解