好记性不如铅笔头

C && C++, 编程, 网络通讯

Linux环境下使用libpcap进行输入流量分析

最近在进行流量审计,需要实现一个设备输入流量分析工具,一般网络抓包我们都是用wireshark后者tcpdump等工具,但是这些工具功能都非常全,对于存储空间和性能都有很高要求。如果我们只是要简单的实现一个输入流量统计和分析功能,我们可以自己基于libpcap库简单实现一个,其实wireshark和tcpdump也是基于libpcap进行的二次开发。
首先记录下网址:
官网:【 https://www.tcpdump.org/
github:【 https://github.com/the-tcpdump-group/libpcap/releases
manpages:【 https://www.tcpdump.org/manpages/pcap.3pcap.html

如何快速搭建一个libpcap的开发环境呢,其实非常简单,在ubuntu下直接安装即可

apt-get install libpcap-dev

然后在编译时链接上 -lpcap 库。

libpcap的官方manpage写的非常清晰全面,因此无需多费口舌来笔记libpcap的API介绍,只要静下心来看一下manpage,就可以实现一个可用的流量分析工具。这里作者简单笔记下自己实现的一个非常简单的对输入流量进行分析统计的DEMO:

// linux_net_listener.c
#include <stdio.h>
#include <pcap/pcap.h>

#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <arpa/inet.h>

//--------------------------------------------------------------
//.h defines

typedef struct
{
    char *p_devname;
    int snap_len;
    int buffer_size;
}pcap_attributes_s;
void start_capture_with_attributes(const pcap_attributes_s *p_attr);
void stop_catpure(void);

//------------------------------------------------------------

typedef enum {
    capture_idle,
    capture_working,
    capture_need_quit,
}capture_status_e;

typedef struct
{
    capture_status_e status;
    pcap_t *p_cap_handler;
}linux_capture_ctx_s;

static linux_capture_ctx_s s_ctx = {0};

void list_all_devs(void)
{
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
    pcap_if_t *p_devs = NULL;
    printf("pcap ver:%s\r\n", pcap_lib_version());

    int result = pcap_findalldevs( &p_devs, errbuf);
    if(result != 0) {
        printf("find dev fail:%d %s\r\n", result, errbuf);
        pcap_freealldevs(p_devs);
        return;
    }

    pcap_if_t *p_tmp = NULL;
    for(p_tmp = p_devs; p_tmp != NULL; p_tmp = p_tmp->next) {
        printf("name:%s desc:%s\r\n", p_tmp->name, p_tmp->description);
    }

    pcap_freealldevs(p_devs);
}


typedef struct
{
    u_int32_t remote_addr;
    u_int32_t local_addr;
    u_int16_t remote_port;
    u_int16_t local_port;
    u_int8_t  ip_proto;
}packet_info_s;

static void __parse_ip_header(const u_char *bytes, packet_info_s *p_pktinfo)
{
    struct iphdr *p_iphdr = (struct iphdr *)(bytes + sizeof(struct ethhdr));
    p_pktinfo->remote_addr = ntohl(p_iphdr->saddr);
    p_pktinfo->local_addr =  ntohl(p_iphdr->daddr);
    p_pktinfo->ip_proto = p_iphdr->protocol;

    switch (p_iphdr->protocol)
    {
        case IPPROTO_TCP: {
            printf("ip_type:TCP ");
            struct tcphdr *p_tcphdr = (struct tcphdr *)(bytes + sizeof(struct ethhdr) + sizeof(struct iphdr));
            p_pktinfo->remote_port = ntohs(p_tcphdr->source);
            p_pktinfo->local_port = ntohs(p_tcphdr->dest);
            break;
        }
        case IPPROTO_UDP: {
            printf("ip_type:UDP ");
            struct udphdr *p_udphdr = (struct udphdr *)(bytes + sizeof(struct ethhdr) + sizeof(struct iphdr));
            p_pktinfo->remote_port = ntohs(p_udphdr->source);
            p_pktinfo->local_port = ntohs(p_udphdr->dest);
            break;
        }
        default: {
            printf("ip_proto:%u ",p_iphdr->protocol);
            break;
        }
    }

}

static void __parse_ether_header(const u_char *bytes, packet_info_s *p_pktinfo)
{
    struct ethhdr *p_ethhdr = (struct ethhdr *)bytes;
    switch ( ntohs(p_ethhdr->h_proto) )
    {
        case ETH_P_IP:{
            printf("ether_type:IP ");
            __parse_ip_header(bytes, p_pktinfo);
            break;
        }
        default: {
            printf("ether_type:0x%X unknown ", ntohs(p_ethhdr->h_proto));
            break;
        }
    }
    printf("ip_proto:%u remote:%x:%u local:%x:%u\r\n", p_pktinfo->ip_proto, p_pktinfo->remote_addr, p_pktinfo->remote_port,
                                                       p_pktinfo->local_addr, p_pktinfo->local_port);

}


static void __pcap_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
#if 0
    printf("+++++\r\nts:%lu.%lu cap:%u len:%u\r\n", h->ts.tv_sec, h->ts.tv_usec, h->caplen, h->len);
    unsigned index = 0;
    for(index = 0; index < h->caplen; index++) {
        printf("%02x ", bytes[index]);
        if((index != 0) && (index % 32 == 0))
            printf("\r\n");
    }
    printf("\r\n-----\r\n");
#endif

    packet_info_s pkg_info = {0};
    __parse_ether_header(bytes, &pkg_info);
}

