From 36580e1d539b4bb7c187d4cf9ccc63afad9edbb1 Mon Sep 17 00:00:00 2001 From: Lahav Schlesinger Date: Wed, 30 Jun 2021 13:06:13 +0300 Subject: [PATCH 1/2] ping: Fix ping6 binding to VRF and address Since Linux kernel commit 1893ff20275b ("net/ipv6: Add l3mdev check to ipv6_chk_addr_and_flags") from v4.17-rc1 ping fails when trying to create IPv6 SOCK_RAW socket (e.g. if net.ipv4.ping_group_range = 1 0) and passing both -I and -I . It works for IPv4 SOCK_RAW socket. # ip netns add tmp_ns # ip -n tmp_ns link add vrf_1 type vrf table 10001 # ip -n tmp_ns link add lo10 type dummy # ip -n tmp_ns link set lo10 master vrf_1 # ip -n tmp_ns link set vrf_1 up # ip -n tmp_ns link set lo10 up # ip -n tmp_ns link set lo up # ip -n tmp_ns addr add 1:2::3:4/128 dev lo10 # ip -n tmp_ns addr add 1.2.3.4/32 dev lo10 # ip netns exec tmp_ns ping -6 1:2::3:4 -I vrf_1 -I 1:2::3:4 -c 1 # IPv6 broken ping: bind icmp socket: Cannot assign requested address # ping 1.2.3.4 -I vrf_1 -I 1.2.3.4 -c 1 # IPv4 working PING 1.2.3.4 (1.2.3.4) from 1.2.3.4 vrf_1: 56(84) bytes of data. 64 bytes from 1.2.3.4: icmp_seq=1 ttl=64 time=0.090 ms --- 1.2.3.4 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.090/0.090/0.090/0.000 ms ping fails because it doesn't actually bind to the VRF interface, while after 1893ff20275b, binding to an IPv6 address searches only on the same l3mdev as the device the function receives. If the socket wasn't SO_BINDTODEVICE-ed, then the kernel will only search for devices that are not ensalved to an l3mdev device (= in the default VRF), which will cause the bind() to fail. Only SOCK_RAW socket is affected. SOCK_DGRAM is not affected because Linux kernel doesn't check the device the socket was SO_BINDTODEVICE-ed to, but only the device from addr->sin6_scope_id (which if none is passed, it will again only search devices in the default VRF). NOTE: creating network namespace to reproduce the issue is needed just on systems with net.ipv4.ping_group_range = 0 2147483647 (e.g. current Fedora, openSUSE, Ubuntu), which causes to use SOCK_DGRAM socket. Alternatively to force SOCK_RAW to it'd be enough just to properly set net.ipv4.ping_group_range: # echo "1 0" > /proc/sys/net/ipv4/ping_group_range Closes: https://github.com/iputils/iputils/pull/344 Reviewed-by: Petr Vorel Signed-off-by: Lahav Schlesinger [ pvorel: adjusted commit message ] Signed-off-by: Petr Vorel (cherry picked from commit 7c65999f98bc4a1984594b7fad1af0eaf0b9d34b) --- ping/ping6_common.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ping/ping6_common.c b/ping/ping6_common.c index 4712928..98b5adb 100644 --- a/ping/ping6_common.c +++ b/ping/ping6_common.c @@ -223,6 +223,8 @@ int ping6_run(struct ping_rts *rts, int argc, char **argv, struct addrinfo *ai, if (rts->device) { struct cmsghdr *cmsg; struct in6_pktinfo *ipi; + int rc; + int errno_save; cmsg = (struct cmsghdr *)(rts->cmsgbuf + rts->cmsglen); rts->cmsglen += CMSG_SPACE(sizeof(*ipi)); @@ -233,6 +235,15 @@ int ping6_run(struct ping_rts *rts, int argc, char **argv, struct addrinfo *ai, ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg); memset(ipi, 0, sizeof(*ipi)); ipi->ipi6_ifindex = if_name2index(rts->device); + + enable_capability_raw(); + rc = setsockopt(sock->fd, SOL_SOCKET, SO_BINDTODEVICE, + rts->device, strlen(rts->device) + 1); + errno_save = errno; + disable_capability_raw(); + + if (rc == -1) + error(2, errno_save, "SO_BINDTODEVICE %s", rts->device); } if (IN6_IS_ADDR_MULTICAST(&rts->whereto6.sin6_addr)) { -- 2.46.0