【编程笔记】libpcap应用之保存文件

一、引言

在实际的工作场景中,会遇到需要将捕获的数据包按照文件大小进行保存,比如按照10M、20M、50M、100M乃至自定义大小的方式来存储文件,方便后期工作以及查看。目前开源工具如**tcpdump、tshark**之类的工具均是支持按文件大小来捕获数据包,那如果基于libpcap库如何来手动实现呢?

二、概述

要手动实现上述需求,需要提前了解PCAP文件的格式,PCAP是一种常用的数据报文存储格式,其按照特定的规格存储,一般来说使用普通文本工具打开PCAP文件会显示乱码,查看该类型文件需要使用十六进制工具或者是指定工具来查看,十六进制工具:https://hexed.it/ 在线可以查看,其他工具如:wireshark、010editor(安装pcap插件即可)等等。
在这里插入图片描述

三、文件格式

PCAP文件格式,主要是由文件头 --> 数据包头1 + 数据包1 --> 数据包头2 + 数据包2 --> ...这类格式组成。其中,文件头(Pcap Header)仅包含一个,而数据包头(Packet Header)+数据包(Packet Data)可以包含多个,如下图所示:
在这里插入图片描述
同样,下图更详细展示了每个区域的字节含义与大小情况:
在这里插入图片描述

1.Pcap Header

Pcap Header的数据结构定义如下,总长度是24字节:

typedef struct pcap_hdr_s {
        guint32 magic_number;   /* magic number */
        guint16 version_major;  /* major version number */
        guint16 version_minor;  /* minor version number */
        gint32  thiszone;       /* GMT to local correction */
        guint32 sigfigs;        /* accuracy of timestamps */
        guint32 snaplen;        /* max length of captured packets, in octets */
        guint32 network;        /* data link type */
} pcap_hdr_t;

各个字段含义如下:

header fieldsizeexplain
Magic4B标记文件开始,并用来识别文件和字节顺序
Major2B当前Pcap文件的主要版本号,一般为0x0200
Minor2B当前Pcap文件的次要版本号,一般为0x0400
ThisZone4B当地的标准事件,如果用的是GMT则全零,一般全零
SigFlags4B时间戳的精度,一般为全零
SnapLen4B所抓获的数据包的最大长度
LinkType4B数据链路类型

2.Packet Header

Packet Header的数据结构定义如下,总长度是16字节。

typedef struct pcaprec_hdr_s {
        guint32 ts_sec;         /* timestamp seconds */
        guint32 ts_usec;        /* timestamp microseconds */
        guint32 incl_len;       /* number of octets of packet saved in file */
        guint32 orig_len;       /* actual length of packet */
} pcaprec_hdr_t;

各个字段含义如下:

header fieldsizeexplain
Timestamp4B时间戳高位,精确到seconds
Timestamp4B时间戳低位,能够精确到microseconds
Caplen4B即抓取到的数据帧长度,由此可以得到下一个数据帧的位置
Len4B实际的数据帧长度

3.Packet Data

Packet是链路层的数据帧,其长度是Packet Header中定义的**Caplen**值,所以Packet Data的长度为Caplen,Packet Header定义的结构:

struct pcap_pkthdr {
    struct timeval ts;  /* 时间戳 */
    bpf_u_int32 caplen; /* 捕获的长度 */
    bpf_u_int32 len;    /* 数据包的实际长度 */
};
struct timeval {
    time_t      tv_sec;     /* seconds */
    suseconds_t tv_usec;    /* microseconds */
};

四、实现思路

  • 开启循环捕获数据包,并设定每个文件的保存大小(阈值):X(字节);
  • 在保存数据包至文件的同时,计算文件的大小:Y(字节),即 Y = Pcap Header(24)+ (Packet Header(16)+ Packet Data(Packet Header->caplen)) * N , N表示数据包数量;
  • 当Y大于X,即达到了设定阈值,应关闭当前文件,并新创建文件来接受数据包;
  • 以此,循环采集即可。

参考示例代码:

  • 使用**pcap_loop()**函数循环采集数据报文
int main(int argc, char *argv[]) {
    char device[256];
    char filter[2048];
    char errbuf[PCAP_ERRBUF_SIZE];
    
    *device = 0;
    *filter = 0;

    int option;
    while((option = getopt(argc, argv, "hi:f:w:b:")) != -1) {
        switch (option) {
            case 'h':
                print_help(argv[0]);
                break;
            case 'i':
                strcpy(device, optarg);
                break;
            case 'f':
                strcpy(filter, optarg);
                break;
            case 'w':
                strcpy(filename, optarg);
                break;
            case 'b':
                packet_cnt = atoi(optarg);
                break;
            default:
                abort();
        }
    }
    if (!*device){
         print_help(argv[0]);
    }
    printf("[*]开始采集: %s, 过滤条件: %s , 文件大小(字节): %d\n",device, filter, packet_cnt);
    handle = pcap_open_live(device, 65535, 1, 0, errbuf);
    if (handle == NULL) {
        fprintf(stderr, "Couldn't open device %s: %s\n", "eth0", errbuf);
        return 1;
    }
    dumpfile = pcap_dump_open(handle, filename);
    if (dumpfile == NULL) {
        fprintf(stderr, "Couldn't open dump file %s: %s\n", filename, pcap_geterr(handle));
        return 2;
    }
    printf("[*]开始写入数据到文件: %s\n", filename);
    if (pcap_loop(handle, -1, packet_handler, (u_char*)NULL) == PCAP_ERROR) {
        fprintf(stderr, "[*]Pcap_loop failed: %s\n", pcap_geterr(handle));
        return -1;
    }
    return 0;
}
  • 定义**packet_size_cnt **变量存储当前采集数据包的大小,即是N个数据包【数据包头(16字节)+数据包大小(**packet_header->caplen**)】,如果超过阈值就新建文件接收数据。
void packet_handler(u_char *user, const struct pcap_pkthdr *packet_header, const u_char *packet_data) {
	// 累加每个数据报文的大小
	packet_size_cnt += (packet_header->caplen + 16);
	
	if ( packet_size_cnt < packet_cnt ) {
	    // 如果没有达到设定值就直接写
	    pcap_dump((char *)dumpfile, packet_header, packet_data);
	} else {
	    // 反之,达到设定值,即关闭文件
	    pcap_dump_close(dumpfile);
	    // 重新改变计数的值
	    packet_size_cnt = 24;
	    // 文件名递增的方式
	    file_id += 1;
	    // 生成新的文件名
	    char tmp_fname[1024];
	    size_t buf_size = strlen(filename) + 20 + 1;
	    snprintf(tmp_fname, buf_size,"%s_%d", filename, file_id);
	    // 创建新文件
	    dumpfile = pcap_dump_open(handle, tmp_fname);
	    if (dumpfile == NULL) {
	        fprintf(stderr, "Couldn't open dump file %s: %s\n", tmp_fname, pcap_geterr(handle));
	        exit(EXIT_FAILURE);
	    }
	    // 将越界的数据报文写入新文件
	    printf("[*]开始写入数据到文件: %s\n", tmp_fname);
	    pcap_dump((char *)dumpfile, packet_header, packet_data);
	    packet_size_cnt += (packet_header->caplen + 16);
	}
  • 实现的效果如下,当然在实际应用的场景中还需要考虑更多的因素。
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值