如何获取ICMP超时响应报头ID?Traceroute程序包校验问题
解决ICMP超时响应的请求匹配问题
嘿,这个问题我之前写traceroute工具的时候也踩过坑!其实超时响应里不是没有你要的ID,只是它藏的位置和回显响应不一样,得换个地方找~
核心原因:超时ICMP包的结构特殊性
当中间路由器返回TTL过期超时(ICMP类型11,代码0)的响应时,这个包的外层ICMP头ID确实是0(因为这是路由器生成的新ICMP包,它自己不需要用ID标识请求)。但路由器会把你原始发送的ICMP请求包的前若干字节(通常是前8字节,包含完整的ICMP头)嵌入到超时响应的数据字段里——这才是你要找的关键!
具体匹配步骤
你需要分两种情况处理收到的ICMP包:
- 回显响应(类型0):直接读取外层ICMP头的ID字段,和你的进程PID对比即可
- 超时响应(类型11,代码0):解析响应包数据字段里的内嵌原始ICMP请求头,从中提取ID(以及序列号)来匹配
额外的验证技巧
为了避免极端情况下的PID冲突(比如不同进程刚好复用了同一个PID),建议发送请求时给每个包分配递增的序列号,收到响应时同时校验ID和序列号,这样匹配的准确性会更高。
代码示例(C语言思路)
#include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <unistd.h> void process_icmp_packet(char *buf, int len) { struct iphdr *ip_header = (struct iphdr *)buf; int ip_header_len = ip_header->ihl * 4; // IP头长度(以4字节为单位) // 跳过IP头,拿到外层ICMP头 struct icmphdr *outer_icmp = (struct icmphdr *)(buf + ip_header_len); if (outer_icmp->type == ICMP_ECHOREPLY) { // 处理回显响应:直接用外层ICMP的ID if (outer_icmp->un.echo.id == getpid()) { printf("匹配到自己的回显响应,序列号:%d\n", outer_icmp->un.echo.sequence); } } else if (outer_icmp->type == ICMP_TIME_EXCEEDED && outer_icmp->code == ICMP_EXC_TTL) { // 处理超时响应:解析内嵌的原始ICMP请求头 // 跳过外层ICMP头,拿到内嵌的原始IP包(包含我们发的ICMP请求) struct icmphdr *inner_icmp = (struct icmphdr *)(buf + ip_header_len + sizeof(struct icmphdr)); // 确认内嵌的是我们发的回显请求 if (inner_icmp->type == ICMP_ECHO && inner_icmp->un.echo.id == getpid()) { printf("匹配到自己的超时响应,原始序列号:%d\n", inner_icmp->un.echo.sequence); } } }
内容的提问来源于stack exchange,提问作者Marcin Barański
相关产品推荐
相关产品推荐

