| From e1a7ac6f3ba6e157adcd0ca94d92a401f1943f56 Mon Sep 17 00:00:00 2001 |
| From: Nicolas Dichtel <nicolas.dichtel@6wind.com> |
| Date: Wed, 4 May 2022 11:07:38 +0200 |
| Subject: ping: fix address binding wrt vrf |
| |
| From: Nicolas Dichtel <nicolas.dichtel@6wind.com> |
| |
| commit e1a7ac6f3ba6e157adcd0ca94d92a401f1943f56 upstream. |
| |
| When ping_group_range is updated, 'ping' uses the DGRAM ICMP socket, |
| instead of an IP raw socket. In this case, 'ping' is unable to bind its |
| socket to a local address owned by a vrflite. |
| |
| Before the patch: |
| $ sysctl -w net.ipv4.ping_group_range='0 2147483647' |
| $ ip link add blue type vrf table 10 |
| $ ip link add foo type dummy |
| $ ip link set foo master blue |
| $ ip link set foo up |
| $ ip addr add 192.168.1.1/24 dev foo |
| $ ip addr add 2001::1/64 dev foo |
| $ ip vrf exec blue ping -c1 -I 192.168.1.1 192.168.1.2 |
| ping: bind: Cannot assign requested address |
| $ ip vrf exec blue ping6 -c1 -I 2001::1 2001::2 |
| ping6: bind icmp socket: Cannot assign requested address |
| |
| CC: stable@vger.kernel.org |
| Fixes: 1b69c6d0ae90 ("net: Introduce L3 Master device abstraction") |
| Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> |
| Reviewed-by: David Ahern <dsahern@kernel.org> |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/ping.c | 12 +++++++++++- |
| 1 file changed, 11 insertions(+), 1 deletion(-) |
| |
| --- a/net/ipv4/ping.c |
| +++ b/net/ipv4/ping.c |
| @@ -309,6 +309,7 @@ static int ping_check_bind_addr(struct s |
| struct net *net = sock_net(sk); |
| if (sk->sk_family == AF_INET) { |
| struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; |
| + u32 tb_id = RT_TABLE_LOCAL; |
| int chk_addr_ret; |
| |
| if (addr_len < sizeof(*addr)) |
| @@ -322,7 +323,8 @@ static int ping_check_bind_addr(struct s |
| pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n", |
| sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port)); |
| |
| - chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr); |
| + tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id; |
| + chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id); |
| |
| if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) |
| chk_addr_ret = RTN_LOCAL; |
| @@ -361,6 +363,14 @@ static int ping_check_bind_addr(struct s |
| if (!dev) { |
| rcu_read_unlock(); |
| return -ENODEV; |
| + } |
| + } |
| + |
| + if (!dev && sk->sk_bound_dev_if) { |
| + dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if); |
| + if (!dev) { |
| + rcu_read_unlock(); |
| + return -ENODEV; |
| } |
| } |
| has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev, |