mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-19 15:01:29 +02:00
After the network monitor's debounce window, re-check the default next hop before triggering a client restart. A flapping NIC that returns to the same default route no longer forces a restart, avoiding redundant sync stream reconnects and peer meta churn.
130 lines
3.1 KiB
Go
130 lines
3.1 KiB
Go
package networkmonitor
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"net/netip"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/routemanager/systemops"
|
|
)
|
|
|
|
type MocMultiEvent struct {
|
|
counter int
|
|
}
|
|
|
|
func (m *MocMultiEvent) checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop) error {
|
|
if m.counter == 0 {
|
|
<-ctx.Done()
|
|
return ctx.Err()
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
m.counter--
|
|
return nil
|
|
}
|
|
|
|
func TestNetworkMonitor_Close(t *testing.T) {
|
|
checkChangeFn = func(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop) error {
|
|
<-ctx.Done()
|
|
return ctx.Err()
|
|
}
|
|
nw := New()
|
|
|
|
var resErr error
|
|
done := make(chan struct{})
|
|
go func() {
|
|
resErr = nw.Listen(context.Background())
|
|
close(done)
|
|
}()
|
|
|
|
time.Sleep(1 * time.Second) // wait for the goroutine to start
|
|
nw.Stop()
|
|
|
|
<-done
|
|
if !errors.Is(resErr, context.Canceled) {
|
|
t.Errorf("unexpected error: %v", resErr)
|
|
}
|
|
}
|
|
|
|
func TestNetworkMonitor_Event(t *testing.T) {
|
|
checkChangeFn = func(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop) error {
|
|
timeout, cancel := context.WithTimeout(ctx, 3*time.Second)
|
|
defer cancel()
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-timeout.Done():
|
|
return nil
|
|
}
|
|
}
|
|
nw := New()
|
|
defer nw.Stop()
|
|
|
|
var resErr error
|
|
done := make(chan struct{})
|
|
go func() {
|
|
resErr = nw.Listen(context.Background())
|
|
close(done)
|
|
}()
|
|
|
|
<-done
|
|
if !errors.Is(resErr, nil) {
|
|
t.Errorf("unexpected error: %v", nil)
|
|
}
|
|
}
|
|
|
|
func TestNetworkMonitor_MultiEvent(t *testing.T) {
|
|
eventsRepeated := 3
|
|
me := &MocMultiEvent{counter: eventsRepeated}
|
|
checkChangeFn = me.checkChange
|
|
|
|
nw := New()
|
|
defer nw.Stop()
|
|
|
|
done := make(chan struct{})
|
|
started := time.Now()
|
|
go func() {
|
|
if resErr := nw.Listen(context.Background()); resErr != nil {
|
|
t.Errorf("unexpected error: %v", resErr)
|
|
}
|
|
close(done)
|
|
}()
|
|
|
|
<-done
|
|
expectedResponseTime := time.Duration(eventsRepeated)*time.Second + debounceTime
|
|
if time.Since(started) < expectedResponseTime {
|
|
t.Errorf("unexpected duration: %v", time.Since(started))
|
|
}
|
|
}
|
|
|
|
func TestSameNexthop(t *testing.T) {
|
|
eth0 := &net.Interface{Index: 1, Name: "eth0"}
|
|
eth1 := &net.Interface{Index: 2, Name: "eth1"}
|
|
ip1 := netip.MustParseAddr("192.168.1.1")
|
|
ip2 := netip.MustParseAddr("192.168.2.1")
|
|
|
|
tests := []struct {
|
|
name string
|
|
a, b systemops.Nexthop
|
|
want bool
|
|
}{
|
|
{"identical", systemops.Nexthop{IP: ip1, Intf: eth0}, systemops.Nexthop{IP: ip1, Intf: eth0}, true},
|
|
{"same index different pointer", systemops.Nexthop{IP: ip1, Intf: eth0}, systemops.Nexthop{IP: ip1, Intf: &net.Interface{Index: 1, Name: "eth0"}}, true},
|
|
{"different ip", systemops.Nexthop{IP: ip1, Intf: eth0}, systemops.Nexthop{IP: ip2, Intf: eth0}, false},
|
|
{"different interface", systemops.Nexthop{IP: ip1, Intf: eth0}, systemops.Nexthop{IP: ip1, Intf: eth1}, false},
|
|
{"both nil interface", systemops.Nexthop{IP: ip1}, systemops.Nexthop{IP: ip1}, true},
|
|
{"one nil interface", systemops.Nexthop{IP: ip1, Intf: eth0}, systemops.Nexthop{IP: ip1}, false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := sameNexthop(tt.a, tt.b); got != tt.want {
|
|
t.Errorf("sameNexthop() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|