PHP实现ICMPv6 Solicitation Request无Neighbor Advertisement回复问题
ICMPv6邻居请求无回复问题排查与修复
以下是用于发送ICMPv6邻居请求(Neighbor Solicitation)的PHP脚本:
$socket = socket_create(AF_INET6, SOCK_RAW, 58); socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 1, 'usec' => 0)); $type = chr(135); $code = chr(0); $check = chr(0); $check .= chr(0); $reserverd = chr(0) . chr(0) . chr(0) . chr(0); $distMul = "ff02::1:ff26:13dc"; // 请求节点多播地址 $target = "fe80::3b40:f032:5726:13dc"; $id = inet_pton($target); socket_set_option($socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255); $package = $type . $code . $check . $reserverd . $id; socket_connect($socket, $distMul, 0); socket_send($socket, $package, strlen($package), 0); $str = socket_read($socket, 2048); socket_close($socket);
Wireshark抓包结果:
问题:该请求始终无法收到Type 136的邻居通告(Neighbor Advertisement)回复。
核心问题与修复方案
- 必须计算ICMPv6校验和:当前直接将校验和设为全0,ICMPv6要求校验和必须基于伪头部(IPv6头部部分字段)+ ICMPv6报文计算,接收方会直接丢弃校验和无效的包。需实现符合RFC 2460标准的校验和计算逻辑。
- 添加源链路层地址选项:邻居请求报文必须携带源链路层地址选项(Type 1),格式为
Type(1字节) + Length(1字节,单位为8字节块) + 6字节MAC地址,否则目标节点无法确定回复的链路层地址,不会发送NA包。 - 修正多播跳数:本地链路的多播跳数应设为1而非255,邻居请求是本地链路范围内的操作,跳数设为1符合协议规范,避免跨网段传输的不必要问题。
- 确认运行权限:RAW socket需要root/管理员权限才能创建,确保脚本以足够权限运行,否则会存在发送/接收限制。
修正后的关键代码示例
- ICMPv6校验和计算函数:
function calculate_icmpv6_checksum($pseudo_header, $icmp_packet) { $data = $pseudo_header . $icmp_packet; $len = strlen($data); $sum = 0; for ($i = 0; $i < $len; $i += 2) { $sum += ord($data[$i]) << 8; if ($i+1 < $len) { $sum += ord($data[$i+1]); } } $sum = ($sum >> 16) + ($sum & 0xffff); $sum += ($sum >> 16); return pack('n', ~$sum & 0xffff); }
- 添加源链路层地址选项:
// 替换为你的网卡MAC地址,格式如"00:11:22:33:44:55" $mac = "00:11:22:33:44:55"; $mac_bytes = pack('H*', str_replace(':', '', $mac)); // 构造源链路层地址选项 $source_link_option = chr(1) . chr(1) . $mac_bytes; // 拼接完整ICMP报文 $package = $type . $code . $check . $reserverd . $id . $source_link_option;
- 修正多播跳数设置:
socket_set_option($socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1);
内容的提问来源于stack exchange,提问作者anru
相关产品推荐
相关产品推荐

