linux下使用libpcap库

15 篇文章 1 订阅
Libpcap是一个在Unix衍生操作系统中用于数据链路层数据包捕获的开源C库。它广泛应用于tcpdump和snort等数据包捕获工具,确保跨平台兼容性。本文介绍了如何利用libpcap创建简单的数据包嗅探器,并提供了相关参考资料。
摘要由CSDN通过智能技术生成

Libpcap是一个开源C库,它提供了一个API,用于直接从Unix衍生操作系统的数据链路层捕获数据包。它被流行的数据包捕获应用程序(如tcpdump和snort)使用,使它们能够在几乎任何风格的Unix上运行。

下面是一个基于libpcap的简单数据包嗅探器应用程序的示例, 该示例通过libpcap库接收数据链路层frame数据包,重组为TCP数据流,或UDP数据包,带有TCP重传、乱序缓存等功能

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pcap.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <array>
#include <map>
#include <utility>

#pragma GCC diagnostic ignored "-Wunused-variable"


typedef std::array<u_char, 2048> Buf;
uint32_t nextExpectedSeq;
std::map<uint32_t, std::pair<uint32_t, Buf> > seq2UnprocessedMap;

const size_t buflen = 1024*1000;
u_char buf[buflen];
u_char* stream = buf;
u_char* recvp = buf;
uint32_t streamlen = 0;

void my_packet_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
void printHex(const u_char *payload, int payload_length);
size_t processStream(u_char *stream, size_t streamlen);

int main(int argc, char **argv)
{
    const char *filename = "p3p1.20220127.2.pcap";
    char error_buffer[PCAP_ERRBUF_SIZE];
    pcap_t *handle;
    /* End the loop after this many packets are captured */
    int total_packet_count = 20;
    u_char *my_arguments = NULL;
    struct bpf_program filter;
    char filter_exp[] = "dst port 60001";

    // handle = pcap_open_live(device, snapshot_length, 0, 10000, error_buffer);
    // Open the device for live capture, as opposed to reading a packet
    // capture file.
    if ((handle = pcap_open_offline(filename, error_buffer)) == NULL)
    {
        printf("pcap_open_offline(): %s\n", error_buffer);
        return 1;
    }
    if (pcap_compile(handle, &filter, filter_exp, 0, 0) == -1) {
        printf("Bad filter - %s\n", pcap_geterr(handle));
        return 2;
    }
    if (pcap_setfilter(handle, &filter) == -1) {
        printf("Error setting filter - %s\n", pcap_geterr(handle));
        return 2;
    }
    pcap_loop(handle, total_packet_count, my_packet_handler, my_arguments);

    return 0;
}

