5_libpcap的使用例子

1.简介

Libpcap是Packet Capture Libray的英文缩写,即数据包捕获函数库。该库提供的C函数接口用于捕捉经过指定网络接口的数据包,该接口应该是被设为混杂模式。这个在原始套接字中有提到。

2.功能

Libpcap主要有如下功能:

(1)数据包捕获

捕获流经本网卡的所有原始数据包,甚至对交换设备中的数据包也能够进行捕获,本功能是嗅探器的基础。

(2)自定义数据包发送

构造任意格式的原始数据包,并发送到目标网络,本功能是新协议验证、甚至攻击验证的基础。

(3)流量采集与统计

对所采集到的网络中的流量信息进行按照新规则分类,按指标进行统计,并输出到指定终端。利用这项功能可以分析目标网络的流量特性。

(4)规则过滤

Libpcap自带规则过滤功能,并提供脚本编程接口,能够按照用户编程的方式对已经采集到的数据包进行过滤,以便提高分析的性能。

3.原理

一个包捕获机制包含三个主要部分,分别是面向底层的包捕获引擎,面向中间层的数据包过滤器,面向应用层的用户接口。    

Linux操作系统对于数据包的处理流程是从底到上的方式,依次经历网络接口卡、网卡驱动层、数据链路层、IP层、传输层,最后到达应用程序。

Libpcap也是基于这种原理,Libpcap的捕获机制并不影响Linux操作系统中网络协议栈对数据包的处理。

对应用程序而言,Libpcap包捕获机制只是提供了一个统一的API接口,用户只需要按照相关的编程流程,简单地调用若干函数就可以捕获到感兴趣的数据包。

具体来说,Libpcap库主要由三个部分组成,网络分接头、数据包过滤器和用户API。

(1)网络分接头

网络分接头(Network Tap)是一种链路层旁路机制,负责采集网卡数据包。

(2) 数据包过滤器

数据包过滤器(Packet Filter)是针对数据包的一种过滤机制,在Libpcap中采用BPF(BSD Packet Filter)算法对数据包执行过滤操作,这种算法的基本思想就是基于规则匹配,对伊符合条件的额数据包进行放行。

(3) 用户API

用户API是Libpcap面向上层应用程序提供的编程接口,用户通过调用相关的函数实现数据包的捕获或者发送。

具体来说,Libpcap的工作原理可以描述为,当一个数据包到达网卡时,Libpcap利用创建的套接字从链路层驱动程序中获得该数据包的拷贝,即旁路机制,同时通过Tap函数将数据包发给BPF过滤器。

BPF过滤器根据用户已经定义好的过滤过则对数据包进行逐一匹配,若匹配成功则放入内核缓冲区,并传递给用户缓冲区,匹配失败则直接丢弃。如果没有设置过滤规则,所有的数据包都将放入内核缓冲区,并传递给用户缓冲区。    

    

4.实战例子

测试平台:ubuntu 14.04

1>周期性获取数据包

handle=pcap_open_live(nic_name,4096,1,5,errBuf);    if(NULL == handle){        printf("Unable to open the adapter. %s\n", nic_name);    }else{        int fd = pcap_get_selectable_fd(handle);        int buf_size;        socklen_t optlen = sizeof(buf_size);        if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf_size, &optlen) == -1) {            perror("getsockopt failed");        }        printf("Kernel buffer size ===>>>>> : %d bytes\n", buf_size);                  buf_size = 320 * 1024;        setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf_size, sizeof(buf_size));            setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size));         // 设置用户态缓冲区大小为        pcap_set_buffer_size(handle, 320 * 1024);        fd = pcap_get_selectable_fd(handle);                   if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf_size, &optlen) == -1) {            perror("getsockopt failed");        }        printf("NEW Recv Kernel buffer size ===>>>>> : %d bytes\n", buf_size);                   if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf_size, &optlen) == -1) {            perror("getsockopt failed");        }        printf("NEW Send Kernel buffer size ===>>>>> : %d bytes\n", buf_size);                   struct bpf_program fp;        // const char *filter = "arp or (ether proto 0x8892) or ((ether proto 0x8100 or ether proto 0x88a8))"; // 根据实际情况调整        // const char *filter = "arp or (ether proto 0x88CC) or (ether proto 0x8892) or (ether proto 0x8100) or (ether proto 0x88a8) or ether proto 0x0800"; // 根据实际情况调整        const char *filter = "arp or ether proto 0x88CC or ether proto 0x8892 or ether proto 0x8100 or ether proto 0x88a8 or ether proto 0x0800"; // 根据实际情况调整        // 编译并应用 BPF 过滤器        if (pcap_compile(handle, &fp, filter, 0, PCAP_NETMASK_UNKNOWN) == -1) {            fprintf(stderr, "Error compiling filter: %s\n", pcap_geterr(handle));        }            if (pcap_setfilter(handle, &fp) == -1) {                fprintf(stderr, "Error setting filter: %s\n", pcap_geterr(handle));        }    }              while(1)    {        header.len = 0;        packet = pcap_next(handle,&header);        if(header.len == 0) {            continue;        }                   for(int i=0; i            printf("%02x ",packet[i]);            if(i%32 == 0){                printf("\n");            }        }

2>事件方式获取

// 回调函数原型void packet_handler(u_char *user, const struct pcap_pkthdr *hdr, const u_char *bytes){    (void)user; // 避免未使用参数警告    printf("Packet captured! Length: %d\n", hdr->len);              for(int i=0; i<len; i++){        printf("%02x ",bytes[i]);        if(i%32 == 0){            printf("\n");        }    }    // 在此解析数据包内容(如解析以太网/IP/TCP头)}              //2 event style    // 创建未激活的pcap句柄        handle = pcap_create(nic_name, errBuf);    if (handle == NULL) {        fprintf(stderr, "Could not create handle: %s\n", errBuf);        return 2;    }              // 设置缓冲区大小为 2MB(单位:字节)    if (pcap_set_buffer_size(handle, 2 * 1024 * 1024) != 0) {        fprintf(stderr, "Error setting buffer size\n");        return 3;    }              // 设置混杂模式(可选)    if (pcap_set_promisc(handle, 1) != 0) {        fprintf(stderr, "Could not set promiscuous mode\n");        return 4;    }              // 激活捕获会话    if (pcap_activate(handle) != 0) {        fprintf(stderr, "Activate error: %s\n", pcap_geterr(handle));        return 5;    }              // 开始事件循环,永久捕获数据包    printf("Starting packet capture...\n");    if (pcap_loop(handle, 0, packet_handler, NULL) < 0) {        fprintf(stderr, "pcap_loop failed: %s\n", pcap_geterr(handle));        return 8;}

          

 如需完整测试工程,可在公众号后台打赏后留言~

    

欢迎关注:

往期相关:

4_profinet主站连接从站报文抓取

3_profinet实时性调优

2_etherCAT通讯为什么那么快?

1_传统以太网为什么不具备实时性

1_eip_现场总线技术与工业以太网

EtherCAT协议概述

实时工业以太网概述

EtherCAT通信特点_7

工业以太网三剑客之EtherCAT_EtherCAT开发_6

EtherCAT开发_2_SSC使用记录

工业通讯Modbus简介(一)

工业以太网三剑客之——EtherNet/IP工业以太网三剑客之——Profinet

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值