C语言写PCAP文件

0 背景介绍

对于做网络的工程师,pcap文件是经常接触的,通常抓包之后保存的报文即使pcap文件,用来分析定位问题是非常方便的。这篇文章介绍下pcap文件格式、报文格式以及如何将一个报文写入到pcap文件的c语言实现。

  1. pcap文件简介

Pcap文件是最常用的数据报文存储格式,可以理解为一种文件格式。Pcap文件用记事本打开会显示乱码,对于pcap文件有特定的打开工具,例如最常用的wireshark软件,可以用wireshark打开来分析一个pcap文件的内容。

对于一个做网络相关开发的工程师,会分析pcap文件是非常有用的,对我们开发及定位问题非常有用。通常我们可以使用wireshark、tcpdump等工具抓包,抓取到的报文可以存储为pcap文件,分析报文用于确定问题原因。

Pcap的文件格式也比较简单,大致为:pcap文件头-报文1头-报文1数据……报文n头-报文n数据,如下图:

    1. Pcap Header

文件头,每一个pcap文件只有一个文件头,总共占24(B)字节,以下是总共7个字段的含义,格式如下图:

每个字段的含义如下:

Magic(4B):标记文件开始,并用来识别文件和字节顺序。值可以为0xa1b2c3d4或者0xd4c3b2a1,如果是0xa1b2c3d4表示是大端模式,按照原来的顺序一个字节一个字节的读,如果是0xd4c3b2a1表示小端模式,下面的字节都要交换顺序。现在的电脑大部分是小端模式;

Major(2B):当前文件的主要版本号,一般为0x0200;

Minor(2B):当前文件的次要版本号,一般为0x0400;

ThisZone(4B):当地的标准事件,如果用的是GMT则全零,一般全零;

SigFigs(4B):时间戳的精度,一般为全零;

SnapLen(4B):最大的存储长度,设置所抓获的数据包的最大长度,如果所有数据包都要抓获,将值设置为65535;

LinkType(4B):链路类型。解析数据包首先要判断它的LinkType,所以这个值很重要。一般的值为1,即以太网。常用的LinkType(链路类型),如下:

值 类型描述

0 BSD loopback devices, except for later OpenBSD

1 Ethernet, and Linux loopback devices

6 802.5 Token Ring

7 ARCnet

8 SLIP

9 PPP

10 FDDI

100 LLC/SNAP-encapsulated ATM

101 “raw IP”, with no link

102 BSD/OS SLIP

103 BSD/OS PPP

104 Cisco HDLC

105 802.11

108 later OpenBSD loopback devices (with the AF_value in network byte order)

113 special Linux “cooked” capture

114 LocalTalk

    1. packet Header

数据包头可以有多个,每个数据包头后面都跟着真正的数据包。以下是Packet Heade的格式及4个字段含义:

Timestamp(4B):时间戳高位,精确到seconds,这是Unix时间戳。捕获数据包的时间一般是根据这个值;

Timestamp(4B):时间戳低位,能够精确到microseconds;

Caplen(4B):当前数据区的长度,即抓取到的数据帧长度,由此可以得到下一个数据帧的位置;

Len(4B):离线数据长度,网路中实际数据帧的长度,一般不大于Caplen,多数情况下和Caplen值一样。

    1. Packet Data

Packet是链路层的数据帧,长度就是Packet Header中定义的Caplen值,所以每个Packet Header后面都跟着Caplen长度的Packet Data。也就是说pcap文件并没有规定捕获的数据帧之间有什么间隔字符串。Packet数据帧部分的格式就是标准的网络协议格式了。

  1. 报文格式简介

最简单的tcp报文,不考虑ip option字段、分片等各种复杂的情况,最简单的报文封装如下图:

最终收发的报文都是一个封装好的以太帧,我们写入pcap文件的data即是将一个以太帧写入pcap文件。

  1. C语言写pcap文件
    1. libpcap库

libpcap(Packet Capture Library)即数据包捕获函数库,是Unix/Linux平台下的网络数据包捕获函数库。它是一个独立于系统的用户层包捕获的API接口,为底层网络监测提供了一个可移植的框架。著名的软件TCPDUMP就是在libpcap的基础上开发而成的。

libpcap可以实现以下功能:

- 数据包捕获:捕获流经网卡的原始数据包

- 自定义数据包发送:任何构造格式的原始数据包

- 流量采集与统计:网络采集的中流量信息

- 规则过滤:提供自带规则过滤功能,按需要选择过滤规则

Libpcap库带有写pcap文件的功能,可以使用libpcap库的相关接口实现pcap文件的写入,相关接口如下:

  1. pcap_open_dead
  2. pcap_dump_open
  3. pcap_dump
  4. pcap_dump_flush
  5. pcap_dump_close

libpcap库提供的功能比较多,如果只写pcap文件可以直接写,参考libpcap文件的源码及pcap文件格式,很容易将一个报文写入pcap文件。

    1. c语言写pcap文件

使用c语言直接写pacao文件,代码量也不大,当我们只需要写pcap文件的功能的时候,可以直接将报文写入pcap文件(跟libpcap实现其实是相同的),以一个最简单的tcp报文为例,源码如下:

