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

Merge branch 'wireguard-fixes'

Jason A. Donenfeld says:

====================
wireguard fixes for 5.7-rc5

With Ubuntu and Debian having backported this into their kernels, we're
finally seeing testing from places we hadn't seen prior, which is nice.
With that comes more fixes:

1) The CI for PPC64 was running with extremely small stacks for 64-bit,
   causing spurious crashes in surprising places.

2) There's was an old leftover routing loop restriction, which no longer
   makes sense given the queueing architecture, and was causing problems
   for people who really did want nested routing.

3) Not yielding our kthread on CONFIG_PREEMPT_VOLUNTARY systems caused
   RCU stalls and other issues, reported by Wang Jian, with the fix
   suggested by Sultan Alsawaf.

4) Clang spewed warnings in a selftest for CONFIG_IPV6=n, reported by
   Arnd Bergmann.

5) A complicated if statement was simplified to an assignment while also
   making the likely/unlikely hinting more correct and simple, and
   increasing readability, suggested by Sultan.

Patches (2) and (3) have Fixes: lines and are probably good candidates
for stable.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6f5c27f9 243f2148
...@@ -226,21 +226,20 @@ void wg_packet_handshake_receive_worker(struct work_struct *work) ...@@ -226,21 +226,20 @@ void wg_packet_handshake_receive_worker(struct work_struct *work)
static void keep_key_fresh(struct wg_peer *peer) static void keep_key_fresh(struct wg_peer *peer)
{ {
struct noise_keypair *keypair; struct noise_keypair *keypair;
bool send = false; bool send;
if (peer->sent_lastminute_handshake) if (peer->sent_lastminute_handshake)
return; return;
rcu_read_lock_bh(); rcu_read_lock_bh();
keypair = rcu_dereference_bh(peer->keypairs.current_keypair); keypair = rcu_dereference_bh(peer->keypairs.current_keypair);
if (likely(keypair && READ_ONCE(keypair->sending.is_valid)) && send = keypair && READ_ONCE(keypair->sending.is_valid) &&
keypair->i_am_the_initiator && keypair->i_am_the_initiator &&
unlikely(wg_birthdate_has_expired(keypair->sending.birthdate, wg_birthdate_has_expired(keypair->sending.birthdate,
REJECT_AFTER_TIME - KEEPALIVE_TIMEOUT - REKEY_TIMEOUT))) REJECT_AFTER_TIME - KEEPALIVE_TIMEOUT - REKEY_TIMEOUT);
send = true;
rcu_read_unlock_bh(); rcu_read_unlock_bh();
if (send) { if (unlikely(send)) {
peer->sent_lastminute_handshake = true; peer->sent_lastminute_handshake = true;
wg_packet_send_queued_handshake_initiation(peer, false); wg_packet_send_queued_handshake_initiation(peer, false);
} }
...@@ -516,6 +515,8 @@ void wg_packet_decrypt_worker(struct work_struct *work) ...@@ -516,6 +515,8 @@ void wg_packet_decrypt_worker(struct work_struct *work)
&PACKET_CB(skb)->keypair->receiving)) ? &PACKET_CB(skb)->keypair->receiving)) ?
PACKET_STATE_CRYPTED : PACKET_STATE_DEAD; PACKET_STATE_CRYPTED : PACKET_STATE_DEAD;
wg_queue_enqueue_per_peer_napi(skb, state); wg_queue_enqueue_per_peer_napi(skb, state);
if (need_resched())
cond_resched();
} }
} }
......
...@@ -120,9 +120,9 @@ bool __init wg_ratelimiter_selftest(void) ...@@ -120,9 +120,9 @@ bool __init wg_ratelimiter_selftest(void)
enum { TRIALS_BEFORE_GIVING_UP = 5000 }; enum { TRIALS_BEFORE_GIVING_UP = 5000 };
bool success = false; bool success = false;
int test = 0, trials; int test = 0, trials;
struct sk_buff *skb4, *skb6; struct sk_buff *skb4, *skb6 = NULL;
struct iphdr *hdr4; struct iphdr *hdr4;
struct ipv6hdr *hdr6; struct ipv6hdr *hdr6 = NULL;
if (IS_ENABLED(CONFIG_KASAN) || IS_ENABLED(CONFIG_UBSAN)) if (IS_ENABLED(CONFIG_KASAN) || IS_ENABLED(CONFIG_UBSAN))
return true; return true;
......
...@@ -124,20 +124,17 @@ void wg_packet_send_handshake_cookie(struct wg_device *wg, ...@@ -124,20 +124,17 @@ void wg_packet_send_handshake_cookie(struct wg_device *wg,
static void keep_key_fresh(struct wg_peer *peer) static void keep_key_fresh(struct wg_peer *peer)
{ {
struct noise_keypair *keypair; struct noise_keypair *keypair;
bool send = false; bool send;
rcu_read_lock_bh(); rcu_read_lock_bh();
keypair = rcu_dereference_bh(peer->keypairs.current_keypair); keypair = rcu_dereference_bh(peer->keypairs.current_keypair);
if (likely(keypair && READ_ONCE(keypair->sending.is_valid)) && send = keypair && READ_ONCE(keypair->sending.is_valid) &&
(unlikely(atomic64_read(&keypair->sending.counter.counter) > (atomic64_read(&keypair->sending.counter.counter) > REKEY_AFTER_MESSAGES ||
REKEY_AFTER_MESSAGES) || (keypair->i_am_the_initiator &&
(keypair->i_am_the_initiator && wg_birthdate_has_expired(keypair->sending.birthdate, REKEY_AFTER_TIME)));
unlikely(wg_birthdate_has_expired(keypair->sending.birthdate,
REKEY_AFTER_TIME)))))
send = true;
rcu_read_unlock_bh(); rcu_read_unlock_bh();
if (send) if (unlikely(send))
wg_packet_send_queued_handshake_initiation(peer, false); wg_packet_send_queued_handshake_initiation(peer, false);
} }
...@@ -281,6 +278,8 @@ void wg_packet_tx_worker(struct work_struct *work) ...@@ -281,6 +278,8 @@ void wg_packet_tx_worker(struct work_struct *work)
wg_noise_keypair_put(keypair, false); wg_noise_keypair_put(keypair, false);
wg_peer_put(peer); wg_peer_put(peer);
if (need_resched())
cond_resched();
} }
} }
...@@ -304,6 +303,8 @@ void wg_packet_encrypt_worker(struct work_struct *work) ...@@ -304,6 +303,8 @@ void wg_packet_encrypt_worker(struct work_struct *work)
} }
wg_queue_enqueue_per_peer(&PACKET_PEER(first)->tx_queue, first, wg_queue_enqueue_per_peer(&PACKET_PEER(first)->tx_queue, first,
state); state);
if (need_resched())
cond_resched();
} }
} }
......
...@@ -76,12 +76,6 @@ static int send4(struct wg_device *wg, struct sk_buff *skb, ...@@ -76,12 +76,6 @@ static int send4(struct wg_device *wg, struct sk_buff *skb,
net_dbg_ratelimited("%s: No route to %pISpfsc, error %d\n", net_dbg_ratelimited("%s: No route to %pISpfsc, error %d\n",
wg->dev->name, &endpoint->addr, ret); wg->dev->name, &endpoint->addr, ret);
goto err; goto err;
} else if (unlikely(rt->dst.dev == skb->dev)) {
ip_rt_put(rt);
ret = -ELOOP;
net_dbg_ratelimited("%s: Avoiding routing loop to %pISpfsc\n",
wg->dev->name, &endpoint->addr);
goto err;
} }
if (cache) if (cache)
dst_cache_set_ip4(cache, &rt->dst, fl.saddr); dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
...@@ -149,12 +143,6 @@ static int send6(struct wg_device *wg, struct sk_buff *skb, ...@@ -149,12 +143,6 @@ static int send6(struct wg_device *wg, struct sk_buff *skb,
net_dbg_ratelimited("%s: No route to %pISpfsc, error %d\n", net_dbg_ratelimited("%s: No route to %pISpfsc, error %d\n",
wg->dev->name, &endpoint->addr, ret); wg->dev->name, &endpoint->addr, ret);
goto err; goto err;
} else if (unlikely(dst->dev == skb->dev)) {
dst_release(dst);
ret = -ELOOP;
net_dbg_ratelimited("%s: Avoiding routing loop to %pISpfsc\n",
wg->dev->name, &endpoint->addr);
goto err;
} }
if (cache) if (cache)
dst_cache_set_ip6(cache, dst, &fl.saddr); dst_cache_set_ip6(cache, dst, &fl.saddr);
......
...@@ -48,8 +48,11 @@ cleanup() { ...@@ -48,8 +48,11 @@ cleanup() {
exec 2>/dev/null exec 2>/dev/null
printf "$orig_message_cost" > /proc/sys/net/core/message_cost printf "$orig_message_cost" > /proc/sys/net/core/message_cost
ip0 link del dev wg0 ip0 link del dev wg0
ip0 link del dev wg1
ip1 link del dev wg0 ip1 link del dev wg0
ip1 link del dev wg1
ip2 link del dev wg0 ip2 link del dev wg0
ip2 link del dev wg1
local to_kill="$(ip netns pids $netns0) $(ip netns pids $netns1) $(ip netns pids $netns2)" local to_kill="$(ip netns pids $netns0) $(ip netns pids $netns1) $(ip netns pids $netns2)"
[[ -n $to_kill ]] && kill $to_kill [[ -n $to_kill ]] && kill $to_kill
pp ip netns del $netns1 pp ip netns del $netns1
...@@ -77,18 +80,20 @@ ip0 link set wg0 netns $netns2 ...@@ -77,18 +80,20 @@ ip0 link set wg0 netns $netns2
key1="$(pp wg genkey)" key1="$(pp wg genkey)"
key2="$(pp wg genkey)" key2="$(pp wg genkey)"
key3="$(pp wg genkey)" key3="$(pp wg genkey)"
key4="$(pp wg genkey)"
pub1="$(pp wg pubkey <<<"$key1")" pub1="$(pp wg pubkey <<<"$key1")"
pub2="$(pp wg pubkey <<<"$key2")" pub2="$(pp wg pubkey <<<"$key2")"
pub3="$(pp wg pubkey <<<"$key3")" pub3="$(pp wg pubkey <<<"$key3")"
pub4="$(pp wg pubkey <<<"$key4")"
psk="$(pp wg genpsk)" psk="$(pp wg genpsk)"
[[ -n $key1 && -n $key2 && -n $psk ]] [[ -n $key1 && -n $key2 && -n $psk ]]
configure_peers() { configure_peers() {
ip1 addr add 192.168.241.1/24 dev wg0 ip1 addr add 192.168.241.1/24 dev wg0
ip1 addr add fd00::1/24 dev wg0 ip1 addr add fd00::1/112 dev wg0
ip2 addr add 192.168.241.2/24 dev wg0 ip2 addr add 192.168.241.2/24 dev wg0
ip2 addr add fd00::2/24 dev wg0 ip2 addr add fd00::2/112 dev wg0
n1 wg set wg0 \ n1 wg set wg0 \
private-key <(echo "$key1") \ private-key <(echo "$key1") \
...@@ -230,9 +235,38 @@ n1 ping -W 1 -c 1 192.168.241.2 ...@@ -230,9 +235,38 @@ n1 ping -W 1 -c 1 192.168.241.2
n1 wg set wg0 private-key <(echo "$key3") n1 wg set wg0 private-key <(echo "$key3")
n2 wg set wg0 peer "$pub3" preshared-key <(echo "$psk") allowed-ips 192.168.241.1/32 peer "$pub1" remove n2 wg set wg0 peer "$pub3" preshared-key <(echo "$psk") allowed-ips 192.168.241.1/32 peer "$pub1" remove
n1 ping -W 1 -c 1 192.168.241.2 n1 ping -W 1 -c 1 192.168.241.2
n2 wg set wg0 peer "$pub3" remove
# Test that we can route wg through wg
ip1 addr flush dev wg0
ip2 addr flush dev wg0
ip1 addr add fd00::5:1/112 dev wg0
ip2 addr add fd00::5:2/112 dev wg0
n1 wg set wg0 private-key <(echo "$key1") peer "$pub2" preshared-key <(echo "$psk") allowed-ips fd00::5:2/128 endpoint 127.0.0.1:2
n2 wg set wg0 private-key <(echo "$key2") listen-port 2 peer "$pub1" preshared-key <(echo "$psk") allowed-ips fd00::5:1/128 endpoint 127.212.121.99:9998
ip1 link add wg1 type wireguard
ip2 link add wg1 type wireguard
ip1 addr add 192.168.241.1/24 dev wg1
ip1 addr add fd00::1/112 dev wg1
ip2 addr add 192.168.241.2/24 dev wg1
ip2 addr add fd00::2/112 dev wg1
ip1 link set mtu 1340 up dev wg1
ip2 link set mtu 1340 up dev wg1
n1 wg set wg1 listen-port 5 private-key <(echo "$key3") peer "$pub4" allowed-ips 192.168.241.2/32,fd00::2/128 endpoint [fd00::5:2]:5
n2 wg set wg1 listen-port 5 private-key <(echo "$key4") peer "$pub3" allowed-ips 192.168.241.1/32,fd00::1/128 endpoint [fd00::5:1]:5
tests
# Try to set up a routing loop between the two namespaces
ip1 link set netns $netns0 dev wg1
ip0 addr add 192.168.241.1/24 dev wg1
ip0 link set up dev wg1
n0 ping -W 1 -c 1 192.168.241.2
n1 wg set wg0 peer "$pub2" endpoint 192.168.241.2:7
ip2 link del wg0
ip2 link del wg1
! n0 ping -W 1 -c 10 -f 192.168.241.2 || false # Should not crash kernel
ip0 link del wg1
ip1 link del wg0 ip1 link del wg0
ip2 link del wg0
# Test using NAT. We now change the topology to this: # Test using NAT. We now change the topology to this:
# ┌────────────────────────────────────────┐ ┌────────────────────────────────────────────────┐ ┌────────────────────────────────────────┐ # ┌────────────────────────────────────────┐ ┌────────────────────────────────────────────────┐ ┌────────────────────────────────────────┐
...@@ -282,6 +316,20 @@ pp sleep 3 ...@@ -282,6 +316,20 @@ pp sleep 3
n2 ping -W 1 -c 1 192.168.241.1 n2 ping -W 1 -c 1 192.168.241.1
n1 wg set wg0 peer "$pub2" persistent-keepalive 0 n1 wg set wg0 peer "$pub2" persistent-keepalive 0
# Test that onion routing works, even when it loops
n1 wg set wg0 peer "$pub3" allowed-ips 192.168.242.2/32 endpoint 192.168.241.2:5
ip1 addr add 192.168.242.1/24 dev wg0
ip2 link add wg1 type wireguard
ip2 addr add 192.168.242.2/24 dev wg1
n2 wg set wg1 private-key <(echo "$key3") listen-port 5 peer "$pub1" allowed-ips 192.168.242.1/32
ip2 link set wg1 up
n1 ping -W 1 -c 1 192.168.242.2
ip2 link del wg1
n1 wg set wg0 peer "$pub3" endpoint 192.168.242.2:5
! n1 ping -W 1 -c 1 192.168.242.2 || false # Should not crash kernel
n1 wg set wg0 peer "$pub3" remove
ip1 addr del 192.168.242.1/24 dev wg0
# Do a wg-quick(8)-style policy routing for the default route, making sure vethc has a v6 address to tease out bugs. # Do a wg-quick(8)-style policy routing for the default route, making sure vethc has a v6 address to tease out bugs.
ip1 -6 addr add fc00::9/96 dev vethc ip1 -6 addr add fc00::9/96 dev vethc
ip1 -6 route add default via fc00::1 ip1 -6 route add default via fc00::1
......
...@@ -10,3 +10,4 @@ CONFIG_CMDLINE_BOOL=y ...@@ -10,3 +10,4 @@ CONFIG_CMDLINE_BOOL=y
CONFIG_CMDLINE="console=hvc0 wg.success=hvc1" CONFIG_CMDLINE="console=hvc0 wg.success=hvc1"
CONFIG_SECTION_MISMATCH_WARN_ONLY=y CONFIG_SECTION_MISMATCH_WARN_ONLY=y
CONFIG_FRAME_WARN=1280 CONFIG_FRAME_WARN=1280
CONFIG_THREAD_SHIFT=14
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