Commit c3f463ba authored by David S. Miller's avatar David S. Miller

Merge branch 'vrf-saddr-selection'

David Ahern says:

====================
net: l3mdev: Fix source address for unnumbered deployments

David Lamparter noted a use case where the source address selection fails
to pick an address from a VRF interface - unnumbered interfaces. The use
case has the VRF device as the VRF local loopback with addresses and
interfaces enslaved without an address themselves. e.g,

    ip addr add 9.9.9.9/32 dev lo
    ip link set lo up

    ip link add name vrf0 type vrf table 101
    ip rule add oif vrf0 table 101
    ip rule add iif vrf0 table 101
    ip link set vrf0 up
    ip addr add 10.0.0.3/32 dev vrf0

    ip link add name dummy2 type dummy
    ip link set dummy2 master vrf0 up

    --> note dummy2 has no address - unnumbered device

    ip route add 10.2.2.2/32 dev dummy2 table 101
    ip neigh add 10.2.2.2 dev dummy2 lladdr 02:00:00:00:00:02

ping to the 10.2.2.2 through the L3 domain:

    $ ping -I vrf0 -c1 10.2.2.2
    ping: Warning: source address might be selected on device other than vrf0.
    PING 10.2.2.2 (10.2.2.2) from 9.9.9.9 vrf0: 56(84) bytes of data.

picks up the wrong address -- the one from 'lo' not vrf0. And from tcpdump:
    12:57:29.449128 IP 9.9.9.9 > 10.2.2.2: ICMP echo request, id 2491, seq 1, length 64

This patch series changes address selection to only consider devices in
the same L3 domain and to use the VRF device as the L3 domains loopback.

    $ ping -I vrf0 -c1 10.2.2.2
    PING 10.2.2.2 (10.2.2.2) from 10.0.0.3 vrf0: 56(84) bytes of data.

From tcpdump:
    12:59:25.096426 IP 10.0.0.3 > 10.2.2.2: ICMP echo request, id 2113, seq 1, length 64

Now the source address comes from vrf0.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8d3f2806 17b693cd
...@@ -39,7 +39,7 @@ struct l3mdev_ops { ...@@ -39,7 +39,7 @@ struct l3mdev_ops {
#ifdef CONFIG_NET_L3_MASTER_DEV #ifdef CONFIG_NET_L3_MASTER_DEV
int l3mdev_master_ifindex_rcu(struct net_device *dev); int l3mdev_master_ifindex_rcu(const struct net_device *dev);
static inline int l3mdev_master_ifindex(struct net_device *dev) static inline int l3mdev_master_ifindex(struct net_device *dev)
{ {
int ifindex; int ifindex;
...@@ -179,7 +179,7 @@ struct dst_entry *l3mdev_rt6_dst_by_oif(struct net *net, ...@@ -179,7 +179,7 @@ struct dst_entry *l3mdev_rt6_dst_by_oif(struct net *net,
#else #else
static inline int l3mdev_master_ifindex_rcu(struct net_device *dev) static inline int l3mdev_master_ifindex_rcu(const struct net_device *dev)
{ {
return 0; return 0;
} }
......
...@@ -1194,6 +1194,7 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) ...@@ -1194,6 +1194,7 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
__be32 addr = 0; __be32 addr = 0;
struct in_device *in_dev; struct in_device *in_dev;
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
int master_idx;
rcu_read_lock(); rcu_read_lock();
in_dev = __in_dev_get_rcu(dev); in_dev = __in_dev_get_rcu(dev);
...@@ -1214,12 +1215,33 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) ...@@ -1214,12 +1215,33 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
if (addr) if (addr)
goto out_unlock; goto out_unlock;
no_in_dev: no_in_dev:
master_idx = l3mdev_master_ifindex_rcu(dev);
/* For VRFs, the VRF device takes the place of the loopback device,
* with addresses on it being preferred. Note in such cases the
* loopback device will be among the devices that fail the master_idx
* equality check in the loop below.
*/
if (master_idx &&
(dev = dev_get_by_index_rcu(net, master_idx)) &&
(in_dev = __in_dev_get_rcu(dev))) {
for_primary_ifa(in_dev) {
if (ifa->ifa_scope != RT_SCOPE_LINK &&
ifa->ifa_scope <= scope) {
addr = ifa->ifa_local;
goto out_unlock;
}
} endfor_ifa(in_dev);
}
/* Not loopback addresses on loopback should be preferred /* Not loopback addresses on loopback should be preferred
in this case. It is important that lo is the first interface in this case. It is important that lo is the first interface
in dev_base list. in dev_base list.
*/ */
for_each_netdev_rcu(net, dev) { for_each_netdev_rcu(net, dev) {
if (l3mdev_master_ifindex_rcu(dev) != master_idx)
continue;
in_dev = __in_dev_get_rcu(dev); in_dev = __in_dev_get_rcu(dev);
if (!in_dev) if (!in_dev)
continue; continue;
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* @dev: targeted interface * @dev: targeted interface
*/ */
int l3mdev_master_ifindex_rcu(struct net_device *dev) int l3mdev_master_ifindex_rcu(const struct net_device *dev)
{ {
int ifindex = 0; int ifindex = 0;
...@@ -28,8 +28,15 @@ int l3mdev_master_ifindex_rcu(struct net_device *dev) ...@@ -28,8 +28,15 @@ int l3mdev_master_ifindex_rcu(struct net_device *dev)
ifindex = dev->ifindex; ifindex = dev->ifindex;
} else if (netif_is_l3_slave(dev)) { } else if (netif_is_l3_slave(dev)) {
struct net_device *master; struct net_device *master;
struct net_device *_dev = (struct net_device *)dev;
master = netdev_master_upper_dev_get_rcu(dev); /* netdev_master_upper_dev_get_rcu calls
* list_first_or_null_rcu to walk the upper dev list.
* list_first_or_null_rcu does not handle a const arg. We aren't
* making changes, just want the master device from that list so
* typecast to remove the const
*/
master = netdev_master_upper_dev_get_rcu(_dev);
if (master) if (master)
ifindex = master->ifindex; ifindex = master->ifindex;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment