diff --git a/client/internal/peer/conn_pq_test.go b/client/internal/peer/conn_pq_test.go index c4edb3424..b813a44a0 100644 --- a/client/internal/peer/conn_pq_test.go +++ b/client/internal/peer/conn_pq_test.go @@ -17,14 +17,14 @@ type fakePQ struct { ok bool } -func (f fakePQ) OfferPayload(string) ([]byte, int) { return nil, 0 } -func (f fakePQ) ShouldSendBootstrapOffer(string) bool { return false } -func (f fakePQ) AnswerPayload(string, []byte) ([]byte, int) { return nil, 0 } -func (f fakePQ) OnAnswer(string, []byte) {} -func (f fakePQ) PSK(string) (wgtypes.Key, bool) { return f.psk, f.ok } -func (f fakePQ) SetRemoteAddr(string, netip.AddrPort) {} -func (f fakePQ) OnDataPathRekeyed(string, time.Duration) {} -func (f fakePQ) OnDataPathDown(string) {} +func (f fakePQ) OfferPayload(string) ([]byte, uint16) { return nil, 0 } +func (f fakePQ) ShouldSendBootstrapOffer(string) bool { return false } +func (f fakePQ) AnswerPayload(string, []byte) ([]byte, uint16) { return nil, 0 } +func (f fakePQ) OnAnswer(string, []byte) {} +func (f fakePQ) PSK(string) (wgtypes.Key, bool) { return f.psk, f.ok } +func (f fakePQ) SetRemoteAddr(string, netip.AddrPort) {} +func (f fakePQ) OnDataPathRekeyed(string, time.Duration) {} +func (f fakePQ) OnDataPathDown(string) {} // TestConn_presharedKey_PQ covers the post-quantum branch of presharedKey across the // three states that matter: a derived PSK is programmed, and — before one exists — diff --git a/client/internal/peer/handshaker.go b/client/internal/peer/handshaker.go index a18e59a69..2c978fe81 100644 --- a/client/internal/peer/handshaker.go +++ b/client/internal/peer/handshaker.go @@ -235,23 +235,17 @@ func (h *Handshaker) pqRegisterEndpoint(remotePort uint16) { h.config.PQ.SetRemoteAddr(h.config.Key, netip.AddrPortFrom(overlay, remotePort)) } -// pqPeerOverlayAddr picks the peer's overlay address for the pq data path. The pq -// transport binds on the local WG overlay IPv4, so an IPv4 AllowedIP is preferred; a v6 -// prefix is used only when this interface actually has a v6 overlay, and never in place -// of a usable v4. Returns false when no suitable address exists. +// pqPeerOverlayAddr returns the peer's IPv4 overlay address for the pq data path. A +// RemotePeerConfig carries only the peer overlay (v4 /32, optionally v6 /128) — served +// routes are programmed on WireGuard separately and never land in WgConfig.AllowedIps — +// so AllowedIps[0] is the v4 overlay, matching conn.AllowedIP(). The transport is v4 +// (the overlay always has v4; v6 is additive). Returns false when no v4 overlay exists. func (h *Handshaker) pqPeerOverlayAddr() (netip.Addr, bool) { - var v6 netip.Addr - for _, p := range h.config.WgConfig.AllowedIps { - a := p.Addr().Unmap() - if a.Is4() { - return a, true - } - if a.Is6() && !v6.IsValid() { - v6 = a - } + if len(h.config.WgConfig.AllowedIps) == 0 { + return netip.Addr{}, false } - if v6.IsValid() && h.config.WgConfig.WgInterface.Address().HasIPv6() { - return v6, true + if a := h.config.WgConfig.AllowedIps[0].Addr().Unmap(); a.Is4() { + return a, true } return netip.Addr{}, false } diff --git a/client/internal/pqkem_transport.go b/client/internal/pqkem_transport.go index 09eb53720..920e655c4 100644 --- a/client/internal/pqkem_transport.go +++ b/client/internal/pqkem_transport.go @@ -30,19 +30,15 @@ func newPQTransport(overlayIP netip.Addr) (*pqTransport, error) { if !overlayIP.IsValid() { return nil, fmt.Errorf("invalid overlay IP for pqkem transport") } - // Unmap an IPv4-mapped IPv6 address so AsSlice() yields 4 bytes, and pick the UDP - // network matching the overlay family (a hardcoded "udp4" fails on a v6 overlay and - // on a mapped v4 whose AsSlice() is 16 bytes). + // The WG overlay always carries an IPv4 address (v6 is additive, never standalone), + // so the transport binds over IPv4. Unmap first so AsSlice() yields 4 bytes for an + // IPv4-mapped IPv6 address (a hardcoded "udp4" would otherwise fail on its 16 bytes). overlayIP = overlayIP.Unmap() - network := "udp6" - if overlayIP.Is4() { - network = "udp4" - } ip := net.IP(overlayIP.AsSlice()) - conn, err := net.ListenUDP(network, &net.UDPAddr{IP: ip, Port: DefaultPort}) + conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: ip, Port: DefaultPort}) if err != nil { log.Debugf("pqkem: default port %d unavailable on %s (%v), using an ephemeral port", DefaultPort, overlayIP, err) - conn, err = net.ListenUDP(network, &net.UDPAddr{IP: ip, Port: 0}) + conn, err = net.ListenUDP("udp4", &net.UDPAddr{IP: ip, Port: 0}) if err != nil { return nil, fmt.Errorf("bind pqkem udp on overlay %s: %w", overlayIP, err) }