diff --git a/client/internal/engine.go b/client/internal/engine.go index cdaa5057c..1ab629466 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -932,6 +932,10 @@ func (e *Engine) removePeer(peerKey string) error { e.connMgr.RemovePeerConn(peerKey) + if e.pqkemManager != nil { + e.pqkemManager.RemovePeer(pqkem.RemoteID(peerKey)) + } + err := e.statusRecorder.RemovePeer(peerKey) if err != nil { log.Warnf("received error when removing peer %s from status recorder: %v", peerKey, err) diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index c454c014b..7c6ba1215 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -90,6 +90,9 @@ type PQHandshaker interface { // PSK returns the peer's latest derived post-quantum PSK to program at WG // peer-config time (the pull path). ok is false until one has been derived. PSK(remoteKey string) (wgtypes.Key, bool) + // SetRemotePort registers the peer's data-path endpoint from signalling: the peer's + // WG overlay IP combined with its advertised pq UDP port. + SetRemotePort(remoteKey string, overlayIP netip.Addr, port int) } // ConnConfig is a peer Connection configuration diff --git a/client/internal/peer/handshaker.go b/client/internal/peer/handshaker.go index 23f744909..767688375 100644 --- a/client/internal/peer/handshaker.go +++ b/client/internal/peer/handshaker.go @@ -130,6 +130,8 @@ func (h *Handshaker) Listen(ctx context.Context) { h.updateRemoteICEState(&remoteOfferAnswer) + h.pqRegisterEndpoint(remoteOfferAnswer.MlkemPort) + if h.relayListener != nil { h.relayListener.Notify(&remoteOfferAnswer) } @@ -152,6 +154,8 @@ func (h *Handshaker) Listen(ctx context.Context) { h.updateRemoteICEState(&remoteOfferAnswer) + h.pqRegisterEndpoint(remoteOfferAnswer.MlkemPort) + if h.relayListener != nil { h.relayListener.Notify(&remoteOfferAnswer) } @@ -170,6 +174,15 @@ 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 || len(h.config.WgConfig.AllowedIps) == 0 { + return + } + h.config.PQ.SetRemotePort(h.config.Key, h.config.WgConfig.AllowedIps[0].Addr(), remotePort) +} + func (h *Handshaker) SendOffer() error { h.mu.Lock() defer h.mu.Unlock() diff --git a/client/internal/pqkem_adapter.go b/client/internal/pqkem_adapter.go index 611e72498..4de9a0ab1 100644 --- a/client/internal/pqkem_adapter.go +++ b/client/internal/pqkem_adapter.go @@ -1,6 +1,8 @@ package internal import ( + "net/netip" + log "github.com/sirupsen/logrus" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" @@ -78,3 +80,13 @@ func (p pqHandshaker) PSK(remoteKey string) (wgtypes.Key, bool) { } return wgtypes.Key(psk), true } + +// SetRemotePort 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. +func (p pqHandshaker) SetRemotePort(remoteKey string, overlayIP netip.Addr, port int) { + if port <= 0 || port > 65535 || !overlayIP.IsValid() { + return + } + p.mgr.AddPeer(pqkem.RemoteID(remoteKey), netip.AddrPortFrom(overlayIP, uint16(port))) +}