我正在实施自己的IPv6网络堆栈,并且在使ICMPv6邻居发现工作时遇到了问题。堆栈的工作原理是捕获现有接口的数据包,挑选发送给它的数据包,然后从"虚拟接口发送新数据包。堆栈的地址。
为了测试这个,我有一台Ubuntu机器,一台LXC容器用作测试客户端来发送流量。 Ubuntu主机具有以下LXC容器的桥接接口:
ubuntu:~$ ifconfig
...
lxcbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.3.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::216:3eff:fe00:0 prefixlen 64 scopeid 0x20<link>
ether 00:16:3e:00:00:00 txqueuelen 1000 (Ethernet)
RX packets 522282 bytes 85867541 (85.8 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 554574 bytes 51636337 (51.6 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
...
容器具有以下网络接口:
my-continer:~$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.3.40 netmask 255.255.255.0 broadcast 10.0.3.255
inet6 fe80::216:3eff:fe72:dc1c prefixlen 64 scopeid 0x20<link>
ether 00:16:3e:72:dc:1c txqueuelen 1000 (Ethernet)
RX packets 554722 bytes 51647709 (51.6 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 522297 bytes 93181219 (93.1 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
...
如果我从LXC容器中ping Ubuntu主机,它当然有效:
my-continer:~$ sudo tcpdump -i eth0 -nn icmp6 -vv
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
11:56:44.435633 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) fe80::216:3eff:fe72:dc1c > ff02::1:ff00:0: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has fe80::216:3eff:fe00:0
source link-address option (1), length 8 (1): 00:16:3e:72:dc:1c
0x0000: 0016 3e72 dc1c
11:56:44.435649 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) fe80::216:3eff:fe00:0 > fe80::216:3eff:fe72:dc1c: [icmp6 sum ok] ICMP6, neighbor advertisement, length 32, tgt is fe80::216:3eff:fe00:0, Flags [solicited, override]
destination link-address option (2), length 8 (1): 00:16:3e:00:00:00
0x0000: 0016 3e00 0000
11:56:44.435652 IP6 (flowlabel 0x4fb04, hlim 64, next-header ICMPv6 (58) payload length: 64) fe80::216:3eff:fe72:dc1c > fe80::216:3eff:fe00:0: [icmp6 sum ok] ICMP6, echo request, seq 1
11:56:44.435660 IP6 (flowlabel 0xbb56b, hlim 64, next-header ICMPv6 (58) payload length: 64) fe80::216:3eff:fe00:0 > fe80::216:3eff:fe72:dc1c: [icmp6 sum ok] ICMP6, echo reply, seq 1
11:56:45.465749 IP6 (flowlabel 0x4fb04, hlim 64, next-header ICMPv6 (58) payload length: 64) fe80::216:3eff:fe72:dc1c > fe80::216:3eff:fe00:0: [icmp6 sum ok] ICMP6, echo request, seq 2
11:56:45.465768 IP6 (flowlabel 0xbb56b, hlim 64, next-header ICMPv6 (58) payload length: 64) fe80::216:3eff:fe00:0 > fe80::216:3eff:fe72:dc1c: [icmp6 sum ok] ICMP6, echo reply, seq 2
11:56:46.490187 IP6 (flowlabel 0x4fb04, hlim 64, next-header ICMPv6 (58) payload length: 64) fe80::216:3eff:fe72:dc1c > fe80::216:3eff:fe00:0: [icmp6 sum ok] ICMP6, echo request, seq 3
11:56:46.490204 IP6 (flowlabel 0xbb56b, hlim 64, next-header ICMPv6 (58) payload length: 64) fe80::216:3eff:fe00:0 > fe80::216:3eff:fe72:dc1c: [icmp6 sum ok] ICMP6, echo reply, seq 3
我自己的堆栈配置了mac地址0c:22:38:4e:9a:bc
和IPv6地址fe80::216:3eff:fe00:1234
。
实现在Erlang中,处理邻居请求数据包的代码如下:
process(#ipv6{headers = [#icmpv6{type = neighbor_solicitation, payload = {IP6, _}}|_]} = Packet, {IP6, Mac}) ->
#ipv6{src = Src} = Packet,
send(#ipv6{
src = IP6,
dst = Src,
next = ?IP_PROTO_ICMPv6,
hlim = 16#FF,
headers = [
{icmpv6, #icmpv6{
type = neighbor_advertisement,
code = 0,
payload = {IP6, #{source_link_layer_addr => Mac}}
}}
]
});
Packet
是传入的邻居请求请求,{IP6, Mac}
是已配置堆栈的IPv6地址和mac地址。
当ping它时,数据包到达主机上的接口并被libpcap接收。我生成对地址解析的响应,但ping不会成功:
my-continer:~$ ping6 -I eth0 fe80::216:3eff:fe00:1244
PING fe80::216:3eff:fe00:1244(fe80::216:3eff:fe00:1244) from fe80::216:3eff:fe72:dc1c%eth0 eth0: 56 data bytes
From fe80::216:3eff:fe72:dc1c%eth0 icmp_seq=1 Destination unreachable: Address unreachable
From fe80::216:3eff:fe72:dc1c%eth0 icmp_seq=2 Destination unreachable: Address unreachable
From fe80::216:3eff:fe72:dc1c%eth0 icmp_seq=3 Destination unreachable: Address unreachable
^C
--- fe80::216:3eff:fe00:1244 ping statistics ---
4 packets transmitted, 0 received, +3 errors, 100% packet loss, time 3065ms
跟踪显示邻居广告包到达容器:
my-continer:~$ sudo tcpdump -i eth0 -nn icmp6 -vv
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:04:23.751427 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) fe80::216:3eff:fe72:dc1c > ff02::1:ff00:1234: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has fe80::216:3eff:fe00:1234
source link-address option (1), length 8 (1): 00:16:3e:72:dc:1c
0x0000: 0016 3e72 dc1c
12:04:23.753662 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) fe80::216:3eff:fe00:1234 > fe80::216:3eff:fe72:dc1c: [icmp6 sum ok] ICMP6, neighbor advertisement, length 32, tgt is fe80::216:3eff:fe00:1234, Flags [solicited, override]
source link-address option (1), length 8 (1): 0c:22:38:4e:9a:bc
0x0000: 0c22 384e 9abc
12:04:24.761850 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) fe80::216:3eff:fe72:dc1c > ff02::1:ff00:1234: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has fe80::216:3eff:fe00:1234
source link-address option (1), length 8 (1): 00:16:3e:72:dc:1c
0x0000: 0016 3e72 dc1c
12:04:24.765901 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) fe80::216:3eff:fe00:1234 > fe80::216:3eff:fe72:dc1c: [icmp6 sum ok] ICMP6, neighbor advertisement, length 32, tgt is fe80::216:3eff:fe00:1234, Flags [solicited, override]
source link-address option (1), length 8 (1): 0c:22:38:4e:9a:bc
0x0000: 0c22 384e 9abc
12:04:25.786994 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) fe80::216:3eff:fe72:dc1c > ff02::1:ff00:1234: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has fe80::216:3eff:fe00:1234
source link-address option (1), length 8 (1): 00:16:3e:72:dc:1c
0x0000: 0016 3e72 dc1c
12:04:25.789413 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) fe80::216:3eff:fe00:1234 > fe80::216:3eff:fe72:dc1c: [icmp6 sum ok] ICMP6, neighbor advertisement, length 32, tgt is fe80::216:3eff:fe00:1234, Flags [solicited, override]
source link-address option (1), length 8 (1): 0c:22:38:4e:9a:bc
0x0000: 0c22 384e 9abc
它们与发送到实际接口和从实际接口发送的内容完全相同,除了地址本身。
检查路由表显示虚拟地址的地址解析由于某种原因不起作用:
my-continer:~$ sudo ip -6 neigh
fe80::216:3eff:fe00:0 dev eth0 lladdr 00:16:3e:00:00:00 STALE
fe80::216:3eff:fe00:1234 dev eth0 FAILED
这里发现邻居失败的原因是什么?我的ICMPv6协议实现中缺少什么使容器接受虚拟地址作为有效路由?