You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Libnet在C语言中创建UDP分片数据包的问题排查

使用Libnet 1.2实现UDP数据包分片的正确方法

问题核心

你遇到的问题本质是Libnet 1.2不会自动处理IP分片,仅设置MF标志无法触发分片行为,必须手动拆分UDP数据报并构建多个独立的IP分片包。你的代码存在三个关键错误:

  1. 有效载荷仅32字节,远小于常规MTU(1500字节),不满足分片触发条件
  2. 分片偏移字段设置错误(128对应1024字节偏移,与实际数据长度不匹配)
  3. 未手动拆分数据并生成多个分片包

分片实现的关键规则

IP分片必须遵循以下规则:

  • 所有分片使用相同的IP ID,接收端通过ID重组数据包
  • 分片偏移字段单位为8字节,需计算当前分片相对于UDP数据报起始位置的偏移量(除以8后转网络字节序)
  • 除最后一个分片外,其余分片需设置MF(更多分片)标志(htons(0x2000))
  • 每个分片的IP负载(IP头之后的内容)长度必须是8字节的整数倍,且不超过MTU - IP头长度(常规MTU=1500,IP头=20,所以最大负载1480字节)

正确实现步骤与代码示例

以下是完整的UDP分片实现代码,以2000字节的大有效载荷为例:

#include <libnet.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MTU 1500
#define IP_HEADER_LEN LIBNET_IPV4_H
#define UDP_HEADER_LEN LIBNET_UDP_H
// 第一个分片包含UDP头,最大负载为MTU-IP头-UDP头(需8字节对齐)
#define FIRST_FRAG_MAX (MTU - IP_HEADER_LEN - UDP_HEADER_LEN)
// 后续分片仅包含payload,最大负载为(MTU-IP头)向下取整到8字节倍数
#define SUBSEQ_FRAG_MAX ((MTU - IP_HEADER_LEN) / 8 * 8)

int main(int argc, char *argv[]) {
    libnet_t *l;
    char errbuf[LIBNET_ERRBUF_SIZE];
    uint16_t src_port = 12345;
    uint16_t dest_port = 54321;
    const char *source_ip = "192.168.1.100";
    const char *dest_ip = "192.168.1.200";

    // 初始化Libnet上下文
    l = libnet_init(LIBNET_RAW4, NULL, errbuf);
    if (l == NULL) {
        fprintf(stderr, "Libnet init failed: %s\n", errbuf);
        return 1;
    }

    // 准备大有效载荷(2000字节,超过MTU)
    char large_payload[2000];
    memset(large_payload, 'A', sizeof(large_payload));
    int total_udp_length = UDP_HEADER_LEN + sizeof(large_payload);
    uint16_t ip_id = libnet_get_prand(LIBNET_PR16); // 所有分片共用的IP ID
    int current_offset = 0;
    int frag_count = 0;

    while (current_offset < total_udp_length) {
        int frag_data_len;
        uint16_t ip_flags = htons(0x2000); // 默认设置MF标志
        uint8_t *frag_payload = NULL;

        if (current_offset == 0) {
            // 第一个分片:包含完整UDP头
            frag_data_len = MIN(total_udp_length - current_offset, FIRST_FRAG_MAX + UDP_HEADER_LEN);
            // 构建UDP头及对应payload片段
            libnet_build_udp(
                src_port,
                dest_port,
                total_udp_length, // UDP总长度(整个包的长度)
                0,
                (uint8_t*)large_payload,
                frag_data_len - UDP_HEADER_LEN,
                l,
                0
            );
        } else {
            // 后续分片:仅包含payload片段
            frag_data_len = MIN(total_udp_length - current_offset, SUBSEQ_FRAG_MAX);
            frag_payload = (uint8_t*)large_payload + (current_offset - UDP_HEADER_LEN);
        }

        // 检查是否为最后一个分片,清除MF标志
        if (current_offset + frag_data_len >= total_udp_length) {
            ip_flags = 0;
        }

        // 计算分片偏移(单位:8字节,转网络字节序)
        uint16_t frag_offset = htons((current_offset / 8) & 0x1FFF); // 仅保留低13位

        // 构建IP头
        libnet_build_ipv4(
            IP_HEADER_LEN + frag_data_len,
            0,
            ip_id, // 所有分片使用相同ID
            ip_flags | frag_offset, // 标志位 + 分片偏移
            64, // TTL值
            IPPROTO_UDP,
            0,
            libnet_name2addr4(l, source_ip, LIBNET_RESOLVE),
            libnet_name2addr4(l, dest_ip, LIBNET_RESOLVE),
            frag_payload,
            (current_offset == 0) ? 0 : frag_data_len, // 非首分片需指定数据长度
            l,
            0
        );

        // 发送当前分片
        int sent_bytes = libnet_write(l);
        if (sent_bytes == -1) {
            fprintf(stderr, "Failed to send fragment %d: %s\n", frag_count, libnet_geterror(l));
        } else {
            printf("Sent fragment %d, %d bytes\n", frag_count, sent_bytes);
        }

        // 清空Libnet上下文,准备下一个分片
        libnet_clear_packet(l);
        current_offset += frag_data_len;
        frag_count++;
    }

    libnet_destroy(l);
    return 0;
}

关键注意事项

  • 必须确保每个分片的负载长度是8字节的整数倍,否则接收端无法正确重组
  • 所有分片的IP ID必须一致,否则接收端会将其视为独立数据包
  • 首分片必须包含完整的UDP头,后续分片仅携带payload片段
  • 发送完每个分片后需调用libnet_clear_packet()重置上下文,避免不同分片的数据混淆

内容的提问来源于stack exchange,提问作者okncskn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 11:25:57