Module Name:    src
Committed By:   riastradh
Date:           Sun Jul 28 14:38:19 UTC 2024

Modified Files:
        src/sys/net: if_wg.c

Log Message:
wg(4): Fix logic to ensure session initiation is underway.

Previously, wg_task_send_init_message would call
wg_send_handshake_msg_init if either:

(a) the stable session is UNKNOWN, meaning a session has not yet been
    established, either by us or by the peer (but it could be in
    progress); or

(b) the stable session is not UNKNOWN but the unstable session is
    _not_ INIT_ACTIVE, meaning there is an established session and we
    are not currently initiating a new session.

If wg_output (or wgintr) found no established session while there was
already a session being initiated, we may only enter
wg_task_send_init_message after the session is already established,
and trigger spurious reinitiation.

Instead, create a separate flag to indicate whether it is mandatory
to rekey because limits have passed.  Then create a session only if:

(a) the stable session is not ESTABLISHED, or
(b) the mandatory rekey flag is not set,

and clear the mandatory rekey flag.

While here, arrange to do rekey-after-time on tx, not on callout.  If
there's no data to tx, we shouldn't reinitiate a session -- we should
stay quiet on the network.

PR kern/55729: net/if_wg/t_misc:wg_rekey test case fails
PR kern/56252: wg(4) state machine has race conditions
PR kern/58463: if_wg does not work when idle.


To generate a diff of this commit:
cvs rdiff -u -r1.94 -r1.95 src/sys/net/if_wg.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_wg.c
diff -u src/sys/net/if_wg.c:1.94 src/sys/net/if_wg.c:1.95
--- src/sys/net/if_wg.c:1.94	Sun Jul 28 14:37:59 2024
+++ src/sys/net/if_wg.c	Sun Jul 28 14:38:19 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wg.c,v 1.94 2024/07/28 14:37:59 riastradh Exp $	*/
+/*	$NetBSD: if_wg.c,v 1.95 2024/07/28 14:38:19 riastradh Exp $	*/
 
 /*
  * Copyright (C) Ryota Ozaki <ozaki.ry...@gmail.com>
@@ -41,7 +41,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wg.c,v 1.94 2024/07/28 14:37:59 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wg.c,v 1.95 2024/07/28 14:38:19 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_altq_enabled.h"
@@ -612,12 +612,13 @@ struct wg_peer {
 
 	struct timespec		wgp_last_handshake_time;
 
-	callout_t		wgp_rekey_timer;
 	callout_t		wgp_handshake_timeout_timer;
 	callout_t		wgp_session_dtor_timer;
 
 	time_t			wgp_handshake_start_time;
 
+	volatile unsigned	wgp_force_rekey;
+
 	int			wgp_n_allowedips;
 	struct wg_allowedip	wgp_allowedips[WG_ALLOWEDIPS];
 
@@ -735,7 +736,6 @@ static struct wg_session *
 static void	wg_update_endpoint_if_necessary(struct wg_peer *,
 		    const struct sockaddr *);
 
-static void	wg_schedule_rekey_timer(struct wg_peer *);
 static void	wg_schedule_session_dtor_timer(struct wg_peer *);
 
 static bool	wg_is_underload(struct wg_softc *, struct wg_peer *, int);
@@ -2146,8 +2146,6 @@ wg_handle_msg_resp(struct wg_softc *wg, 
 	wgp->wgp_last_sent_mac1_valid = false;
 	wgp->wgp_last_sent_cookie_valid = false;
 
-	wg_schedule_rekey_timer(wgp);
-
 	wg_update_endpoint_if_necessary(wgp, src);
 
 	/*
@@ -2438,14 +2436,6 @@ wg_lookup_session_by_index(struct wg_sof
 }
 
 static void
-wg_schedule_rekey_timer(struct wg_peer *wgp)
-{
-	int timeout = MIN(wg_rekey_after_time, (unsigned)(INT_MAX / hz));
-
-	callout_schedule(&wgp->wgp_rekey_timer, timeout * hz);
-}
-
-static void
 wg_send_keepalive_msg(struct wg_peer *wgp, struct wg_session *wgs)
 {
 	struct mbuf *m;
@@ -3146,16 +3136,22 @@ wg_task_send_init_message(struct wg_soft
 		return;
 	}
 
+	/*
+	 * If we already have an established session, there's no need
+	 * to initiate a new one -- unless the rekey-after-time or
+	 * rekey-after-messages limits have passed.
+	 */
 	wgs = wgp->wgp_session_stable;