/* Finds the payload of a TCP/IP packet */
void my_packet_handler(
    u_char *args,
    const struct pcap_pkthdr *header,
    const u_char *packet)
{
    printf("------------------------------------------------------------\n");
    /* First, lets make sure we have an IP packet */
    struct ether_header *eth_header;
    eth_header = (struct ether_header *)packet;
    if (ntohs(eth_header->ether_type) != ETHERTYPE_IP)
    {
        printf("Not an IP packet. Skipping...\n\n");
        return;
    }

    /* The total packet length, including all headers
       and the data payload is stored in
       header->len and header->caplen. Caplen is
       the amount actually available, and len is the
       total packet length even if it is larger
       than what we currently have captured. If the snapshot
       length set with pcap_open_live() is too small, you may
       not have the whole packet. */
    printf("Total packet size: %d\n", header->caplen);
    // printf("Expected packet size: %d bytes\n", header->len);

    /* Pointers to start point of various headers */
    // const u_char *ip_header;
    // const u_char *tcp_header;
    // const u_char *udp_header;
    const u_char *payload;

    /* Header lengths in bytes */
    int ethernet_header_length = 14; /* Doesn't change */
    int ip_header_length;
    int tcp_header_length;
    int udp_header_length;
    int payload_length;

    /* Find start of IP header */
    struct iphdr* ip = (struct iphdr*)(packet + ethernet_header_length);
    /* The second-half of the first byte in ip_header
       contains the IP header length (IHL). */
    ip_header_length = ip->ihl;
    /* The IHL is number of 32-bit segments. Multiply
       by four to get a byte count for pointer arithmetic */
    ip_header_length = ip_header_length * 4;
    // printf("IP header length (IHL) in bytes: %d\n", ip_header_length);

    struct sockaddr_in source;
    struct sockaddr_in dest;
    memset(&source, 0, sizeof(source));
    source.sin_addr.s_addr = ip->saddr;
    memset(&dest, 0, sizeof(dest));
    dest.sin_addr.s_addr = ip->daddr;
    printf("%s -> ", inet_ntoa(source.sin_addr));
    printf("%s\n", inet_ntoa(dest.sin_addr));

    /* Now that we know where the IP header is, we can 
       inspect the IP header for a protocol number to 
       make sure it is TCP before going any further. 
       Protocol is always the 10th byte of the IP header */
    u_char protocol = ip->protocol;
    if (protocol == IPPROTO_TCP)
    {
        /* Add the ethernet and ip header length to the start of the packet
       to find the beginning of the TCP header */
        // tcp_header = packet + ethernet_header_length + ip_header_length;
        struct tcphdr* tcp = (struct tcphdr*)(packet + ethernet_header_length + ip_header_length);
        printf("%d -> %d\n", ntohs(tcp->source), ntohs(tcp->dest));
        // tcp_header_length = tcp_header_length * 4;
        tcp_header_length = tcp->doff * 4;
        printf("TCP header length in bytes: %d\n", tcp_header_length);
        printf("TCP seq: %u\n", ntohl(tcp->seq));

        /* Add up all the header sizes to find the payload offset */
        int total_headers_size = ethernet_header_length + ip_header_length + tcp_header_length;
        // printf("Size of all headers combined: %d bytes\n", total_headers_size);
        payload_length = header->caplen -
                         (ethernet_header_length + ip_header_length + tcp_header_length);
        printf("Payload size: %d\n", payload_length);
        if( payload_length < 0)
        {
            printf("Payload size is less than zero!!!\n");
            return;
        }
        payload = packet + total_headers_size;
        // printf("Memory address where payload begins: %p\n\n", payload);

        /* Print payload in ASCII */
        // printHex(payload, payload_length);

        uint32_t tcpseq = ntohl(tcp->seq);
        uint32_t tcplen = tcp->doff * 4;
        uint32_t headerlen = total_headers_size;
        const u_char* data = packet + headerlen;
        uint32_t datalen = payload_length;
        bool syn = (tcp->syn != 0);
        bool fin = (tcp->fin != 0);
        bool rst = (tcp->rst != 0);
        bool psh = (tcp->psh != 0);

        if(syn)
        {
            printf("SYN, will reset the next expected seq:%u\n", nextExpectedSeq);
            seq2UnprocessedMap.clear();
            nextExpectedSeq = 0;
        }
        else
        {
            nextExpectedSeq = tcpseq;
        }

        // if(datalen == 0)
        // {
        //     return;
        // }

        if(nextExpectedSeq !=0  && tcpseq != nextExpectedSeq)
        {
            printf("Arrived seq %u is not expected %u\n", tcpseq, nextExpectedSeq);
            if(seq2UnprocessedMap.find(tcpseq) != seq2UnprocessedMap.end())
            {
                printf("Duplicated packet %u\n", tcpseq);
            }
            else
            {
                Buf buf;
                memcpy(buf.data(), data, datalen);
                seq2UnprocessedMap.emplace(tcpseq, std::make_pair(datalen, buf));
            }
            return;
        }

        if(buflen - streamlen < datalen)
        {
            printf("Not enough buf, exit!!!\n");
            exit(1);
        }

        // move unprocessed stream from buf tail to head
        if(buf + buflen - recvp < datalen)
        {
            memcpy(buf, stream, streamlen);
            stream = buf;
            recvp = buf + streamlen;
        }

        //
        memcpy(recvp, data, datalen);
        recvp += datalen;
        streamlen += datalen;

        nextExpectedSeq = tcpseq + datalen;

        // try to assemble data in cache
        while(seq2UnprocessedMap.size() > 0 && seq2UnprocessedMap.find(nextExpectedSeq) != seq2UnprocessedMap.end())
        {
            const auto& datapair = seq2UnprocessedMap[nextExpectedSeq];
            size_t datasize = datapair.first;

            if(buflen - streamlen < datalen)
            {
                printf("Not enough buf, break!!!\n");
                break;
            }

            // move unprocessed stream from buf tail to head
            if(buf + buflen - recvp < datalen)
            {
                memcpy(buf, stream, streamlen);
                stream = buf;
                recvp = buf + streamlen;
            }

            memcpy(recvp, datapair.second.data(), datasize);
            recvp += datasize;
            streamlen += datasize;

            seq2UnprocessedMap.erase(nextExpectedSeq);
            nextExpectedSeq += datasize;
        }

		// process assembled stream
        size_t res = processStream(stream, streamlen);
        stream += res;
        streamlen -= res;

    }
    else if (protocol == IPPROTO_UDP)
    {
        // udp_header = packet + ethernet_header_length + ip_header_length;
        struct udphdr* udp = (struct udphdr*)(packet + ethernet_header_length + ip_header_length);
        printf("%d -> %d\n", ntohs(udp->source), ntohs(udp->dest));
        udp_header_length = 8;
        int total_headers_size = ethernet_header_length + ip_header_length + udp_header_length;
        payload_length = header->caplen -
                         (ethernet_header_length + ip_header_length + udp_header_length);
        payload = packet + total_headers_size;
        /* Print payload in ASCII */
        printHex(payload, payload_length);
    }
    else
    {
        printf("Not a TCP/UDP packet. Skipping...\n\n");
    }
    return;
}

void printHex(const u_char *payload, int payload_length)
{
    if (payload_length > 0)
    {
        const u_char *temp_pointer = payload;
        int byte_count = 0;
        while (byte_count++ < payload_length)
        {
            printf("%02x", *temp_pointer);
            temp_pointer++;
            if ((byte_count & 0x0F) == 0)
            {
                printf("\n");
            }
            else
            {
                printf(" ");
            }
        }
        printf("\n");
    }
}

size_t processStream(u_char *stream, size_t streamlen)
{
    if(streamlen < sizeof(MDQPHdr))
    {
        return 0;
    }
    MDQPHdr* hdr = reinterpret_cast<MDQPHdr*>(stream);
    size_t bodylen = hdr->Length;
    printf("[MDQPHdr] Flag:%02x, TypeID:%02x, Length:%u, RequestID:%d\n", hdr->Flag, hdr->TypeID, bodylen, hdr->RequestID);
    
    size_t packetlen = bodylen + sizeof(MDQPHdr);
    if(packetlen > streamlen)
    {
        return 0;
    }
    else
    {
        return packetlen;
    }
}

参考:
https://www.tcpdump.org/index.html
https://vichargrave.github.io/programming/develop-a-packet-sniffer-with-libpcap/#tcp-and-udp-header-parsing
https://www.devdungeon.com/content/using-libpcap-c

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值