On 5/20/19 2:40 AM, Mike Manning wrote: > Since the commit 1893ff20275b ("net/ipv6: Add l3mdev check to > ipv6_chk_addr_and_flags"), traceroute using TCP SYN or ICMP ECHO option > and ping fail when specifying a source address typically on a loopback > /dummy interface in the same VRF, e.g.: > > # ip vrf exec vrfgreen ping 3000::1 -I 2222::2 > ping: bind icmp socket: Cannot assign requested address > # ip vrf exec vrfgreen traceroute 3000::1 -s 2222::2 -T > bind: Cannot assign requested address > > IPv6 traceroute using default UDP and IPv4 ping & traceroute continue > to work inside a VRF using a source address. > > The reason is that the source address is provided via bind without a > device given by these applications in this case. The call to > ipv6_check_addr() in rawv6_bind() returns false as the default VRF is > assumed if no dev was given, but the src addr is in a non-default VRF. > > The solution is to check that the address exists in the L3 domain that > the dev is part of only if the dev has been specified. > > Signed-off-by: Mike Manning <mmann...@vyatta.att-mail.com> > --- > net/ipv6/addrconf.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c > index f96d1de79509..3963306ec27f 100644 > --- a/net/ipv6/addrconf.c > +++ b/net/ipv6/addrconf.c > @@ -1908,6 +1908,7 @@ int ipv6_chk_addr_and_flags(struct net *net, const > struct in6_addr *addr, > int strict, u32 banned_flags) > { > unsigned int hash = inet6_addr_hash(net, addr); > + const struct net_device *orig_dev = dev; > const struct net_device *l3mdev; > struct inet6_ifaddr *ifp; > u32 ifp_flags; > @@ -1922,7 +1923,7 @@ int ipv6_chk_addr_and_flags(struct net *net, const > struct in6_addr *addr, > if (!net_eq(dev_net(ifp->idev->dev), net)) > continue; > > - if (l3mdev_master_dev_rcu(ifp->idev->dev) != l3mdev) > + if (orig_dev && l3mdev_master_dev_rcu(ifp->idev->dev) != l3mdev) > continue; > > /* Decouple optimistic from tentative for evaluation here. >
Wrong fix. When looking up the address you have to give the L3 domain of interest. This change: diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 84dbe21b71e5..96a3559f2a09 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -287,7 +287,9 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) /* Binding to link-local address requires an interface */ if (!sk->sk_bound_dev_if) goto out_unlock; + } + if (sk->sk_bound_dev_if) { err = -ENODEV; dev = dev_get_by_index_rcu(sock_net(sk), sk->sk_bound_dev_if); make raw binds similar to tcp. See: c5ee066333ebc ("ipv6: Consider sk_bound_dev_if when binding a socket to an address") ec90ad334986f ("ipv6: Consider sk_bound_dev_if when binding a socket to a v4 mapped address")