# HG changeset patch # User Taylor R Campbell # Date 1722305085 0 # Tue Jul 30 02:04:45 2024 +0000 # Branch trunk # Node ID 49d89a34c0a9f23d69004d3657b64e0237f9d0c9 # Parent e7250061dcb13a49d9808fe1a5862ef996863bad # EXP-Topic riastradh-pr58463-wgidle wg(4): Make a rule for who wins when both peers send INIT at once. The rule is that the peer with the lexicographically smaller public key takes priority iff the low order bit of H(peer A pubkey) ^ H(peer B pubkey) ^ H(posix minutes as le64) is 0, and the peer with the lexicographically larger public key takes priority iff the low-order bit is 1. XXX PR kern/NNNNN diff -r e7250061dcb1 -r 49d89a34c0a9 sys/net/if_wg.c --- a/sys/net/if_wg.c Fri Jul 26 12:07:15 2024 +0000 +++ b/sys/net/if_wg.c Tue Jul 30 02:04:45 2024 +0000 @@ -1522,6 +1522,44 @@ wg_fill_msg_init(struct wg_softc *wg, st WG_DLOG("%s: sender=%x\n", __func__, wgs->wgs_local_index); } +/* + * wg_initiator_priority(wg, wgp) + * + * Return true if we claim priority over peer wgp as initiator at + * the moment, false if not. + * + * We jointly flip a coin by computing + * + * H(pubkey A) ^ H(pubkey B) ^ H(posix minutes as le64), + * + * and taking the low-order bit. If our public key + * lexicographically precedes the peer's public key, we claim + * priority iff the bit is 0; otherwise we claim priority iff the + * bit is 1. + */ +static bool +wg_initiator_priority(struct wg_softc *wg, struct wg_peer *wgp) +{ + uint64_t now = htole64(time_second/60); + uint8_t h_min; + uint8_t h_local[BLAKE2S_MAX_DIGEST]; + uint8_t h_peer[BLAKE2S_MAX_DIGEST]; + int borrow; + unsigned i; + + blake2s(&h_min, 1, &now, sizeof(now), NULL, 0); + blake2s(h_local, sizeof(h_local), wg->wg_pubkey, sizeof(wg->wg_pubkey), + NULL, 0); + blake2s(h_peer, sizeof(h_peer), + wgp->wgp_pubkey, sizeof(wgp->wgp_pubkey), + NULL, 0); + + for (borrow = 0, i = 0; i < BLAKE2S_MAX_DIGEST; i++) + borrow = (h_local[i] - h_peer[i] + borrow) >> 8; + + return 1 & (h_local[0] ^ h_peer[0] ^ h_min ^ borrow); +} + static void __noinline wg_handle_msg_init(struct wg_softc *wg, const struct wg_msg_init *wgmi, const struct sockaddr *src) @@ -1685,10 +1723,17 @@ wg_handle_msg_init(struct wg_softc *wg, switch (wgs->wgs_state) { case WGS_STATE_UNKNOWN: /* new session initiated by peer */ break; - case WGS_STATE_INIT_ACTIVE: /* we're already initiating, drop */ - /* XXX Who wins if both sides send INIT? */ - WG_TRACE("Session already initializing, ignoring the message"); - goto out; + case WGS_STATE_INIT_ACTIVE: /* we're already initiating */ + if (wg_initiator_priority(wg, wgp)) { + WG_TRACE("Session already initializing," + " ignoring the message"); + goto out; + } + WG_TRACE("Yielding session initiation to peer"); + wg_put_session_index(wg, wgs); + KASSERTMSG(wgs->wgs_state == WGS_STATE_UNKNOWN, "state=%d", + wgs->wgs_state); + break; case WGS_STATE_INIT_PASSIVE: /* peer is retrying, start over */ WG_TRACE("Session already initializing, destroying old states"); /*