C++ 解析pcap文件

Posted fulianzhou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 解析pcap文件相关的知识,希望对你有一定的参考价值。

pcapParser.h

#ifndef _PCAP_PARSER_H_
#define _PCAP_PARSER_H_

#include <stdint.h>

#pragma pack(1)
//pacp文件头结构体
struct pcap_file_header

    uint32_t magic;       /* 0xa1b2c3d4 */
    uint16_t version_major;   /* magjor Version 2 */
    uint16_t version_minor;   /* magjor Version 4 */
    uint32_t thiszone;      /* gmt to local correction */
    uint32_t sigfigs;     /* accuracy of timestamps */
    uint32_t snaplen;     /* max length saved portion of each pkt */
    uint32_t linktype;    /* data link type (LINKTYPE_*) */
;

//时间戳
struct time_val

    int tv_sec;         /* seconds 含义同 time_t 对象的值 */
    int tv_usec;        /* and microseconds */
;

//pcap数据包头结构体
struct pcap_pkthdr

    struct time_val ts;  /* time stamp */
    uint32_t caplen; /* length of portion present */
    uint32_t len;    /* length this packet (off wire) */
;

// ethnet协议头
struct EthnetHeader_t 

    unsigned char srcMac[6];
    unsigned char dstMac[6];
    uint16_t protoType;
;

//IP数据报头 20字节
struct IPHeader_t

    uint8_t Ver_HLen;       //版本+报头长度
    uint8_t TOS;            //服务类型
    uint16_t TotalLen;       //总长度
    uint16_t ID; //标识
    uint16_t Flag_Segment;   //标志+片偏移
    uint8_t TTL;            //生存周期
    uint8_t Protocol;       //协议类型
    uint16_t Checksum;       //头部校验和
    uint32_t SrcIP; //源IP地址
    uint32_t DstIP; //目的IP地址
;

// UDP头 (8字节)
struct UDPHeader_t

    uint16_t SrcPort;    // 源端口号16bit
    uint16_t DstPort;    // 目的端口号16bit
    uint16_t Length;     // 长度
    uint16_t CheckSum;   // 校验码
;

// TCP头 (20字节)
struct TCPHeader_t 

    uint16_t srcPort;          // 源端口
    uint16_t dstPort;          // 目的端口
    uint32_t SeqNo;            // 序列号
    uint32_t AckNo;            // 确认号
    uint16_t headAndFlags;     // 首部长度即标志位
    uint16_t WinSize;          // 窗口大小
    uint16_t CheckSum;         // 校验和
    uint16_t UrgPtr;           // 紧急指针
;

#pragma pack()

class PcapParser

private:
    char mUdpData[4096];             // 4k缓存
    uint32_t mUdpLen;
    char mTcpData[4096];             // 4k缓存
    uint32_t mTcpLen;
    
    uint32_t mPackIndex;


    void ipDecode(const char* buf);
    void udpDecode(const char* buf, int len);
    void tcpDecode(const char* buf, int len);

public:
    PcapParser() : mUdpLen(0), mTcpLen(0)  
    ~PcapParser()
public:
    // 过滤Ip
    virtual int ipFilter(const char* srcIp, const char* dstIp) return 0;  
    // 过滤端口
    virtual int tcpFilter(const uint16_t srcPort, const uint16_t dstPort, const uint32_t msgLen)  return 0; 
    virtual int udpFilter(const uint16_t srcPort, const uint16_t dstPort, const uint32_t msgLen)  return 0; 
    // udp消息回调
    virtual int onUdpMsg(const char* buf, int len) return 0; 
    // tcp消息回调
    virtual int onTcpMsg(const char* buf, int len) return 0; 

    // pcap文件解析
    void parse(const char* filename);
;








#endif

pcapParser.cpp

#include "pcapParser.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <arpa/inet.h>

void PcapParser::tcpDecode(const char* buf, int len)

    int offset = 0;
    TCPHeader_t* tcpHeader = (TCPHeader_t*)(buf + offset);
    offset += sizeof(TCPHeader_t);

    uint16_t srcPort = tcpHeader->srcPort;
    uint16_t dstPort = tcpHeader->dstPort;
    // 用户数据长度
    uint16_t dataLen = len - sizeof(TCPHeader_t);

    if (0 != tcpFilter(srcPort, dstPort, dataLen))
    
        // tcp过滤
        return;
    

    //printf("[%d]->[%d] len:%d\\n", srcPort, dstPort, dataLen);

    // 存到缓存,用来做粘包,半包处理
    memcpy(mTcpData + mTcpLen, buf + offset, dataLen);
    mTcpLen += dataLen;

    // 用户数据
    int usedLen = onTcpMsg(mTcpData, mTcpLen);
    if (usedLen > 0)
    
        memcpy(mTcpData, mTcpData + usedLen, usedLen);
        mTcpLen -= usedLen;
    


