mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-28 23:39:07 +01:00
Hold a peer offer or answer that arrives before the handshaker starts listening
This commit is contained in:
@@ -81,14 +81,19 @@ type Handshaker struct {
|
|||||||
|
|
||||||
func NewHandshaker(log *log.Entry, config ConnConfig, signaler *Signaler, ice *WorkerICE, relay *WorkerRelay, metricsStages *MetricsStages) *Handshaker {
|
func NewHandshaker(log *log.Entry, config ConnConfig, signaler *Signaler, ice *WorkerICE, relay *WorkerRelay, metricsStages *MetricsStages) *Handshaker {
|
||||||
h := &Handshaker{
|
h := &Handshaker{
|
||||||
log: log,
|
log: log,
|
||||||
config: config,
|
config: config,
|
||||||
signaler: signaler,
|
signaler: signaler,
|
||||||
ice: ice,
|
ice: ice,
|
||||||
relay: relay,
|
relay: relay,
|
||||||
metricsStages: metricsStages,
|
metricsStages: metricsStages,
|
||||||
remoteOffersCh: make(chan OfferAnswer),
|
// Buffered by one so an offer or answer that arrives between Open launching
|
||||||
remoteAnswerCh: make(chan OfferAnswer),
|
// the Listen goroutine and it reaching its receive is held rather than
|
||||||
|
// dropped. A peer activated by an incoming signal receives the remote's
|
||||||
|
// message in that window; an unbuffered channel skips it as "receiver not
|
||||||
|
// ready", and the connection cannot proceed until the remote re-sends.
|
||||||
|
remoteOffersCh: make(chan OfferAnswer, 1),
|
||||||
|
remoteAnswerCh: make(chan OfferAnswer, 1),
|
||||||
}
|
}
|
||||||
// assume remote supports ICE until we learn otherwise from received offers
|
// assume remote supports ICE until we learn otherwise from received offers
|
||||||
h.remoteICESupported.Store(ice != nil)
|
h.remoteICESupported.Store(ice != nil)
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package peer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
|
signal "github.com/netbirdio/netbird/shared/signal/client"
|
||||||
|
sProto "github.com/netbirdio/netbird/shared/signal/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestHandshaker(t *testing.T) *Handshaker {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
localKey, err := wgtypes.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("generate local key: %v", err)
|
||||||
|
}
|
||||||
|
remoteKey, err := wgtypes.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("generate remote key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signaler := NewSignaler(&signal.MockClient{
|
||||||
|
ReadyFunc: func() bool { return true },
|
||||||
|
SendFunc: func(*sProto.Message) error { return nil },
|
||||||
|
}, localKey)
|
||||||
|
|
||||||
|
cfg := ConnConfig{
|
||||||
|
Key: remoteKey.PublicKey().String(),
|
||||||
|
LocalKey: localKey.PublicKey().String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHandshaker(log.WithField("test", t.Name()), cfg, signaler, nil, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHandshakerHoldsSignalArrivingBeforeListen covers the case where a peer is
|
||||||
|
// activated by an incoming signal: the remote's offer/answer arrives in the same
|
||||||
|
// step that opens the connection, before the Listen loop starts reading. The
|
||||||
|
// message must be held rather than dropped, or the connection cannot proceed until
|
||||||
|
// the remote re-sends. This is the path taken when an eager peer connects to a
|
||||||
|
// lazily-managed one.
|
||||||
|
//
|
||||||
|
// The answer path is used because its Listen branch dispatches to the same
|
||||||
|
// listeners as the offer path without also sending an answer, so it exercises the
|
||||||
|
// buffered-channel behavior (which covers both channels) without needing a relay.
|
||||||
|
func TestHandshakerHoldsSignalArrivingBeforeListen(t *testing.T) {
|
||||||
|
h := newTestHandshaker(t)
|
||||||
|
|
||||||
|
processed := make(chan struct{}, 4)
|
||||||
|
h.AddRelayListener(func(*OfferAnswer) { processed <- struct{}{} })
|
||||||
|
|
||||||
|
// Delivered before Listen is reading, exactly as when the peer is woken by the
|
||||||
|
// remote's signal and the message is delivered right after Open.
|
||||||
|
h.OnRemoteAnswer(OfferAnswer{
|
||||||
|
WgListenPort: 51820,
|
||||||
|
IceCredentials: IceCredentials{UFrag: "ufrag", Pwd: "pwd"},
|
||||||
|
})
|
||||||
|
|
||||||
|
go h.Listen(t.Context())
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-processed:
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
t.Fatal("signal that arrived before Listen was ready was dropped")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user