IPv6原始套接字发送UDP数据报出现bad length问题求助
从你描述的tcpdump输出(UDP, bad length 12280 > 40)和代码片段来看,问题大概率出在数据报长度字段的错误设置或者sendto调用时的长度参数错误,结合IPv6原始套接字的特性,下面是具体的排查点和修复方案:
1. IPv6头部的有效载荷长度(payload length)设置错误
IPv6头部的ip6_plen字段表示的是IPv6头部之后的所有数据长度(包括UDP头部和UDP数据),而不是整个数据报的总长度(IPv6头部固定40字节)。如果你错误地将整个datagram的长度(sizeof(struct ip6_hdr) + sizeof(struct udphdr) + data_len)赋值给ip6_plen,内核或抓包工具就会计算出错误的有效载荷长度,导致"bad length"提示。
正确的设置应该是:
struct ip6_hdr *ip6hdr = (struct ip6_hdr *)datagram; ip6hdr->ip6_plen = htons(sizeof(struct udphdr) + data_len); // 仅UDP头+数据的长度
2. UDP头部的长度字段错误
UDP头部的uh_ulen字段需要设置为UDP头部+UDP数据的总长度(同样以字节为单位),并且要转换为网络字节序。如果你忘记设置这个字段,或者设置的值远大于实际数据长度(比如你代码里的12280),tcpdump就会认为长度不合法。
正确的设置:
struct udphdr *udphdr = (struct udphdr *)(datagram + sizeof(struct ip6_hdr)); udphdr->uh_ulen = htons(sizeof(struct udphdr) + data_len);
3. IPV6_CHECKSUM套接字选项的偏移量设置错误
当使用IPV6_CHECKSUM选项时,你需要告诉内核校验和字段在你发送的数据中的偏移位置。对于UDP来说,校验和字段在UDP头部的第6-7字节(偏移量为sizeof(struct ip6_hdr) + offsetof(struct udphdr, uh_sum)),如果你设置的offset不正确,内核无法正确计算校验和,也可能间接导致长度解析异常。
正确的设置方式:
offset = sizeof(struct ip6_hdr) + offsetof(struct udphdr, uh_sum); retval = setsockopt(sockfd, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset));
注意:设置IPV6_CHECKSUM后,你需要将UDP的uh_sum字段初始化为0,内核会自动填充正确的校验和。
4. sendto调用的长度参数错误
在调用sendto时,传入的长度应该是整个数据报的总长度(IPv6头部+UDP头部+数据),如果你错误地传入了过大的值(比如12280),超过了实际数据的长度,就会导致tcpdump捕获到异常长度的数据包。
正确的sendto调用:
ssize_t sent_len = sendto(sockfd, datagram, sizeof(struct ip6_hdr) + sizeof(struct udphdr) + data_len, 0, rp->ai_addr, rp->ai_addrlen);
额外提醒:原始套接字的权限与协议设置
确保你创建原始套接字时指定了正确的协议:
sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
并且运行程序时有足够的权限(比如root用户),否则可能会出现发送失败或数据报被截断的情况。
内容的提问来源于stack exchange,提问作者weeeez21