-	if (wgs->wgs_state == WGS_STATE_UNKNOWN) {
-		/* XXX What if the unstable session is already INIT_ACTIVE?  */
-		wg_send_handshake_msg_init(wg, wgp);
-	} else {
-		/* rekey */
-		wgs = wgp->wgp_session_unstable;
-		if (wgs->wgs_state != WGS_STATE_INIT_ACTIVE)
-			wg_send_handshake_msg_init(wg, wgp);
-	}
+	if (wgs->wgs_state == WGS_STATE_ESTABLISHED &&
+	    !atomic_swap_uint(&wgp->wgp_force_rekey, 0))
+		return;
+
+	/*
+	 * Ensure we're initiating a new session.  If the unstable
+	 * session is already INIT_ACTIVE or INIT_PASSIVE, this does
+	 * nothing.
+	 */
+	wg_send_handshake_msg_init(wg, wgp);
 }
 
 static void
@@ -3576,14 +3572,6 @@ next0:		m_freem(m);
 }
 
 static void
-wg_rekey_timer(void *arg)
-{
-	struct wg_peer *wgp = arg;
-
-	wg_schedule_peer_task(wgp, WGP_TASK_SEND_INIT_MESSAGE);
-}
-
-static void
 wg_purge_pending_packets(struct wg_peer *wgp)
 {
 	struct mbuf *m;
@@ -3611,8 +3599,6 @@ wg_alloc_peer(struct wg_softc *wg)
 	wgp = kmem_zalloc(sizeof(*wgp), KM_SLEEP);
 
 	wgp->wgp_sc = wg;
-	callout_init(&wgp->wgp_rekey_timer, CALLOUT_MPSAFE);
-	callout_setfunc(&wgp->wgp_rekey_timer, wg_rekey_timer, wgp);
 	callout_init(&wgp->wgp_handshake_timeout_timer, CALLOUT_MPSAFE);
 	callout_setfunc(&wgp->wgp_handshake_timeout_timer,
 	    wg_handshake_timeout_timer, wgp);
@@ -3690,7 +3676,6 @@ wg_destroy_peer(struct wg_peer *wgp)
 	wg_purge_pending_packets(wgp);
 
 	/* Halt all packet processing and timeouts.  */
-	callout_halt(&wgp->wgp_rekey_timer, NULL);
 	callout_halt(&wgp->wgp_handshake_timeout_timer, NULL);
 	callout_halt(&wgp->wgp_session_dtor_timer, NULL);
 
@@ -4302,7 +4287,8 @@ wg_send_data_msg(struct wg_peer *wgp, st
 		if_statadd(ifp, if_obytes, mlen);
 		if_statinc(ifp, if_opackets);
 		if (wgs->wgs_is_initiator &&
-		    wgs->wgs_time_last_data_sent == 0) {
+		    ((time_uptime - wgs->wgs_time_established) >=
+			wg_rekey_after_time)) {
 			/*
 			 * [W] 6.2 Transport Message Limits
 			 * "if a peer is the initiator of a current secure
@@ -4311,7 +4297,9 @@ wg_send_data_msg(struct wg_peer *wgp, st
 			 *  transmitting a transport data message, the current
 			 *  secure session is REKEY-AFTER-TIME seconds old,"
 			 */
-			wg_schedule_rekey_timer(wgp);
+			WG_TRACE("rekey after time");
+			atomic_store_relaxed(&wgp->wgp_force_rekey, 1);
+			wg_schedule_peer_task(wgp, WGP_TASK_SEND_INIT_MESSAGE);
 		}
 		wgs->wgs_time_last_data_sent = time_uptime;
 		if (wg_session_get_send_counter(wgs) >=
@@ -4323,6 +4311,8 @@ wg_send_data_msg(struct wg_peer *wgp, st
 			 *  5.4.2), after it has sent REKEY-AFTER-MESSAGES
 			 *  transport data messages..."
 			 */
+			WG_TRACE("rekey after messages");
+			atomic_store_relaxed(&wgp->wgp_force_rekey, 1);
 			wg_schedule_peer_task(wgp, WGP_TASK_SEND_INIT_MESSAGE);
 		}
 	}

Reply via email to