Skip to content

Commit 232378e

Browse files
dsaherndavem330
authored andcommitted
net/ipv6: Change address check to always take a device argument
ipv6_chk_addr_and_flags determines if an address is a local address and optionally if it is an address on a specific device. For example, it is called by ip6_route_info_create to determine if a given gateway address is a local address. The address check currently does not consider L3 domains and as a result does not allow a route to be added in one VRF if the nexthop points to an address in a second VRF. e.g., $ ip route add 2001:db8:1::/64 vrf r2 via 2001:db8:102::23 Error: Invalid gateway address. where 2001:db8:102::23 is an address on an interface in vrf r1. ipv6_chk_addr_and_flags needs to allow callers to always pass in a device with a separate argument to not limit the address to the specific device. The device is used used to determine the L3 domain of interest. To that end add an argument to skip the device check and update callers to always pass a device where possible and use the new argument to mean any address in the domain. Update a handful of users of ipv6_chk_addr with a NULL dev argument. This patch handles the change to these callers without adding the domain check. ip6_validate_gw needs to handle 2 cases - one where the device is given as part of the nexthop spec and the other where the device is resolved. There is at least 1 VRF case where deferring the check to only after the route lookup has resolved the device fails with an unintuitive error "RTNETLINK answers: No route to host" as opposed to the preferred "Error: Gateway can not be a local address." The 'no route to host' error is because of the fallback to a full lookup. The check is done twice to avoid this error. Signed-off-by: David Ahern <dsahern@gmail.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 9fbb704 commit 232378e

File tree

7 files changed

+43
-19
lines changed

7 files changed

+43
-19
lines changed

include/net/addrconf.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg);
6969
int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
7070
const struct net_device *dev, int strict);
7171
int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
72-
const struct net_device *dev, int strict,
73-
u32 banned_flags);
72+
const struct net_device *dev, bool skip_dev_check,
73+
int strict, u32 banned_flags);
7474

7575
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
7676
int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr);

net/ipv6/addrconf.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,19 +1851,24 @@ static int ipv6_count_addresses(const struct inet6_dev *idev)
18511851
int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
18521852
const struct net_device *dev, int strict)
18531853
{
1854-
return ipv6_chk_addr_and_flags(net, addr, dev, strict, IFA_F_TENTATIVE);
1854+
return ipv6_chk_addr_and_flags(net, addr, dev, !dev,
1855+
strict, IFA_F_TENTATIVE);
18551856
}
18561857
EXPORT_SYMBOL(ipv6_chk_addr);
18571858

18581859
int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
1859-
const struct net_device *dev, int strict,
1860-
u32 banned_flags)
1860+
const struct net_device *dev, bool skip_dev_check,
1861+
int strict, u32 banned_flags)
18611862
{
18621863
unsigned int hash = inet6_addr_hash(net, addr);
18631864
struct inet6_ifaddr *ifp;
18641865
u32 ifp_flags;
18651866

18661867
rcu_read_lock();
1868+
1869+
if (skip_dev_check)
1870+
dev = NULL;
1871+
18671872
hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) {
18681873
if (!net_eq(dev_net(ifp->idev->dev), net))
18691874
continue;

net/ipv6/anycast.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,11 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
6666
return -EPERM;
6767
if (ipv6_addr_is_multicast(addr))
6868
return -EINVAL;
69-
if (ipv6_chk_addr(net, addr, NULL, 0))
69+
70+
if (ifindex)
71+
dev = __dev_get_by_index(net, ifindex);
72+
73+
if (ipv6_chk_addr_and_flags(net, addr, dev, true, 0, IFA_F_TENTATIVE))
7074
return -EINVAL;
7175

7276
pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL);
@@ -90,8 +94,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
9094
dev = __dev_get_by_flags(net, IFF_UP,
9195
IFF_UP | IFF_LOOPBACK);
9296
}
93-
} else
94-
dev = __dev_get_by_index(net, ifindex);
97+
}
9598

9699
if (!dev) {
97100
err = -ENODEV;

net/ipv6/datagram.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,8 +801,9 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
801801
if (addr_type != IPV6_ADDR_ANY) {
802802
int strict = __ipv6_addr_src_scope(addr_type) <= IPV6_ADDR_SCOPE_LINKLOCAL;
803803
if (!(inet_sk(sk)->freebind || inet_sk(sk)->transparent) &&
804-
!ipv6_chk_addr(net, &src_info->ipi6_addr,
805-
strict ? dev : NULL, 0) &&
804+
!ipv6_chk_addr_and_flags(net, &src_info->ipi6_addr,
805+
dev, !strict, 0,
806+
IFA_F_TENTATIVE) &&
806807
!ipv6_chk_acast_addr_src(net, dev,
807808
&src_info->ipi6_addr))
808809
err = -EINVAL;

net/ipv6/ip6_tunnel.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -758,9 +758,11 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
758758
ldev = dev_get_by_index_rcu(net, p->link);
759759

760760
if ((ipv6_addr_is_multicast(laddr) ||
761-
likely(ipv6_chk_addr(net, laddr, ldev, 0))) &&
761+
likely(ipv6_chk_addr_and_flags(net, laddr, ldev, false,
762+
0, IFA_F_TENTATIVE))) &&
762763
((p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) ||
763-
likely(!ipv6_chk_addr(net, raddr, NULL, 0))))
764+
likely(!ipv6_chk_addr_and_flags(net, raddr, ldev, true,
765+
0, IFA_F_TENTATIVE))))
764766
ret = 1;
765767
}
766768
return ret;
@@ -990,12 +992,14 @@ int ip6_tnl_xmit_ctl(struct ip6_tnl *t,
990992
if (p->link)
991993
ldev = dev_get_by_index_rcu(net, p->link);
992994

993-
if (unlikely(!ipv6_chk_addr(net, laddr, ldev, 0)))
995+
if (unlikely(!ipv6_chk_addr_and_flags(net, laddr, ldev, false,
996+
0, IFA_F_TENTATIVE)))
994997
pr_warn("%s xmit: Local address not yet configured!\n",
995998
p->name);
996999
else if (!(p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) &&
9971000
!ipv6_addr_is_multicast(raddr) &&
998-
unlikely(ipv6_chk_addr(net, raddr, NULL, 0)))
1001+
unlikely(ipv6_chk_addr_and_flags(net, raddr, ldev,
1002+
true, 0, IFA_F_TENTATIVE)))
9991003
pr_warn("%s xmit: Routing loop! Remote address found on this node!\n",
10001004
p->name);
10011005
else

net/ipv6/ndisc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
707707
int probes = atomic_read(&neigh->probes);
708708

709709
if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr,
710-
dev, 1,
710+
dev, false, 1,
711711
IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))
712712
saddr = &ipv6_hdr(skb)->saddr;
713713
probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);

net/ipv6/route.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2632,18 +2632,19 @@ static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
26322632
{
26332633
const struct in6_addr *gw_addr = &cfg->fc_gateway;
26342634
int gwa_type = ipv6_addr_type(gw_addr);
2635+
bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
26352636
const struct net_device *dev = *_dev;
2637+
bool need_addr_check = !dev;
26362638
int err = -EINVAL;
26372639

26382640
/* if gw_addr is local we will fail to detect this in case
26392641
* address is still TENTATIVE (DAD in progress). rt6_lookup()
26402642
* will return already-added prefix route via interface that
26412643
* prefix route was assigned to, which might be non-loopback.
26422644
*/
2643-
if (ipv6_chk_addr_and_flags(net, gw_addr,
2644-
gwa_type & IPV6_ADDR_LINKLOCAL ?
2645-
dev : NULL, 0, 0)) {
2646-
NL_SET_ERR_MSG(extack, "Invalid gateway address");
2645+
if (dev &&
2646+
ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2647+
NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
26472648
goto out;
26482649
}
26492650

@@ -2683,6 +2684,16 @@ static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
26832684
"Egress device can not be loopback device for this route");
26842685
goto out;
26852686
}
2687+
2688+
/* if we did not check gw_addr above, do so now that the
2689+
* egress device has been resolved.
2690+
*/
2691+
if (need_addr_check &&
2692+
ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2693+
NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
2694+
goto out;
2695+
}
2696+
26862697
err = 0;
26872698
out:
26882699
return err;

0 commit comments

Comments
 (0)