修改捕获数据包的头部

Has*_*han 5 c linux network-programming netfilter linux-kernel

我正在尝试修改 IP 标头以包含更多 IP 选项,并使用libnetfiletr_queue. 到目前为止,我已经设法获得如下所示的数据包。

if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
    fprintf(stderr, "Unable to set nfq_set_mode\n");
    exit(1);
}
Run Code Online (Sandbox Code Playgroud)

然后我设法走得很远,如下图所示,

static int my_callBack(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,struct nfq_data *tb)
{   
    int id = 0;
    int packet_len;
    unsigned char *data;
    struct nfqnl_msg_packet_hdr *packet_hdr;
    unsigned char *data;

    packet_hdr = nfq_get_msg_packet_hdr(tb);

    if (packet_hdr) {
        id = ntohl(packet_hdr->packet_id);          
    }

    packet_len = nfq_get_payload(tb, &data);

    if (packet_len >= 0) {
        //print payload length
        printf("payload_length = %d ", packet_len);
        //modify packet ip header  
    }

    return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
}
Run Code Online (Sandbox Code Playgroud)

但是从这里开始,我对如何IP header//modify packet ip header注释处修改捕获的数据包感到有些困惑。修改 IP 标头的示例(例如流量类(IPV6)/ IP 选项/版本/标志/目标地址)没关系,因为我只需要了解修改的工作原理:)。

我尝试了很多资源,但无法继续进行下去。非常感谢您对此查询的专家建议和帮助。:)

非常感谢 :)

awi*_*man 4

要修改 IP 标头的值,请首先定义一个结构来表示标头。您可以通过阅读您尝试访问的协议的 RFC 规范来了解其结构。

以下是 IPv6 RFC 的链接: https://www.rfc-editor.org/rfc/rfc2460#section-3

IPv6 标头的第一行有点棘手,因为它们不使用字节对齐字段。版本字段为 4 位宽,流量类别为 8 位宽,流标签为 20 位宽。整个标头有 320 位(40 字节),其中 256 位是 src 和 dest 地址。其他字段仅使用 64 位,因此像这样定义结构可能是最简单的:

struct ipv6_hdr {
    uint32_t row1;
    uint16_t payload_length;
    uint8_t next_header;
    uint8_t hop_limit;
    uint16_t src[8];
    uint16_t dest[8];
};
Run Code Online (Sandbox Code Playgroud)

要提取第一行值,您可以使用一些掩码:

#define VERSION_MASK 0xF0000000
#define TRAFFIC_CLASS_MASK 0x0FF00000
#define FLOW_LABEL_MASK 0x000FFFFF

struct ipv6_hdr foo;

...

nfq_get_payload(tb, &foo); // Just an example; don't overflow your buffer!

// bit-wise AND gets masked field from row1
uint8_t version = (uint8_t) ((foo->row1 & VERSION_MASK) >> 28);  // shift (32-4) bits
Run Code Online (Sandbox Code Playgroud)

一旦您将结构指向数据有效负载,假设您的字节数组与此格式匹配,修改标头值就变得简单赋值:

version = 6;

// bit-wise OR puts our value in the right place in row1
foo->row1 &= ~(VERSION_MASK) // clear out the old value first
foo->row1 = ((uint32_t) version << 28) | foo->row1;  
Run Code Online (Sandbox Code Playgroud)

我选择将结构体中的 src 和 dest 地址设为 16 位值的数组,因为 IPv6 地址是一系列 8、16 位值。这应该可以轻松隔离任何给定的字节对。

在对其应用正确的结构之前,您必须确定数据有效负载的格式。

有关如何创建 IPv4 标头的信息,请查看其 RFC: https: //www.rfc-editor.org/rfc/rfc791#section-3.1

希望这会有所帮助(您可能需要摆弄我的代码示例才能获得正确的语法,已经过去了几个月)。


根据评论中的要求编辑有关校验和的信息

按照此 RFC 在修改标头后生成校验和: https://www.rfc-editor.org/rfc/rfc1071

关键要点是在生成新的校验和之前将标头中的校验和字段清零。