如何在C语言中从IPv6套接字读取HTTP头并提取请求URL
问题根因
你现有代码的异常主要来自三个核心问题:
- 未处理IPv6扩展头:IPv6基本头之后可能跟随0~N个扩展头(逐跳选项、路由头、分片头、认证头、封装安全载荷头、目的选项头等),你直接偏移
sizeof(struct ipv6hdr)就找TCP头的逻辑,遇到带扩展头的报文必然偏移错误 - 直接用
printk("%s", data)输出payload:HTTP payload不是以'\0'结尾的C字符串,且你没有校验data指针是否在skb的线性区域内,超出范围就会读到无效内存,出现乱码甚至打印data is 0的情况 - 缺少协议合法性校验:你默认报文是TCP协议,没有判断IPv6头的nexthdr字段,遇到UDP/ICMPv6报文也会按TCP解析,逻辑完全错误
IPv6扩展头遍历方法
内核已经提供了标准的IPv6扩展头遍历接口ipv6_skip_exthdr,不需要手动解析,该函数会自动跳过所有合法扩展头,返回传输层头的偏移量,同时输出最终的传输层协议类型。
修正后的完整代码
#include <linux/ipv6.h> #include <linux/tcp.h> #include <linux/skbuff.h> #include <linux/string.h> static int sc_get_http_info(struct sk_buff *skb) { struct ipv6hdr *ip6h; struct tcphdr *tcph; unsigned char *data; unsigned int datalen, transport_off; int nexthdr; /* 校验IPv6报文合法性 */ if (!skb) return -EINVAL; ip6h = ipv6_hdr(skb); if (ip6h->version != 6) return -EPROTONOSUPPORT; /* 遍历所有IPv6扩展头,获取传输层头偏移 */ nexthdr = ip6h->nexthdr; transport_off = sizeof(struct ipv6hdr); transport_off = ipv6_skip_exthdr(skb, transport_off, &nexthdr); if (transport_off < 0) return -EINVAL; // 扩展头解析错误 /* 仅处理TCP报文,需要UDP场景替换为IPPROTO_UDP即可 */ if (nexthdr != IPPROTO_TCP) return -EPROTONOSUPPORT; /* 校验TCP头是否在skb线性区内,避免越界访问 */ if (!pskb_may_pull(skb, transport_off + sizeof(struct tcphdr))) return -ENODATA; tcph = (struct tcphdr *)(skb_network_header(skb) + transport_off); /* 校验TCP头长度合法性 */ if (tcph->doff * 4 < sizeof(struct tcphdr)) return -EINVAL; /* 计算HTTP payload的起始偏移和长度 */ unsigned int http_off = transport_off + tcph->doff * 4; datalen = skb->len - http_off; if (datalen <= 0) return -ENODATA; // 无应用层payload /* 校验payload可读,避免越界 */ if (!pskb_may_pull(skb, http_off + 1)) return -ENODATA; data = skb_network_header(skb) + http_off; /* 打印payload不要用%s,指定长度输出避免内存越界 */ printk("data len: %u, first 32 bytes: %*ph\n", datalen, min(datalen, 32U), data); /* 提取URL逻辑示例:匹配HTTP请求行,提取第二字段 */ if (datalen >= 4 && !strncmp(data, "GET ", 4)) { unsigned char *url_start = data + 4; unsigned char *url_end = memchr(url_start, ' ', datalen - 4); if (url_end) { int url_len = url_end - url_start; printk("HTTP URL: %*s\n", url_len, url_start); } } return 0; }
注意事项
- 分片报文处理:如果遇到IPv6分片报文,仅首分片携带传输层头,后续分片没有HTTP头,需要完整解析的场景需要先做分片重组
- HTTPS报文:HTTPS payload是加密的,无法直接解析出明文URL,仅HTTP明文报文可直接提取
- 大包处理:如果HTTP报文长度超过MTU被分段,Netfilter钩子点可能只能拿到单段数据,要完整解析需要做流级别的数据拼接
内容的提问来源于stack exchange,提问作者manivarma indukuri
相关产品推荐
相关产品推荐

