diff --git a/trace/udp_ipv6.go b/trace/udp_ipv6.go index a4d5d3b..dee3f0d 100644 --- a/trace/udp_ipv6.go +++ b/trace/udp_ipv6.go @@ -117,27 +117,88 @@ func (t *UDPTracerIPv6) listenICMP() { } } +// handleICMPMessage 处理 ICMPv6 消息并提取 UDP 源端口 +// +// ICMPv6 错误消息格式: +// - ICMPv6 头部 (8 字节) +// - 原始 IPv6 包 (包含 IPv6 头部和 UDP 头部) +// +// 处理步骤: +// 1. 验证消息长度 +// 2. 解析 ICMPv6 头部 +// 3. 提取原始 IPv6 包 +// 4. 处理可能的扩展头部 +// 5. 提取 UDP 源端口 +// 6. 发送结果到对应通道 func (t *UDPTracerIPv6) handleICMPMessage(msg ReceivedMessage) { - // 对于IPv6,我们需要从ICMP消息中提取UDP源端口 - // 这里假设UDP头部在ICMP消息的固定位置 - if len(msg.Msg) < 48+8 { - return - } + // ICMPv6 错误消息至少需要包含 IPv6 头部(40字节)和部分 UDP 头部 + if len(msg.Msg) < 48 { + return + } - // IPv6 ICMP错误消息包含原始数据包的IPv6头(40字节)和UDP头(8字节) - // 从UDP头部提取源端口(前2字节) - srcPort := int(uint16(msg.Msg[48])<<8 | uint16(msg.Msg[49])) - - t.inflightRequestLock.Lock() - defer t.inflightRequestLock.Unlock() - ch, ok := t.inflightRequest[srcPort] - if !ok { - return - } - ch <- Hop{ - Success: true, - Address: msg.Peer, - } + // 尝试解析 ICMPv6 消息中包含的原始数据包 + var offset int = 8 // ICMPv6 头部长度 + + // 检查剩余长度是否足够包含 IPv6 头部 + if len(msg.Msg) < offset+40 { + return + } + + // 验证 IPv6 版本 (前4位应该是6) + if (msg.Msg[offset] >> 4) != 6 { + return + } + + // 获取下一个头部类型 + nextHeader := msg.Msg[offset+6] + + // 跳过 IPv6 基本头部 + offset += 40 + + // 处理可能的扩展头部 + for nextHeader != 17 && offset+2 < len(msg.Msg) { // 17 是 UDP 协议号 + switch nextHeader { + case 0: // Hop-by-Hop Options + case 43: // Routing + case 44: // Fragment + case 50: // ESP + case 51: // AH + case 60: // Destination Options + if offset+2 >= len(msg.Msg) { + return // 不够长,无法读取扩展头部长度 + } + nextHeader = msg.Msg[offset] + headerLen := int(msg.Msg[offset+1])*8 + 8 + offset += headerLen + default: + // 未知或不支持的扩展头部类型 + return + } + } + + // 确认下一个头部是 UDP (17) + if nextHeader != 17 { + return + } + + // 确保有足够的数据来读取 UDP 源端口 + if offset+2 > len(msg.Msg) { + return + } + + // 从 UDP 头部提取源端口(前2字节) + srcPort := int(uint16(msg.Msg[offset])<<8 | uint16(msg.Msg[offset+1])) + + t.inflightRequestLock.Lock() + defer t.inflightRequestLock.Unlock() + ch, ok := t.inflightRequest[srcPort] + if !ok { + return + } + ch <- Hop{ + Success: true, + Address: msg.Peer, + } } var cachedLocalPortv6 int