怎样提高网站的权重,整合营销传播方案案例,微信网站是什么,西安华为公司转自#xff1a;libpcap实现机制及接口函数 - 简书
1.Libpcap 的工作原理 Libpcap的工作原理可以描述为#xff0c;当一个数据包到达网卡时#xff0c;通过网络分接口#xff08;即旁路机制#xff09;将数据包发给BPF过滤器#xff0c;匹配通过的数据包可以被libpcap利…转自libpcap实现机制及接口函数 - 简书
1.Libpcap 的工作原理 Libpcap的工作原理可以描述为当一个数据包到达网卡时通过网络分接口即旁路机制将数据包发给BPF过滤器匹配通过的数据包可以被libpcap利用创建的套接字PF_PACKET从链路层驱动程序中获得。进而在用户空间提供独立于系统的用户级API接口。 流程图示意图 一个数据包的捕捉分为三个主要部分 面向底层包捕获、面向中间层的数据包过滤面向应用层的用户接口
这与Linux操作系统对数据包的处理流程是相同的 网卡-网卡驱动-数据链路层-IP层-传输层-应用程序 2、Libpcap的实现机制
这里实现的包捕获机制是在数据链路层增加一个旁路处理并不干扰系统自身的网路协议栈的处理对发送和接收的数据包通过Linux内核做过滤和缓冲处理最后直接传递给上层应用程序。因此libpcap在捕获到达网卡的数据包后绕开了传统linux协议栈处理直接使用链路层PF_PACKET协议族原始套接字方式向用户空间传递报文。 libpcap捕获报文机制示意图
接下来对照上图分层解释报文从网卡最终到达用户空间的处理流程
网络报文的接收源自网络设备网卡。
物理层
网络设备在接收到一个报文之后通过中断IRQ告知CPU。网卡驱动程序需要注册对该中断事件的处理函数以处理接收到的报文。在中断中执行以下操作分配一个缓冲区sk_buff把接收的数据拷贝进去第一次拷贝对缓冲区结构内的一些参数做初始化以告知较高层协议数据是什么类型skb-protocol非NAPI调用netif_rx( )函数通知内核将帧放入CPU的softnet_data-input_pkt_queue。netif_rx会调用网络接口函数netif_rx_schedule使用softdate_net结构中内嵌的backlog_dev作为dev参数NAPI帧存放在每个设备自己的队列之中。调用netif_rx_schedule函数直接以对应设备的dev结构为参数然后触发相关联的软IRQNET_RX_SOFTIRQ此时网卡驱动程序已经将输入设备排入轮询列表poll_list接下来执行net_rx_action函数浏览poll_list设备列表这些设备的入口队列都有数据非NAPI设备执行process_backlog函数backlog_dev-poll若时间片用完或者配额用尽将该设备放置列表尾部等待下一次中断到来时继续被调用若处理完input_pkt_queue列表中的全部报文则将设备退出poll_list同时打开设备中断服务继续监听下一个报文到来。 NAPI设备执行poll函数dev-poll可以做一些轮询的工作如果网络设备已经接收了多个报文可以一次性处理。就算设备此刻所接收到的报文都已经处理完了驱动程序也可以根据某种方式预判设备在很短的一段时间内还将收到报文于是依然将自己对应的dev结构留在poll_list中处于轮询状态。增大了报文接收的平均延时但避免了大量中断带来的开销。dev设备退出poll_list同时打开设备中断服务。 接着调用netif_receive_skb函数如果有抓包程序由网络分接口进入BPF过滤器将规则匹配的报文拷贝到系统内核缓存 第二次拷贝否则直接丢弃数据包*注 : linux 在 PF_PACKET 类型的 socket 上支持内核过滤。Linux 内核允许我们把一个名为 LPF(Linux Packet Filter) 的过滤器直接放到 PF_PACKET 类型 socket 的处理过程中过滤器在网卡接收中断执行后立即执行 *处理数据链路层的桥接功能根据skb-protocol字段确定上层协议并提交给网络层处理进入网络协议栈| | | 内核空间 | / 数据链路层 / | | 用户空间 | |
libpcap绕过了Linux内核收包流程中协议栈部分的处理使得用户空间API可以直接调用套接字PF_PACKET从链路层驱动程序中获得数据报文的拷贝将其从内核缓冲区拷贝至用户空间缓冲区第三次拷贝fdsocket(PF_PACKET,sock_RAW,htons(ETH_P_ALL))libpcap 函数库注册的报文接收类型为 ETH_P_ALL即接收所有的网络数据帧其处理函数为 packet_rcv()。该函数工作在数据链路层。 进而调用recvfrom函数获得捕获的报文 需要进行系统调用 packet_rcv() 函数将直接调用 skb_queue_tail() 将数据报文存放在代表相应网络连接控制结构struct sock的接收队列 receive_queue 中。这样数据报文在接收过程中就绕过了 TCP 层和 IP 层繁琐的协议处理过程。最后睡眠在 sk 等待队列上的函数 packet_recvmsg() 会接收链路层数据帧并将该数据帧直接拷贝到应用程序缓冲区中。 最后libpcap面向用户空间提供独立于系统的可调用的函数接口3、BPF过滤器 BPF本质上来说是一也个设备驱动(device driver)能够被应用程序用来读取网络上通过这个网络适配器的包。但是BPF又是一个特殊的驱动因为它并没有直接控制网络适配器而是网络适配器真正的设备驱动调用BPF来传递数据。 BPF正常情况下被用作诊断工具去检查与本机相连的网络的流通状况。一个BPF设备能够配置一个filter根据这个filter的特征来忽略或者接收到来的包。 BPF拥有两个组件 the network tap 和 the packet filter 。the network tap 收集来自网络设备驱动的包的一个拷贝并把它专递给监听程序。the packet filter 决定是否接收这个包并且把它拷贝给监听程序 BPF为每一个要求服务的抓包程序关联一个filter和两个buffer。BPF分配buffer 且通常情况下它的额度是4KB the store buffer 被使用来接收来自适配器的数据 the hold buffer被使用来拷贝包到应用程序 通常情况下 当一个包到达网络接口时 数据链路设备驱动将把它发送到系统协议栈。但是当BPF在这个接口上面监听时网络设备驱动将首先调用 BPF的network tap函数。这个tap函数将包送入每一个监听程序的filter。而用户定义的filter决定 是否接收这个包 每一个包有多少字节将会被保存。如果filter接收这个包 那么tap 将会从数据链路层驱动的缓存中拷贝这个数目的字节数到 与这个filter关联的store buffer中store buffer在内核中定义。同时网络接口的设备驱动将会重新获得控制权且正常的协议处理将会进行。 监听进程执行read系统调用去从BPF(hold buffer)接收包,并将阻塞于此。当hold buffer 满的时候或者当超时发生时BPF将会拷贝这些数据到进程内存空间且唤醒这个进程。监听程序能够一次接收多个包。 BPF结构图
4、关键函数
未使用NAPI的网络设备驱动程序通过netif_rx通知内核帧已经接收
skb dev_alloc_skb(pkt_len 5) ;
... ... ...
if (skb!NULL) {skb-dev dev;skb_reserve(skb,2); //把IP对齐在16字节边界上
... ... ...
/*把DATA数据拷贝到sk_buff结构*/
... ... ...
skb-protocol eth_type_trans(skb,dev) ;
netif_rx(skb) ;
dev-last_rxjiffies;
... ... ...}5、用户级API
1获取数据包捕获描述字函数名称pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)函数功能获得用于捕获网络数据包的数据包捕获描述字。参数说明device参数为指定打开的网络设备名。snaplen参数定义捕获数据的最大字节数。Promisc 指定是否将网络接口置于混杂模式。to_ms参数指*定超时时间毫秒。ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消息。
2打开保存捕获数据包文件函数名称pcap_t *pcap_open_offline(char *fname, char *ebuf)函数功能打开以前保存捕获数据包的文件用于读取。参数说明fname参数指定打开的文件名。该文件中的数据格式与tcpdump和tcpslice兼容。”-“为标准输入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传递错误消息。
3转储数据包函数名称pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)函数功能打开用于保存捕获数据包的文件用于写入。参数说明fname参数为”-“时表示标准输出。出错时返回NULL。p参数为调用pcap_open_offline() 或pcap_open_live()函数后返回的pcap结构指针即网卡句柄。fname参数指定打开的文件名存盘的文件名。如果返回NULL则可调用pcap_geterr()函数获取错误消息。
4查找网络设备函数名称char *pcap_lookupdev(char *errbuf)函数功能用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名指针。返回值如果函数出错则返回NULL同时errbuf中存放相关的错误消息。
5获取网络号和掩码
函数名称int pcap_lookupnet(char *device, bpf_u_int32 *netp,bpf_u_int32 *maskp, char *errbuf)函数功能获得指定网络设备的网络号和掩码。参数说明netp参数和maskp参数都是bpf_u_int32指针。返回值如果函数出错则返回-1同时errbuf中存放相关的错误消息。
6)捕获并处理数据包 ** 函数名称**int pcap_dispatch(pcap_t *p, int cnt,pcap_handler callback, u_char *user)函数功能捕获并处理数据包。参数说明cnt参数指定函数返回前所处理数据包的最大值。cnt -1表示在一个缓冲区中处理所有的数据包。cnt0表示处理所有数据包直到产生以下错误之一读取到EOF超时读取。callback参数指定一个带有三个参数的回调函数这三个参数为一个从pcap_dispatch()函数传递过来的u_char指针一个pcap_pkthdr结构的指针和一个数据包大小的u_char指针。返回值如果成功则返回读取到的字节数。读取到EOF时则返回零值。出错时则返回-1此时可调用pcap_perror()或pcap_geterr()函数获取错误消息。
** 7)捕获和处理数据包** ** 函数名称int pcap_loop(pcap_t *p, int cnt,pcap_handler callback, u_char *user) ** 函数功能功能基本与pcap_dispatch()函数相同只不过此函数在cnt个数据包被处理或出现错误时才返回但读取超时不会返回。而如果为pcap_open_live()函数指定了一个非零值的超时设置然后调用pcap_dispatch()函数则当超时发生时pcap_dispatch()函数会返回。cnt参数为负值时pcap_loop()函数将始终循环运行除非出现错误。
** 8)输出数据包**函数名称void pcap_dump(u_char *user, struct pcap_pkthdr *h,u_char *sp) ** 函数功能**向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可作为pcap_dispatch()函数的回调函数。
参数说明: 参数1: 所建立的文件pcap_dump_open()的返回值,要进行强制转换.;参数2: 数据包特有的内容.;参数 3: 数据包内容指针
9)编译字串至过滤程序函数名称int pcap_compile(pcap_t *p, struct bpf_program *fp,char *str, int optimize, bpf_u_int32 netmask)函数功能将str参数指定的字符串编译到过滤程序中。参数说明fp是一个bpf_program结构的指针在pcap_compile()函数中被赋值。optimize参数控制结果代码的优化。netmask参数指定本地网络的网络掩码。
10)指定过滤程序函数名称int pcap_setfilter(pcap_t p, struct bpf_program fp)函数功能指定一个过滤程序。参数说明fp参数是bpf_program结构指针通常取自pcap_compile()函数调用。 ** 返回值出错时返回-1成功时返回0
11)获取下一个数据包函数名称u_char pcap_next(pcap_t p, struct pcap_pkthdr *h) ** 函数功能返回指向下一个数据包的u_char指针
12)获取数据链路层类型函数名称int pcap_datalink(pcap_t *p) ** 函数功能**返回数据链路层类型例如DLT_EN10MB
13)获取快照参数值函数名称int pcap_snapshot(pcap_t *p) ** 函数功能**返回pcap_open_live被调用后的snapshot参数值
14)检测字节顺序函数名称int pcap_is_swapped(pcap_t *p)函数功能返回当前系统主机字节与被打开文件的字节顺序是否不同
15)获取主版本号 ** 函数名称**int pcap_major_version(pcap_t *p)函数功能返回写入被打开文件所使用的pcap函数的主版本号
16)获取辅版本号函数名称int pcap_minor_version(pcap_t *p) ** 函数功能**返回写入被打开文件所使用的pcap函数的辅版本号
17)结构赋值函数名称int pcap_stats(pcap_t *p, struct pcap_stat *ps)函数功能向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始捕获数据以来至今共捕获到的数据包统计。如果出错或不支持数据包统计则返回-1且可调用pcap_perror()或pcap_geterr()函数来获取错误消息。
18)获取打开文件名函数名称FILE *pcap_file(pcap_t *p)函数功能返回被打开文件的文件名。
19)获取描述字号码函数名称int pcap_fileno(pcap_t *p)函数功能返回被打开文件的文件描述字号码
** 20)显示错误消息**函数名称void pcap_perror(pcap_t *p, char *prefix)函数功能在标准输出设备上显示最后一个pcap库错误消息。以prefix参数指定的字符串为消息头。
6、参考文档
基于 linux 平台的 libpcap 源代码分析libpcap使用方法libpcap编程小结tcpdump sniffexPF_PACKET原始套接字 SOCK_RAWudp数据报从网卡驱动到用户空间流程总结PACKET_MMAP实现原理分析 linux网络报文接收发送浅析 高速网络环境下基于零拷贝的报文捕获机制基于Linux平台的libpcap源码分析和优化14人点赞 日记本 作者shaarawy18 链接https://www.jianshu.com/p/ed6db49a3428 来源简书 著作权归作者所有。商业转载请联系作者获得授权非商业转载请注明出处。