Prevent wg(4) stuck on peer destruction.
authormvs <mvs@openbsd.org>
Mon, 23 Oct 2023 10:22:05 +0000 (10:22 +0000)
committermvs <mvs@openbsd.org>
Mon, 23 Oct 2023 10:22:05 +0000 (10:22 +0000)
While interface going down and output stopped, packets could rest in
`if_snd' queue. So the (!ifq_empty(&sc->sc_if.if_snd)) condition will
always be true and wg_peer_destroy() will sleep until interface became
up and stuck packets transmitted.

Check IFF_RUNNING flag within (!ifq_empty(&sc->sc_if.if_snd)) loop in
wg_peer_destroy(). If the flag is not set that means interface is down,
so drain the `if_snd' queue manually to prevent wg_peer_destroy() stuck.

Problem reported and fix tested by Kirill Miazine.

ok bluhm@

sys/net/if_wg.c

index 8567599..b6fc0c8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_wg.c,v 1.31 2023/09/26 15:16:44 sthen Exp $ */
+/*     $OpenBSD: if_wg.c,v 1.32 2023/10/23 10:22:05 mvs Exp $ */
 
 /*
  * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
@@ -509,6 +509,14 @@ wg_peer_destroy(struct wg_peer *peer)
 
        NET_LOCK();
        while (!ifq_empty(&sc->sc_if.if_snd)) {
+               /*
+                * XXX: `if_snd' of stopped interface could still
+                * contain packets
+                */
+               if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING)) {
+                       ifq_purge(&sc->sc_if.if_snd);
+                       continue;
+               }
                NET_UNLOCK();
                tsleep_nsec(sc, PWAIT, "wg_ifq", 1000);
                NET_LOCK();