From fe530cf25aff19d2bd54480f14328d5d1a767ea2 Mon Sep 17 00:00:00 2001 From: riccardom Date: Tue, 4 Aug 2026 18:33:13 +0200 Subject: [PATCH] Skip default port send in signal proto --- client/internal/peer/conn.go | 3 ++- client/internal/peer/handshaker.go | 4 +++- client/internal/pqkem/manager.go | 6 +++--- client/internal/pqkem_adapter.go | 32 ++++++++++++++++++++++-------- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index c13d59051..f5f8efea4 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -93,7 +93,8 @@ type PQHandshaker interface { // peer-config time (the pull path). ok is false until one has been derived. PSK(remoteKey string) (wgtypes.Key, bool) // SetRemoteAddr registers the peer's data-path endpoint learned from signalling: - // its WG overlay IP with the advertised pq UDP port. + // its WG overlay IP with the announced pq UDP port (port 0 means the peer omitted + // it and is on the default port). SetRemoteAddr(remoteKey string, addr netip.AddrPort) // OnDataPathRekeyed signals a fresh WireGuard handshake for the peer; it clocks the // next chained PSK rotation pushed over the data path. sinceActivity is how long diff --git a/client/internal/peer/handshaker.go b/client/internal/peer/handshaker.go index 165d9db2d..0434709aa 100644 --- a/client/internal/peer/handshaker.go +++ b/client/internal/peer/handshaker.go @@ -183,9 +183,11 @@ func (h *Handshaker) Listen(ctx context.Context) { // pqRegisterEndpoint feeds the post-quantum handshaker the peer's data-path endpoint // (its WG overlay IP plus the advertised pq UDP port) learned from a remote offer/answer. func (h *Handshaker) pqRegisterEndpoint(remotePort int) { - if h.config.PQ == nil || remotePort <= 0 || remotePort > 65535 || len(h.config.WgConfig.AllowedIps) == 0 { + if h.config.PQ == nil || remotePort < 0 || remotePort > 65535 || len(h.config.WgConfig.AllowedIps) == 0 { return } + // remotePort may be 0 (the peer omitted it, meaning the default port); the adapter + // resolves 0 to DefaultPort. addr := netip.AddrPortFrom(h.config.WgConfig.AllowedIps[0].Addr(), uint16(remotePort)) h.config.PQ.SetRemoteAddr(h.config.Key, addr) } diff --git a/client/internal/pqkem/manager.go b/client/internal/pqkem/manager.go index 205194ebd..9bb108dfb 100644 --- a/client/internal/pqkem/manager.go +++ b/client/internal/pqkem/manager.go @@ -186,8 +186,9 @@ func (m *Manager) trace(msg string, args ...any) { } // AddPeer registers where a peer's data-path messages are sent and received: its -// overlay endpoint (IP:port). A peer that advertises a PQ endpoint is, by that fact, -// running the KEM, so it is marked capable. +// overlay endpoint (IP:port). This is pure routing and says nothing about capability — +// PQ capability is decided solely from the peer's KEM payload (see processOffer / +// processAnswer / MarkNonCapable), never from an endpoint or port. func (m *Manager) AddPeer(remoteID RemoteID, endpoint netip.AddrPort) { if !endpoint.IsValid() { return @@ -198,7 +199,6 @@ func (m *Manager) AddPeer(remoteID RemoteID, endpoint netip.AddrPort) { } m.peerAddrs[remoteID] = endpoint m.peersByAddr[endpoint] = remoteID - m.capable[remoteID] = true m.mu.Unlock() } diff --git a/client/internal/pqkem_adapter.go b/client/internal/pqkem_adapter.go index 332dc4da4..82d2981de 100644 --- a/client/internal/pqkem_adapter.go +++ b/client/internal/pqkem_adapter.go @@ -54,12 +54,22 @@ type pqHandshaker struct { mgr *pqkem.Manager } +// announcedPort is the PQ data-path port to advertise to peers. It is omitted (0) when +// the manager is on DefaultPort, since peers assume the default when no port is sent; +// only a non-default (collision-forced) port is announced explicitly. +func (p pqHandshaker) announcedPort() int { + if port := p.mgr.LocalPort(); port != DefaultPort { + return port + } + return 0 +} + func (p pqHandshaker) OfferPayload(remoteKey string) ([]byte, int) { payload, err := p.mgr.SignalOffer(pqkem.RemoteID(remoteKey)) if err != nil { log.Warnf("pqkem: build offer for %s: %v", remoteKey, err) } - return payload, p.mgr.LocalPort() + return payload, p.announcedPort() } func (p pqHandshaker) AnswerPayload(remoteKey string, recvOffer []byte) ([]byte, int) { @@ -72,13 +82,13 @@ func (p pqHandshaker) AnswerPayload(remoteKey string, recvOffer []byte) ([]byte, if !p.mgr.IsInitiator(pqkem.RemoteID(remoteKey)) { p.mgr.MarkNonCapable(pqkem.RemoteID(remoteKey)) } - return nil, p.mgr.LocalPort() + return nil, p.announcedPort() } payload, err := p.mgr.SignalOnOffer(pqkem.RemoteID(remoteKey), recvOffer) if err != nil { log.Warnf("pqkem: build answer for %s: %v", remoteKey, err) } - return payload, p.mgr.LocalPort() + return payload, p.announcedPort() } func (p pqHandshaker) OnAnswer(remoteKey string, recvAnswer []byte) { @@ -107,14 +117,20 @@ func (p pqHandshaker) PSK(remoteKey string) (wgtypes.Key, bool) { return wgtypes.Key(psk), true } -// SetRemoteAddr registers the peer's data-path endpoint (overlay IP + pq UDP port) -// learned from signalling. Sends only ever fire once the tunnel is up (clocked by -// OnDataPathRekeyed), so registering here is safe even before connection-up. +// SetRemoteAddr registers the peer's data-path endpoint learned from signalling. A +// zero port means the peer omitted it (it is on DefaultPort), so we resolve it here — +// DefaultPort lives in this package, not in peer. Sends only ever fire once the tunnel +// is up (clocked by OnDataPathRekeyed), so registering here is safe even before +// connection-up. func (p pqHandshaker) SetRemoteAddr(remoteKey string, addr netip.AddrPort) { - if !addr.IsValid() || addr.Port() == 0 { + if !addr.Addr().IsValid() { return } - p.mgr.AddPeer(pqkem.RemoteID(remoteKey), addr) + port := addr.Port() + if port == 0 { + port = DefaultPort + } + p.mgr.AddPeer(pqkem.RemoteID(remoteKey), netip.AddrPortFrom(addr.Addr(), port)) } // OnDataPathRekeyed clocks the next chained PSK rotation on a fresh WG handshake.