如何在IPv4数据包中添加选项?C语言网络工具开发疑问
实现IPv4选项的正确姿势
首先你找到的struct options是Linux内核内部用来管理IP选项的结构体,用户态开发自定义数据包根本用不上,别在这上面耗时间。
要给struct iphdr加IPv4选项,核心是直接构造符合IPv4协议规范的选项字节流,拼在IP头部后面,同时调整IP头部的ihl字段就行——IP头部长度是4字节的倍数,默认无选项时ihl是5(对应20字节),每加4字节选项就把ihl加1。
步骤1:先搞懂IPv4选项的格式
每个IPv4选项都遵循这个结构:
- 1字节:选项类型(Type)
- 1字节:选项长度(Length,包含Type和Length本身)
- N字节:选项数据(Data,可选)
常见的实用选项比如:
- 记录路由(Record Route,类型0x07)
- 时间戳(Timestamp,类型0x44)
- 源路由(Source Route,类型0x83严格路由/0x03宽松路由)
步骤2:手动构造IP选项字节流
举个记录路由选项的例子,我们预留3个IP地址的位置:
// 记录路由选项:类型0x07,总长度14字节(1+1+3*4) unsigned char ip_options[] = { 0x07, // 选项类型:记录路由 0x0E, // 选项长度:14字节 0x00, // 指针:初始指向第一个待填充的IP位置 0x00, 0x00, 0x00, 0x00, // 预留IP位置1 0x00, 0x00, 0x00, 0x00, // 预留IP位置2 0x00, 0x00, 0x00, 0x00 // 预留IP位置3 };
步骤3:和struct iphdr组装成完整数据包
初始化IP头部并调整ihl:
struct iphdr ip_hdr; // 先填IP头部的常规字段 ip_hdr.version = 4; ip_hdr.ttl = 64; ip_hdr.protocol = IPPROTO_TCP; // 假设是TCP数据包,按需改 ip_hdr.saddr = inet_addr("192.168.1.100"); // 源IP ip_hdr.daddr = inet_addr("192.168.1.200"); // 目的IP // 处理选项的4字节对齐:不够的话用填充选项(0x00)补全 int opt_len = sizeof(ip_options); int padded_opt_len = (opt_len + 3) & ~3; // 向上取整到4的倍数 unsigned char padded_options[padded_opt_len]; memcpy(padded_options, ip_options, opt_len); memset(padded_options + opt_len, 0, padded_opt_len - opt_len); // 补填充 // 计算ihl:(基础IP头部20字节 + 对齐后的选项长度) /4 ip_hdr.ihl = (20 + padded_opt_len) / 4;拼接IP头部、选项和数据:
int data_len = 100; // 假设数据长度是100字节,按需改 unsigned char *data = malloc(data_len); // 填充数据内容... int total_packet_len = sizeof(struct iphdr) + padded_opt_len + data_len; unsigned char *packet = malloc(total_packet_len); if (!packet) { /* 内存分配失败,做错误处理 */ } // 依次拷贝IP头部、选项、数据 memcpy(packet, &ip_hdr, sizeof(struct iphdr)); memcpy(packet + sizeof(struct iphdr), padded_options, padded_opt_len); memcpy(packet + sizeof(struct iphdr) + padded_opt_len, data, data_len);计算IP校验和:
校验和要覆盖整个IP头部(包括选项),所以计算时要传整个头部的长度:// 先清零校验和字段 ip_hdr.check = 0; // 计算校验和,ip_checksum是自定义的校验和函数 ip_hdr.check = ip_checksum((unsigned short *)&ip_hdr, ip_hdr.ihl * 4); // 把更新后的IP头部拷贝回数据包 memcpy(packet, &ip_hdr, sizeof(struct iphdr));
几个必须注意的点
- 选项必须4字节对齐:IP头部总长度必须是4字节的倍数,不够的话一定要用填充选项(类型0x00)补全,不然数据包会被网络设备直接丢弃。
- ihl字段不能错:ihl是IP头部(含选项)的长度除以4,算错了会导致后续解析出错。
- 用RAW套接字发送:用户态发送自定义数据包必须创建
SOCK_RAW类型的套接字,还要设置IP_HDRINCL选项,这样内核才不会自动生成IP头部,让你完全自定义。
内容的提问来源于stack exchange,提问作者Pedro Henrique
相关产品推荐
相关产品推荐