#include 
#include 
#include 

#define FILE_NAME_LEN 64

char g_pcap_filename[FILE_NAME_LEN] = {0};
FILE *g_pcap_file = NULL;

/*
 * Standard libpcap format.
 */
#define TCPDUMP_MAGIC   0xa1b2c3d4
#define PCAP_VERSION_MAJOR 2
#define PCAP_VERSION_MINOR 4

struct pcap_file_header {
    unsigned int magic;
    unsigned short version_major;
    unsigned short version_minor;
    unsigned int thiszone;          /* gmt to local correction */
    unsigned int sigfigs;           /* accuracy of timestamps */
    unsigned int snaplen;           /* max length saved portion of each pkt */
    unsigned int linktype;          /* data link type (LINKTYPE_*) */
};

struct pcap_timeval {
    int tv_sec;     /* seconds */
    int tv_usec;    /* microseconds */
};

struct pcap_sf_pkthdr {
    struct pcap_timeval ts;     /* time stamp */
    unsigned int caplen;        /* length of portion present */
    unsigned int len;           /* length of this packet (off wire) */
};

int pcap_init(const char *file)
{
    size_t size = 0;
    struct pcap_file_header pcap_filehdr;

    memset(&pcap_filehdr, 0, sizeof(pcap_filehdr));
    pcap_filehdr.magic = TCPDUMP_MAGIC;
    pcap_filehdr.version_major = PCAP_VERSION_MAJOR;
    pcap_filehdr.version_minor = PCAP_VERSION_MINOR;
    pcap_filehdr.thiszone = 0;
    pcap_filehdr.sigfigs  = 0;
    pcap_filehdr.snaplen  = 65535;
    pcap_filehdr.linktype = 1;

    if ('\0' == file[0]) {
        return 0;
    }

    g_pcap_file = fopen(file, "wb");
    if (!g_pcap_file) {
        printf("Open pcap file failed\n");
        return -1;
    }

    size = sizeof(pcap_filehdr);
    if (fwrite(&pcap_filehdr, size, 1, g_pcap_file) != 1) {
        printf("Write pcapfile header failed\n");
        fclose(g_pcap_file);
        return -1;
    }

    return 0;
}

int pcap_write(const char *buf, const unsigned int len)
{
    struct pcap_sf_pkthdr pkthdr;
    clock_t time;

    time = clock();
    memset(&pkthdr, 0, sizeof(pkthdr));
    pkthdr.ts.tv_sec = time / CLOCKS_PER_SEC;
    pkthdr.ts.tv_usec = time % CLOCKS_PER_SEC;
    pkthdr.caplen = len;
    pkthdr.len = pkthdr.caplen;

    if (g_pcap_file) {
        if (fwrite(&pkthdr, sizeof(pkthdr), 1, g_pcap_file) != 1) {
            printf("Fwrite packet header failed.\n");
            return -1;
        }
        if (fwrite(buf, (size_t)len, 1, g_pcap_file) != 1) {
            printf("Fwrite data failed.\n");
            return -1;
        }
    }

    return 0;
}

void pcap_clean(void)
{
    if (g_pcap_file) {
        fclose(g_pcap_file);
        g_pcap_file = NULL;
    }

    return;
}

int main()

{
    char file[FILE_NAME_LEN] = "test.pcap";
    char buf[2048] = {0x00, 0x50, 0x56, 0xe4, 0xa4, 0x69, 0x00, 0x0c, 0x29, 0xde, 0x31, 0xa6, 0x08, 0x00, 0x45, 0x00,          0x00, 0x3c, 0xa1, 0x78, 0x40, 0x00, 0x40, 0x06, 0xe7, 0x37, 0xc0, 0xa8, 0x3a, 0x80, 0x5b, 0xbd,        0x5b, 0x26, 0xe8, 0xfa, 0x00, 0x50, 0x5b, 0xcb, 0xb3, 0x62, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02,        0xfa, 0xf0, 0xb2, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x7c, 0x2b,
  0xa0, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07};

    if(0 != pcap_init(file)) {
        printf("Pcap init failed.\n");
        return -1;
    }
    if(0 != pcap_write(buf, 74)) {
        printf("Pcap write failed.\n");
        return -1;
    }
    pcap_clean();

    return 0;
}

此代码中,直接将一个buf[2048]写入了pcap文件,通常会将一个skb写入pcap文件(关于skb的详细解释后续将写文字介绍)。其实都是大同小异的。

    1. 结果分析

编译运行上面的代码,这个报文便写入了test.pcap的pcap的文件,现在我们分别用wireshark和vim(16进制)打开分析一下此文件。

从wireshark的解析,可以获取到非常详细的报文信息,可以看到最下面的16进制报文显示,既是代码中的buf值。

接下来将test.pcap以16进制打开,分析如下:

可以清楚的看到,蓝色框既是pcap header;黄色框是packet header;红色框是data。

  1. 总结

至此,pcap文件格式以及如何将一个报文写入pcap文件分析完毕,pcap文件分析报文对于网络工程师是非常有用的,希望此文章对大家有一定的帮助。

你可能感兴趣的:(c语言)