summaryrefslogtreecommitdiff
path: root/include/net
diff options
context:
space:
mode:
authorNicolas Dichtel <nicolas.dichtel@6wind.com>2024-07-10 10:14:28 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-08-14 13:59:03 +0200
commite7f3e5fb20c5b48dcab4011eab778d3353d0beb0 (patch)
tree7cd338fa9fb4020aef3a0707bb1d0f00eeb0fc67 /include/net
parent717c91c6ed73e248de6a15bc53adefb81446c9d0 (diff)
downloadlinux-e7f3e5fb20c5b48dcab4011eab778d3353d0beb0.tar.gz
linux-e7f3e5fb20c5b48dcab4011eab778d3353d0beb0.tar.bz2
linux-e7f3e5fb20c5b48dcab4011eab778d3353d0beb0.zip
ipv6: fix source address selection with route leak
commit 252442f2ae317d109ef0b4b39ce0608c09563042 upstream. By default, an address assigned to the output interface is selected when the source address is not specified. This is problematic when a route, configured in a vrf, uses an interface from another vrf (aka route leak). The original vrf does not own the selected source address. Let's add a check against the output interface and call the appropriate function to select the source address. CC: stable@vger.kernel.org Fixes: 0d240e7811c4 ("net: vrf: Implement get_saddr for IPv6") Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> Link: https://patch.msgid.link/20240710081521.3809742-3-nicolas.dichtel@6wind.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'include/net')
-rw-r--r--include/net/ip6_route.h20
1 files changed, 14 insertions, 6 deletions
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index b32539bb0fb0..61cfc8891f82 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -128,18 +128,26 @@ void rt6_age_exceptions(struct fib6_info *f6i, struct fib6_gc_args *gc_args,
static inline int ip6_route_get_saddr(struct net *net, struct fib6_info *f6i,
const struct in6_addr *daddr,
- unsigned int prefs,
+ unsigned int prefs, int l3mdev_index,
struct in6_addr *saddr)
{
+ struct net_device *l3mdev;
+ struct net_device *dev;
+ bool same_vrf;
int err = 0;
- if (f6i && f6i->fib6_prefsrc.plen) {
+ rcu_read_lock();
+
+ l3mdev = dev_get_by_index_rcu(net, l3mdev_index);
+ if (!f6i || !f6i->fib6_prefsrc.plen || l3mdev)
+ dev = f6i ? fib6_info_nh_dev(f6i) : NULL;
+ same_vrf = !l3mdev || l3mdev_master_dev_rcu(dev) == l3mdev;
+ if (f6i && f6i->fib6_prefsrc.plen && same_vrf)
*saddr = f6i->fib6_prefsrc.addr;
- } else {
- struct net_device *dev = f6i ? fib6_info_nh_dev(f6i) : NULL;
+ else
+ err = ipv6_dev_get_saddr(net, same_vrf ? dev : l3mdev, daddr, prefs, saddr);
- err = ipv6_dev_get_saddr(net, dev, daddr, prefs, saddr);
- }
+ rcu_read_unlock();
return err;
}