[v4,net-next,3/6] net/ipv6: Add l3mdev check to ipv6_chk_addr_and_flags

Message ID 20180313152941.31218-4-dsahern@gmail.com
State Accepted
Delegated to: David Miller
Headers show
Series
  • net/ipv6: Address checks need to consider the L3 domain
Related show

Commit Message

David Ahern March 13, 2018, 3:29 p.m.
Lookup the L3 master device for the passed in device. Only consider
addresses on netdev's with the same master device. If the device is
not enslaved or is NULL, then the l3mdev is NULL which means only
devices not enslaved (ie, in the default domain) are considered.

Signed-off-by: David Ahern <dsahern@gmail.com>
---
 net/ipv6/addrconf.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

Comments

Ido Schimmel March 14, 2018, 12:25 p.m. | #1
On Tue, Mar 13, 2018 at 08:29:38AM -0700, David Ahern wrote:
> Lookup the L3 master device for the passed in device. Only consider
> addresses on netdev's with the same master device. If the device is
> not enslaved or is NULL, then the l3mdev is NULL which means only
> devices not enslaved (ie, in the default domain) are considered.
> 
> Signed-off-by: David Ahern <dsahern@gmail.com>

Reviewed-by: Ido Schimmel <idosch@mellanox.com>

Thanks for working on this. I also encountered this problem when working
on the selftests, but didn't have an immediate use case which required
me to use a non-linklocal address as a gateway.

Patch

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 0677b9732d56..6fd4bbdc444f 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1856,22 +1856,37 @@  int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
 }
 EXPORT_SYMBOL(ipv6_chk_addr);
 
+/* device argument is used to find the L3 domain of interest. If
+ * skip_dev_check is set, then the ifp device is not checked against
+ * the passed in dev argument. So the 2 cases for addresses checks are:
+ *   1. does the address exist in the L3 domain that dev is part of
+ *      (skip_dev_check = true), or
+ *
+ *   2. does the address exist on the specific device
+ *      (skip_dev_check = false)
+ */
 int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
 			    const struct net_device *dev, bool skip_dev_check,
 			    int strict, u32 banned_flags)
 {
 	unsigned int hash = inet6_addr_hash(net, addr);
+	const struct net_device *l3mdev;
 	struct inet6_ifaddr *ifp;
 	u32 ifp_flags;
 
 	rcu_read_lock();
 
+	l3mdev = l3mdev_master_dev_rcu(dev);
 	if (skip_dev_check)
 		dev = NULL;
 
 	hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) {
 		if (!net_eq(dev_net(ifp->idev->dev), net))
 			continue;
+
+		if (l3mdev_master_dev_rcu(ifp->idev->dev) != l3mdev)
+			continue;
+
 		/* Decouple optimistic from tentative for evaluation here.
 		 * Ban optimistic addresses explicitly, when required.
 		 */