// udp协议解析
void PcapParser::udpDecode(const char* buf, int len)

    int offset = 0;
    UDPHeader_t* udpHeader = (UDPHeader_t*)(buf + offset);
    offset += sizeof(UDPHeader_t);

    uint16_t srcPort = ntohs(udpHeader->SrcPort);
    uint16_t dstPort = ntohs(udpHeader->DstPort);
    uint16_t packLen = ntohs(udpHeader->Length);

    // 用户数据长度
    uint16_t dataLen = packLen - sizeof(UDPHeader_t);

    if (0 != udpFilter(srcPort, dstPort, dataLen))
    
        // udp过滤
        return;
    

    // 存到缓存,用来做粘包,半包处理
    memcpy(mUdpData + mUdpLen, buf + offset, dataLen);
    mUdpLen += dataLen;

    // 用户数据
    int usedLen = onUdpMsg(mUdpData, mUdpLen);
    if (usedLen > 0)
    
        memcpy(mUdpData, mUdpData + usedLen, usedLen);
        mUdpLen -= usedLen;
    


// IP 协议解析
void PcapParser::ipDecode(const char* buf)

    int offset = 0;
    IPHeader_t* ipHeader = (IPHeader_t*)(buf + offset);
    offset += sizeof(IPHeader_t);

    char srcIp[32] =  0 ;
    char dstIp[32] =  0 ;

    inet_ntop(AF_INET, &ipHeader->SrcIP, srcIp, sizeof(srcIp));
    inet_ntop(AF_INET, &ipHeader->DstIP, dstIp, sizeof(dstIp));

    uint16_t ipPackLen = ntohs(ipHeader->TotalLen);

    //printf("[%s]->[%s] proto:%#x ipPackLen=%d packIdx=%d\\n", srcIp, dstIp, ipHeader->Protocol, ipPackLen, mPackIndex);

    if (0 != ipFilter(srcIp, dstIp))
    
        return;
    

    switch (ipHeader->Protocol)
    
        case 17:// UDP协议
            udpDecode(buf + offset, ipPackLen - sizeof(IPHeader_t));
            break;
        case 6: // TCP协议
            tcpDecode(buf + offset, ipPackLen - sizeof(IPHeader_t));
            break;
        default:
            printf("[%s:%d]unsupported protocol %#x\\n", __FILE__, __LINE__,
                   ipHeader->Protocol);
            break;
    


void PcapParser::parse(const char* filename)

    struct stat st;
    if (stat(filename, &st))
    
        printf("stat file %s failed, errno=%d errmsg=%s\\n", filename, errno, strerror(errno));
        return;
    

    size_t fileSize = st.st_size;

    if (!fileSize)
    
        printf("file is empty!\\n");
        return;
    

    char *buf = (char*)malloc(fileSize + 1);

    FILE* fp = fopen(filename, "r");
    if (!fp)
    
        printf("open file %s failed, errno=%d errmsg=%s\\n", filename, errno, strerror(errno));
        return;
    
    fread(buf, sizeof(char), fileSize, fp);
    fclose(fp);


    size_t offset = 0;
    // pcap 文件头
    pcap_file_header* fileHeader = (pcap_file_header*)(buf + offset);
    offset += sizeof(pcap_file_header);
    printf("pcap file - magic:%#x version:%d.%d\\n", fileHeader->magic, fileHeader->version_major, fileHeader->version_minor);

    size_t proto_offset = 0;
    mPackIndex = 0;
    while (offset < fileSize)
    
        // pcap 包头
        pcap_pkthdr* pcapHeader = (pcap_pkthdr*)(buf + offset);
        proto_offset = offset + sizeof(pcap_pkthdr);

        // arp协议头
        EthnetHeader_t* ethHeader = (EthnetHeader_t*)(buf + proto_offset);
        proto_offset += sizeof(EthnetHeader_t);

        uint16_t protocol = ntohs(ethHeader->protoType);

        /**
        printf("[%02x:%02x:%02x:%02x:%02x:%02x]->[%02x:%02x:%02x:%02x:%02x:%02x] proto:%04x\\n",
               ethHeader->srcMac[0], ethHeader->srcMac[1], ethHeader->srcMac[2], ethHeader->srcMac[3], ethHeader->srcMac[4], ethHeader->srcMac[5],
               ethHeader->dstMac[0], ethHeader->dstMac[1], ethHeader->dstMac[2], ethHeader->dstMac[3], ethHeader->dstMac[4], ethHeader->dstMac[5],
               protocol);
        */
        // ip 协议
        if (protocol == 0x0800)
        
            ipDecode(buf + proto_offset);
        
        else
        
            printf("[%s:%d]unsupported protocol %#x\\n", __FILE__, __LINE__,
                   protocol);
        

        offset += (pcapHeader->caplen + sizeof(pcap_pkthdr));
        mPackIndex++;
    

    printf("total package count:%d\\n", mPackIndex);

    if (buf)
    
        free(buf);
        buf = NULL;
    




使用方法,继承PcapParser类,调用parse解析

#include "pcapParser.h"
#include <time.h>
class MsgParser : public PcapParser

private:
    int mCount;
public:
    MsgParser()
    
        mCount = 0;
    

public:
    int getCount()  return mCount; 
    int onUdpMsg(const char* buf, int len)
    
		// do something
        return len;
    
;


int main(int argc, char* argv[])

    if (2 != argc)
    
        printf("usage: %s [PCAP_FILE]\\n", argv[0]);
        return 0;
    

    MsgParser parser;
    parser.parse(argv[1]);
    printf("total quote count:%d\\n", parser.getCount());
    return 0;




以上是关于C++ 解析pcap文件的主要内容,如果未能解决你的问题,请参考以下文章

C++ 解析pcap文件

Python解析pcap文件

pcap文件可读性差

在python中解析pcap文件[重复]

scapy 解析pcap文件总结

如何查看pcap包传输的文件