aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Ahern <dsa@cumulusnetworks.com>2016-06-16 19:24:26 -0400
committerDavid S. Miller <davem@davemloft.net>2016-06-18 00:25:29 -0400
commitafbac6010aec514998214fb19a1f37732b7a1d77 (patch)
tree70ba765b4370e8f37a8b7859f054ec4e18727074
parent0d240e7811c4ec1965760ee4643b5bbc9cfacbb3 (diff)
net: ipv6: Address selection needs to consider L3 domains
IPv6 version of 3f2fb9a834cb ("net: l3mdev: address selection should only consider devices in L3 domain") and the follow up commit, a17b693cdd876 ("net: l3mdev: prefer VRF master for source address selection"). That is, if outbound device is given then the address preference order is an address from that device, an address from the master device if it is enslaved, and then an address from a device in the same L3 domain. Signed-off-by: David Ahern <dsa@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/l3mdev.h31
-rw-r--r--net/ipv6/addrconf.c48
2 files changed, 79 insertions, 0 deletions
diff --git a/include/net/l3mdev.h b/include/net/l3mdev.h
index 818fd4f100fc..e90095091aa0 100644
--- a/include/net/l3mdev.h
+++ b/include/net/l3mdev.h
@@ -79,6 +79,31 @@ static inline int l3mdev_master_ifindex_by_index(struct net *net, int ifindex)
79 return rc; 79 return rc;
80} 80}
81 81
82static inline
83const struct net_device *l3mdev_master_dev_rcu(const struct net_device *_dev)
84{
85 /* netdev_master_upper_dev_get_rcu calls
86 * list_first_or_null_rcu to walk the upper dev list.
87 * list_first_or_null_rcu does not handle a const arg. We aren't
88 * making changes, just want the master device from that list so
89 * typecast to remove the const
90 */
91 struct net_device *dev = (struct net_device *)_dev;
92 const struct net_device *master;
93
94 if (!dev)
95 return NULL;
96
97 if (netif_is_l3_master(dev))
98 master = dev;
99 else if (netif_is_l3_slave(dev))
100 master = netdev_master_upper_dev_get_rcu(dev);
101 else
102 master = NULL;
103
104 return master;
105}
106
82/* get index of an interface to use for FIB lookups. For devices 107/* get index of an interface to use for FIB lookups. For devices
83 * enslaved to an L3 master device FIB lookups are based on the 108 * enslaved to an L3 master device FIB lookups are based on the
84 * master index 109 * master index
@@ -190,6 +215,12 @@ static inline int l3mdev_master_ifindex_by_index(struct net *net, int ifindex)
190 return 0; 215 return 0;
191} 216}
192 217
218static inline
219const struct net_device *l3mdev_master_dev_rcu(const struct net_device *dev)
220{
221 return NULL;
222}
223
193static inline int l3mdev_fib_oif_rcu(struct net_device *dev) 224static inline int l3mdev_fib_oif_rcu(struct net_device *dev)
194{ 225{
195 return dev ? dev->ifindex : 0; 226 return dev ? dev->ifindex : 0;
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 6c8fc3f96b11..a1f6b7b31531 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1524,6 +1524,28 @@ out:
1524 return hiscore_idx; 1524 return hiscore_idx;
1525} 1525}
1526 1526
1527static int ipv6_get_saddr_master(struct net *net,
1528 const struct net_device *dst_dev,
1529 const struct net_device *master,
1530 struct ipv6_saddr_dst *dst,
1531 struct ipv6_saddr_score *scores,
1532 int hiscore_idx)
1533{
1534 struct inet6_dev *idev;
1535
1536 idev = __in6_dev_get(dst_dev);
1537 if (idev)
1538 hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev,
1539 scores, hiscore_idx);
1540
1541 idev = __in6_dev_get(master);
1542 if (idev)
1543 hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev,
1544 scores, hiscore_idx);
1545
1546 return hiscore_idx;
1547}
1548
1527int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, 1549int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
1528 const struct in6_addr *daddr, unsigned int prefs, 1550 const struct in6_addr *daddr, unsigned int prefs,
1529 struct in6_addr *saddr) 1551 struct in6_addr *saddr)
@@ -1577,13 +1599,39 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
1577 if (idev) 1599 if (idev)
1578 hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx); 1600 hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx);
1579 } else { 1601 } else {
1602 const struct net_device *master;
1603 int master_idx = 0;
1604
1605 /* if dst_dev exists and is enslaved to an L3 device, then
1606 * prefer addresses from dst_dev and then the master over
1607 * any other enslaved devices in the L3 domain.
1608 */
1609 master = l3mdev_master_dev_rcu(dst_dev);
1610 if (master) {
1611 master_idx = master->ifindex;
1612
1613 hiscore_idx = ipv6_get_saddr_master(net, dst_dev,
1614 master, &dst,
1615 scores, hiscore_idx);
1616
1617 if (scores[hiscore_idx].ifa)
1618 goto out;
1619 }
1620
1580 for_each_netdev_rcu(net, dev) { 1621 for_each_netdev_rcu(net, dev) {
1622 /* only consider addresses on devices in the
1623 * same L3 domain
1624 */
1625 if (l3mdev_master_ifindex_rcu(dev) != master_idx)
1626 continue;
1581 idev = __in6_dev_get(dev); 1627 idev = __in6_dev_get(dev);
1582 if (!idev) 1628 if (!idev)
1583 continue; 1629 continue;
1584 hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx); 1630 hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx);
1585 } 1631 }
1586 } 1632 }
1633
1634out:
1587 rcu_read_unlock(); 1635 rcu_read_unlock();
1588 1636
1589 hiscore = &scores[hiscore_idx]; 1637 hiscore = &scores[hiscore_idx];