ILD

IPv6源路由匹配问题导致ping失败定位
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2019-12-15 站点:Inside Linux Development

在openwrt上,获取到IPv6地址后,ping知名dns server失败:

# ping 240c::6666
PING 240c::6666 (240c::6666): 56 data bytes
ping: sendto: Permission denied


但是指定接口,或源ip地址后,ping ok:

# ping -I 240e:fa:c6b3:ed00:2e61:4ff:fefd:7282 240c::6666
PING 240c::6666 (240c::6666) from 240e:fa:c6b3:ed00:2e61:4ff:fefd:7282: 56 data bytes
64 bytes from 240c::6666: seq=0 ttl=57 time=30.254 ms
64 bytes from 240c::6666: seq=1 ttl=57 time=29.194 ms
^C
--- 240c::6666 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 29.194/29.724/30.254 ms


查看路由表:

# ip -6 route
default from 240e:fa:c6b3:ed00:2e61:4ff:fefd:7282 via fe80::1 dev eth4094  proto static  metric 512
default from 240e:fa:c6b3:ed10::/60 via fe80::1 dev eth4094  proto static  metric 512
240e:fa:c6b3:ed00::/64 dev eth4094  proto static  metric 256
240e:fa:c6b3:ed10::/64 dev br-lan  proto static  metric 1024
unreachable 240e:fa:c6b3:ed10::/60 dev lo  proto static  metric 2147483647  error -101
fe80::/64 dev eth3  proto kernel  metric 256
fe80::/64 dev eth4  proto kernel  metric 256
fe80::/64 dev eth5  proto kernel  metric 256
fe80::/64 dev eth4094  proto kernel  metric 256
fe80::/64 dev ath00  proto kernel  metric 256
fe80::/64 dev br-lan  proto kernel  metric 256
fe80::/64 dev ath01  proto kernel  metric 256
fe80::/64 dev ath10  proto kernel  metric 256
fe80::/64 dev ath11  proto kernel  metric 256

有两台带源地址的默认路由,但是没有制定源IP,则没有匹配任何一条路由:


使用ip -6 route get测试,错误码正好是EACCES。

# ip -6 route get 240c::6666
prohibit 240c::6666 from :: dev lo  table unspec  proto kernel  src 240e:fa:c6b3:ed00:2e61:4ff:fefd:7282  metric 4294967295  error -13


问题定位:

编译strace,查看ping的过程:

socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6) = 3

dup2(3, 0)                              = 0
close(3)                                = 0
setsockopt(0, SOL_ICMPV6, 1, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\375\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377", 32) = 0
setsockopt(0, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0
setsockopt(0, SOL_SOCKET, SO_RCVBUF, [7280], 4) = 0
setsockopt(0, SOL_RAW, 0x7 /* RAW_??? */, [2], 4) = 0
setsockopt(0, SOL_IPV6, 0x8 /* IPV6_??? */, [1], 4) = 0
rt_sigaction(SIGINT, {0xb6f223b8, [INT], SA_RESTART|0x4000000}, {SIG_DFL, [], 0}, 8) = 0
gettimeofday({1576340049, 676382}, NULL) = 0
sendto(0, "\200\0\0\0\5>\0\0^\310\264D\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 64, 0, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "240c::6666", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 EACCES (Permission denied)


创建的是SOC_RAW类型的套接字,跟踪内核源代码3.14.17:

rawv6_sendmsg()net/ipv6/raw.c

-》ip6_dst_lookup_flow()net/ipv6/ip6_output.c

-》ip6_dst_lookup_tail()net/ipv6/ip6_output.c

-》ip6_route_output()net/ipv6/route.c

在ip6_dst_lookup_tail()中会先调用ip6_route_output匹配路由,在调用ip6_route_get_saddr()设置源地址。但是上述源地址是0,不会匹配两条带源地址的默认路由。导致路由查找失败。


测试最新的OpenWrt版本的内核,没有这个问题,ping可以正常通。比较内核版本的差异,发现Markus Stenberg提交了patch,他的做法是,先匹配一个源地址,再进行路由查找。请查看附件链接。


参考:

http://patchwork.ozlabs.org/patch/468061/

https://vincent.bernat.ch/en/blog/2017-ipv6-route-lookup-linux


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.