void start_capture_with_attributes(const pcap_attributes_s *p_attr)
{
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
    int result = 0;
    s_ctx.status = capture_idle;

    s_ctx.p_cap_handler = pcap_create(p_attr->p_devname, errbuf);
    if(s_ctx.p_cap_handler == NULL) {
        printf("create %s fail. %s\r\n", p_attr->p_devname, errbuf);
        goto open_dev_ERR_EXIT;
    }

    result = pcap_set_snaplen(s_ctx.p_cap_handler, p_attr->snap_len);
    if(result != 0) {
        printf("set snaplen %s %d fail. result:%d\r\n", p_attr->p_devname, p_attr->snap_len, result);
        goto open_dev_ERR_EXIT;
    }

    result = pcap_set_immediate_mode(s_ctx.p_cap_handler, 1);
    if(result != 0) {
        printf("set immediate_mode %s 1 fail. result:%d\r\n", p_attr->p_devname, result);
        goto open_dev_ERR_EXIT;
    }

    result = pcap_set_buffer_size(s_ctx.p_cap_handler, p_attr->buffer_size);
    if(result != 0) {
        printf("set buffer size %s %d fail. result:%d\r\n", p_attr->p_devname, p_attr->buffer_size, result);
        goto open_dev_ERR_EXIT;
    }

    // todo linker-layer header modify

    result = pcap_activate(s_ctx.p_cap_handler);
    if(result != 0) {
        char *p_detail = pcap_geterr(s_ctx.p_cap_handler);
        printf("active %s fail. %s\r\n", p_attr->p_devname, p_detail);
        goto open_dev_ERR_EXIT;
    }

    result = pcap_setnonblock(s_ctx.p_cap_handler, 1, errbuf);
    if(result < 0) {
        printf("setnonblock %s fail. result:%d %s\r\n", p_attr->p_devname, result, errbuf);
        goto open_dev_ERR_EXIT;
    }

    result = pcap_setdirection(s_ctx.p_cap_handler, PCAP_D_IN);
    if(result != 0) {
        char *p_detail = pcap_geterr(s_ctx.p_cap_handler);
        printf("setdirection %s fail. %s\r\n", p_attr->p_devname, p_detail);
        goto open_dev_ERR_EXIT;
    }
    s_ctx.status = capture_working;

    printf("start capture\r\n");
    while(s_ctx.status == capture_working) {
        result = pcap_dispatch(s_ctx.p_cap_handler, -1, __pcap_handler, NULL);
        if(result == 0) {
            //todo sleep if need
            continue;
        }
        if(result > 0) {
            continue;
        }
        if(result == PCAP_ERROR) {
            char *p_detail = pcap_geterr(s_ctx.p_cap_handler);
            printf("pcap_dispatch result is PCAP_ERROR. pcap_dispatch %s fail. %s\r\n", p_attr->p_devname, p_detail);
        }else if(result == PCAP_ERROR_BREAK) {
            printf("pcap_dispatch result is PCAP_ERROR_BREAK.\r\n");
        }else {
            printf("pcap_dispatch result:%d. \r\n", result);
        }
        break;
    }


open_dev_ERR_EXIT:
    printf("close pcap\r\n");
    s_ctx.status = capture_idle;
    pcap_close(s_ctx.p_cap_handler);
}

void stop_catpure(void)
{
    printf("stop capture\r\n");
    s_ctx.status = capture_need_quit;
    pcap_breakloop(s_ctx.p_cap_handler);
}

int main(void)
{
    list_all_devs();
    pcap_attributes_s attr =
    {
        .p_devname = "wlp3s0",
        .snap_len = 128,
        .buffer_size = 1024*10,
    };
    start_capture_with_attributes(&attr);

    return 0;
}

编译,链接,执行:

$ gcc linux_net_listener.c -o linux_net_listener -lpcap
$ sudo ./linux_net_listener 
pcap ver:libpcap version 1.8.1
name:wlp3s0 desc:(null)
name:enp0s25 desc:(null)
name:any desc:Pseudo-device that captures on all interfaces
name:lo desc:(null)
name:bluetooth0 desc:Bluetooth adapter number 0
name:nflog desc:Linux netfilter log (NFLOG) interface
name:nfqueue desc:Linux netfilter queue (NFQUEUE) interface
name:usbmon1 desc:USB bus number 1
name:usbmon2 desc:USB bus number 2
name:usbmon3 desc:USB bus number 3
start capture
ether_type:IP ip_type:UDP ip_proto:17 remote:c0a80188:5353 local:e00000fb:5353
ether_type:0x86DD unknown ip_proto:0 remote:0:0 local:0:0
ether_type:IP ip_type:UDP ip_proto:17 remote:c0a80188:5353 local:e00000fb:5353
ether_type:0x86DD unknown ip_proto:0 remote:0:0 local:0:0
ether_type:IP ip_type:UDP ip_proto:17 remote:c0a80188:5353 local:e00000fb:5353
ether_type:0x86DD unknown ip_proto:0 remote:0:0 local:0:0
ether_type:IP ip_type:UDP ip_proto:17 remote:c0a80188:5353 local:e00000fb:5353
ether_type:0x86DD unknown ip_proto:0 remote:0:0 local:0:0
ether_type:IP ip_type:UDP ip_proto:17 remote:c0a80188:5353 local:e00000fb:5353
ether_type:0x86DD unknown ip_proto:0 remote:0:0 local:0:0
ether_type:IP ip_type:UDP ip_proto:17 remote:c0a80169:49154 local:ffffffff:6666
。。。。
。。。。

 

Leave a Reply

6 + 10 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据