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); } }