Skip to content

Commit 889f958

Browse files
committed
device: reset peer src endpoint when netns exits
Each peer's endpoint contains a dst_cache entry that takes a reference to another netdev. When the containing namespace exits, we take down the socket and prevent future sockets from being created (by setting creating_net to NULL), which removes that potential reference on the netns. However, it doesn't release references to the netns that a netdev cached in dst_cache might be taking, so the netns still might fail to exit. Since the socket is gimped anyway, we can simply clear all the dst_caches (by way of clearing the endpoint src), which will release all references. However, the current dst_cache_reset function only releases those references lazily. But it turns out that all of our usages of wg_socket_clear_peer_endpoint_src are called from contexts that are not exactly high-speed or bottle-necked. For example, when there's connection difficulty, or when userspace is reconfiguring the interface. And in particular for this patch, when the netns is exiting. So for those cases, it makes more sense to call dst_release immediately. For that, we add a small helper function to dst_cache. This patch also adds a test to netns.sh from Hangbin Liu to ensure this doesn't regress. Test-by: Hangbin Liu <liuhangbin@gmail.com> Reported-by: Xiumei Mu <xmu@redhat.com> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
1 parent 0573f21 commit 889f958

File tree

5 files changed

+60
-2
lines changed

5 files changed

+60
-2
lines changed

src/compat/compat.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,37 @@ static const struct header_ops ip_tunnel_header_ops = { .parse_protocol = ip_tun
11021102
#endif
11031103
#endif
11041104

1105+
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
1106+
#include <net/dst_cache.h>
1107+
struct dst_cache_pcpu {
1108+
unsigned long refresh_ts;
1109+
struct dst_entry *dst;
1110+
u32 cookie;
1111+
union {
1112+
struct in_addr in_saddr;
1113+
struct in6_addr in6_saddr;
1114+
};
1115+
};
1116+
#define COMPAT_HAS_DEFINED_DST_CACHE_PCPU
1117+
static inline void dst_cache_reset_now(struct dst_cache *dst_cache)
1118+
{
1119+
int i;
1120+
1121+
if (!dst_cache->cache)
1122+
return;
1123+
1124+
dst_cache->reset_ts = jiffies;
1125+
for_each_possible_cpu(i) {
1126+
struct dst_cache_pcpu *idst = per_cpu_ptr(dst_cache->cache, i);
1127+
struct dst_entry *dst = idst->dst;
1128+
1129+
idst->cookie = 0;
1130+
idst->dst = NULL;
1131+
dst_release(dst);
1132+
}
1133+
}
1134+
#endif
1135+
11051136
#if defined(ISUBUNTU1604) || defined(ISRHEL7)
11061137
#include <linux/siphash.h>
11071138
#ifndef _WG_LINUX_SIPHASH_H

src/compat/dst_cache/dst_cache.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ static inline u32 rt6_get_cookie(const struct rt6_info *rt)
2727
#endif
2828
#include <uapi/linux/in.h>
2929

30+
#ifndef COMPAT_HAS_DEFINED_DST_CACHE_PCPU
3031
struct dst_cache_pcpu {
3132
unsigned long refresh_ts;
3233
struct dst_entry *dst;
@@ -36,6 +37,7 @@ struct dst_cache_pcpu {
3637
struct in6_addr in6_saddr;
3738
};
3839
};
40+
#endif
3941

4042
static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache,
4143
struct dst_entry *dst, u32 cookie)

src/device.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ static struct rtnl_link_ops link_ops __read_mostly = {
412412
static void wg_netns_pre_exit(struct net *net)
413413
{
414414
struct wg_device *wg;
415+
struct wg_peer *peer;
415416

416417
rtnl_lock();
417418
list_for_each_entry(wg, &device_list, device_list) {
@@ -421,6 +422,8 @@ static void wg_netns_pre_exit(struct net *net)
421422
mutex_lock(&wg->device_update_lock);
422423
rcu_assign_pointer(wg->creating_net, NULL);
423424
wg_socket_reinit(wg, NULL, NULL);
425+
list_for_each_entry(peer, &wg->peer_list, peer_list)
426+
wg_socket_clear_peer_endpoint_src(peer);
424427
mutex_unlock(&wg->device_update_lock);
425428
}
426429
}

src/socket.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ void wg_socket_clear_peer_endpoint_src(struct wg_peer *peer)
308308
{
309309
write_lock_bh(&peer->endpoint_lock);
310310
memset(&peer->endpoint.src6, 0, sizeof(peer->endpoint.src6));
311-
dst_cache_reset(&peer->endpoint_cache);
311+
dst_cache_reset_now(&peer->endpoint_cache);
312312
write_unlock_bh(&peer->endpoint_lock);
313313
}
314314

src/tests/netns.sh

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,28 @@ ip0 link set wg0 up
621621
kill $ncat_pid
622622
ip0 link del wg0
623623

624+
# Ensure that dst_cache references don't outlive netns lifetime
625+
ip1 link add dev wg0 type wireguard
626+
ip2 link add dev wg0 type wireguard
627+
configure_peers
628+
ip1 link add veth1 type veth peer name veth2
629+
ip1 link set veth2 netns $netns2
630+
ip1 addr add fd00:aa::1/64 dev veth1
631+
ip2 addr add fd00:aa::2/64 dev veth2
632+
ip1 link set veth1 up
633+
ip2 link set veth2 up
634+
waitiface $netns1 veth1
635+
waitiface $netns2 veth2
636+
ip1 -6 route add default dev veth1 via fd00:aa::2
637+
ip2 -6 route add default dev veth2 via fd00:aa::1
638+
n1 wg set wg0 peer "$pub2" endpoint [fd00:aa::2]:2
639+
n2 wg set wg0 peer "$pub1" endpoint [fd00:aa::1]:1
640+
n1 ping6 -c 1 fd00::2
641+
pp ip netns delete $netns1
642+
pp ip netns delete $netns2
643+
pp ip netns add $netns1
644+
pp ip netns add $netns2
645+
624646
# Ensure there aren't circular reference loops
625647
ip1 link add wg1 type wireguard
626648
ip2 link add wg2 type wireguard
@@ -639,7 +661,7 @@ while read -t 0.1 -r line 2>/dev/null || [[ $? -ne 142 ]]; do
639661
done < /dev/kmsg
640662
alldeleted=1
641663
for object in "${!objects[@]}"; do
642-
if [[ ${objects["$object"]} != *createddestroyed ]]; then
664+
if [[ ${objects["$object"]} != *createddestroyed && ${objects["$object"]} != *createdcreateddestroyeddestroyed ]]; then
643665
echo "Error: $object: merely ${objects["$object"]}" >&3
644666
alldeleted=0
645667
fi

0 commit comments

Comments
 (0)