mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-29 19:43:57 +01:00
747797271e
Fix the status indication in the client service. The status of the management server and the signal server was incorrect if the network connection was broken. Basically the status update was not used by the management and signal library.
441 lines
9.7 KiB
Go
441 lines
9.7 KiB
Go
package peer
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/magiconair/properties/assert"
|
|
"github.com/pion/ice/v2"
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/proxy"
|
|
"github.com/netbirdio/netbird/iface"
|
|
sproto "github.com/netbirdio/netbird/signal/proto"
|
|
)
|
|
|
|
var connConf = ConnConfig{
|
|
Key: "LLHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
|
LocalKey: "RRHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
|
StunTurn: []*ice.URL{},
|
|
InterfaceBlackList: nil,
|
|
Timeout: time.Second,
|
|
ProxyConfig: proxy.Config{},
|
|
LocalWgPort: 51820,
|
|
}
|
|
|
|
func TestNewConn_interfaceFilter(t *testing.T) {
|
|
ignore := []string{iface.WgInterfaceDefault, "tun0", "zt", "ZeroTier", "utun", "wg", "ts",
|
|
"Tailscale", "tailscale"}
|
|
|
|
filter := interfaceFilter(ignore)
|
|
|
|
for _, s := range ignore {
|
|
assert.Equal(t, filter(s), false)
|
|
}
|
|
|
|
}
|
|
|
|
func TestConn_GetKey(t *testing.T) {
|
|
conn, err := NewConn(connConf, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
got := conn.GetKey()
|
|
|
|
assert.Equal(t, got, connConf.Key, "they should be equal")
|
|
}
|
|
|
|
func TestConn_OnRemoteOffer(t *testing.T) {
|
|
|
|
conn, err := NewConn(connConf, NewRecorder("https://mgm"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(2)
|
|
go func() {
|
|
<-conn.remoteOffersCh
|
|
wg.Done()
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
accepted := conn.OnRemoteOffer(OfferAnswer{
|
|
IceCredentials: IceCredentials{
|
|
UFrag: "test",
|
|
Pwd: "test",
|
|
},
|
|
WgListenPort: 0,
|
|
Version: "",
|
|
})
|
|
if accepted {
|
|
wg.Done()
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestConn_OnRemoteAnswer(t *testing.T) {
|
|
|
|
conn, err := NewConn(connConf, NewRecorder("https://mgm"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(2)
|
|
go func() {
|
|
<-conn.remoteAnswerCh
|
|
wg.Done()
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
accepted := conn.OnRemoteAnswer(OfferAnswer{
|
|
IceCredentials: IceCredentials{
|
|
UFrag: "test",
|
|
Pwd: "test",
|
|
},
|
|
WgListenPort: 0,
|
|
Version: "",
|
|
})
|
|
if accepted {
|
|
wg.Done()
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
}
|
|
func TestConn_Status(t *testing.T) {
|
|
|
|
conn, err := NewConn(connConf, NewRecorder("https://mgm"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
tables := []struct {
|
|
name string
|
|
status ConnStatus
|
|
want ConnStatus
|
|
}{
|
|
{"StatusConnected", StatusConnected, StatusConnected},
|
|
{"StatusDisconnected", StatusDisconnected, StatusDisconnected},
|
|
{"StatusConnecting", StatusConnecting, StatusConnecting},
|
|
}
|
|
|
|
for _, table := range tables {
|
|
t.Run(table.name, func(t *testing.T) {
|
|
conn.status = table.status
|
|
|
|
got := conn.Status()
|
|
assert.Equal(t, got, table.want, "they should be equal")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConn_Close(t *testing.T) {
|
|
|
|
conn, err := NewConn(connConf, NewRecorder("https://mgm"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(1)
|
|
go func() {
|
|
<-conn.closeCh
|
|
wg.Done()
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
err := conn.Close()
|
|
if err != nil {
|
|
continue
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
type mockICECandidate struct {
|
|
ice.Candidate
|
|
AddressFunc func() string
|
|
TypeFunc func() ice.CandidateType
|
|
}
|
|
|
|
// Address mocks and overwrite ice.Candidate Address method
|
|
func (m *mockICECandidate) Address() string {
|
|
if m.AddressFunc != nil {
|
|
return m.AddressFunc()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Type mocks and overwrite ice.Candidate Type method
|
|
func (m *mockICECandidate) Type() ice.CandidateType {
|
|
if m.TypeFunc != nil {
|
|
return m.TypeFunc()
|
|
}
|
|
return ice.CandidateTypeUnspecified
|
|
}
|
|
|
|
func TestConn_ShouldUseProxy(t *testing.T) {
|
|
publicHostCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "8.8.8.8"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeHost
|
|
},
|
|
}
|
|
privateHostCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "10.0.0.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeHost
|
|
},
|
|
}
|
|
srflxCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "1.1.1.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeServerReflexive
|
|
},
|
|
}
|
|
|
|
prflxCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "1.1.1.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypePeerReflexive
|
|
},
|
|
}
|
|
|
|
relayCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "1.1.1.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeRelay
|
|
},
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
candatePair *ice.CandidatePair
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "Use Proxy When Local Candidate Is Relay",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: relayCandidate,
|
|
Remote: privateHostCandidate,
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Use Proxy When Remote Candidate Is Relay",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: privateHostCandidate,
|
|
Remote: relayCandidate,
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Use Proxy When Local Candidate Is Peer Reflexive",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: prflxCandidate,
|
|
Remote: privateHostCandidate,
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Use Proxy When Remote Candidate Is Peer Reflexive",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: privateHostCandidate,
|
|
Remote: prflxCandidate,
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Local Candidate Is Public And Remote Is Private",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: privateHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Remote Candidate Is Public And Local Is Private",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: privateHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Local Candidate is Public And Remote Is Server Reflexive",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: srflxCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Remote Candidate is Public And Local Is Server Reflexive",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: srflxCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Both Candidates Are Public",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Both Candidates Are Private",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: privateHostCandidate,
|
|
Remote: privateHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
result := shouldUseProxy(testCase.candatePair)
|
|
if result != testCase.expected {
|
|
t.Errorf("got a different result. Expected %t Got %t", testCase.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetProxyWithMessageExchange(t *testing.T) {
|
|
publicHostCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "8.8.8.8"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeHost
|
|
},
|
|
}
|
|
relayCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "1.1.1.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeRelay
|
|
},
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
candatePair *ice.CandidatePair
|
|
inputDirectModeSupport bool
|
|
inputRemoteModeMessage bool
|
|
expected proxy.Type
|
|
}{
|
|
{
|
|
name: "Should Result In Using Wireguard Proxy When Local Eval Is Use Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: relayCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: true,
|
|
inputRemoteModeMessage: true,
|
|
expected: proxy.TypeWireguard,
|
|
},
|
|
{
|
|
name: "Should Result In Using Wireguard Proxy When Remote Eval Is Use Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: true,
|
|
inputRemoteModeMessage: false,
|
|
expected: proxy.TypeWireguard,
|
|
},
|
|
{
|
|
name: "Should Result In Using Wireguard Proxy When Remote Direct Mode Support Is False And Local Eval Is Use Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: relayCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: false,
|
|
inputRemoteModeMessage: false,
|
|
expected: proxy.TypeWireguard,
|
|
},
|
|
{
|
|
name: "Should Result In Using Direct When Remote Direct Mode Support Is False And Local Eval Is No Use Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: false,
|
|
inputRemoteModeMessage: false,
|
|
expected: proxy.TypeNoProxy,
|
|
},
|
|
{
|
|
name: "Should Result In Using Direct When Local And Remote Eval Is No Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: true,
|
|
inputRemoteModeMessage: true,
|
|
expected: proxy.TypeNoProxy,
|
|
},
|
|
}
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
g := errgroup.Group{}
|
|
conn, err := NewConn(connConf, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
conn.meta.protoSupport.DirectCheck = testCase.inputDirectModeSupport
|
|
conn.SetSendSignalMessage(func(message *sproto.Message) error {
|
|
return nil
|
|
})
|
|
|
|
g.Go(func() error {
|
|
return conn.OnModeMessage(ModeMessage{
|
|
Direct: testCase.inputRemoteModeMessage,
|
|
})
|
|
})
|
|
|
|
resultProxy := conn.getProxyWithMessageExchange(testCase.candatePair, 1000)
|
|
|
|
err = g.Wait()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if resultProxy.Type() != testCase.expected {
|
|
t.Errorf("result didn't match expected value: Expected: %s, Got: %s", testCase.expected, resultProxy.Type())
|
|
}
|
|
})
|
|
}
|
|
}
|