2023-06-01 16:00:44 +02:00
|
|
|
package routemanager
|
|
|
|
|
|
|
|
import (
|
2024-11-13 15:21:33 +01:00
|
|
|
"fmt"
|
2023-06-01 16:00:44 +02:00
|
|
|
"net/netip"
|
|
|
|
"testing"
|
2024-04-09 21:20:02 +02:00
|
|
|
"time"
|
2023-06-01 16:00:44 +02:00
|
|
|
|
2024-06-13 13:24:24 +02:00
|
|
|
"github.com/netbirdio/netbird/client/internal/routemanager/static"
|
2023-06-01 16:00:44 +02:00
|
|
|
"github.com/netbirdio/netbird/route"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestGetBestrouteFromStatuses(t *testing.T) {
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
name string
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses map[route.ID]routerPeerStatus
|
|
|
|
expectedRouteID route.ID
|
|
|
|
currentRoute route.ID
|
|
|
|
existingRoutes map[route.ID]*route.Route
|
2023-06-01 16:00:44 +02:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "one route",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
},
|
2024-04-09 21:20:02 +02:00
|
|
|
currentRoute: "",
|
2023-06-01 16:00:44 +02:00
|
|
|
expectedRouteID: "route1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "one connected routes with relayed and direct",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: true,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
},
|
2024-04-09 21:20:02 +02:00
|
|
|
currentRoute: "",
|
2023-06-01 16:00:44 +02:00
|
|
|
expectedRouteID: "route1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "one connected routes with relayed and no direct",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: true,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
},
|
2024-04-09 21:20:02 +02:00
|
|
|
currentRoute: "",
|
2023-06-01 16:00:44 +02:00
|
|
|
expectedRouteID: "route1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "no connected peers",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
connected: false,
|
|
|
|
relayed: false,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
},
|
2024-04-09 21:20:02 +02:00
|
|
|
currentRoute: "",
|
2023-06-01 16:00:44 +02:00
|
|
|
expectedRouteID: "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "multiple connected peers with different metrics",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: 9000,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
ID: "route2",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer2",
|
|
|
|
},
|
|
|
|
},
|
2024-04-09 21:20:02 +02:00
|
|
|
currentRoute: "",
|
2023-06-01 16:00:44 +02:00
|
|
|
expectedRouteID: "route1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "multiple connected peers with one relayed",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
connected: true,
|
|
|
|
relayed: true,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2023-06-01 16:00:44 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
ID: "route2",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer2",
|
|
|
|
},
|
|
|
|
},
|
2024-04-09 21:20:02 +02:00
|
|
|
currentRoute: "",
|
2023-06-01 16:00:44 +02:00
|
|
|
expectedRouteID: "route1",
|
|
|
|
},
|
2024-04-09 21:20:02 +02:00
|
|
|
{
|
|
|
|
name: "multiple connected peers with different latencies",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2024-04-09 21:20:02 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
latency: 300 * time.Millisecond,
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
connected: true,
|
|
|
|
latency: 10 * time.Millisecond,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2024-04-09 21:20:02 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
ID: "route2",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
currentRoute: "",
|
|
|
|
expectedRouteID: "route2",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "should ignore routes with latency 0",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2024-04-09 21:20:02 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
latency: 0 * time.Millisecond,
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
connected: true,
|
|
|
|
latency: 10 * time.Millisecond,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2024-04-09 21:20:02 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
ID: "route2",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
currentRoute: "",
|
|
|
|
expectedRouteID: "route2",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "current route with similar score and similar but slightly worse latency should not change",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2024-04-09 21:20:02 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
2024-05-02 11:51:03 +02:00
|
|
|
latency: 15 * time.Millisecond,
|
2024-04-09 21:20:02 +02:00
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
latency: 10 * time.Millisecond,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2024-04-09 21:20:02 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
ID: "route2",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
currentRoute: "route1",
|
|
|
|
expectedRouteID: "route1",
|
|
|
|
},
|
2024-11-13 15:21:33 +01:00
|
|
|
{
|
|
|
|
name: "relayed routes with latency 0 should maintain previous choice",
|
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: true,
|
|
|
|
latency: 0 * time.Millisecond,
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
connected: true,
|
|
|
|
relayed: true,
|
|
|
|
latency: 0 * time.Millisecond,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
ID: "route2",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
currentRoute: "route1",
|
|
|
|
expectedRouteID: "route1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "p2p routes with latency 0 should maintain previous choice",
|
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
latency: 0 * time.Millisecond,
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
latency: 0 * time.Millisecond,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
ID: "route2",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
currentRoute: "route1",
|
|
|
|
expectedRouteID: "route1",
|
|
|
|
},
|
2024-05-02 11:51:03 +02:00
|
|
|
{
|
|
|
|
name: "current route with bad score should be changed to route with better score",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2024-05-02 11:51:03 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
latency: 200 * time.Millisecond,
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
latency: 10 * time.Millisecond,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2024-05-02 11:51:03 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
ID: "route2",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
currentRoute: "route1",
|
|
|
|
expectedRouteID: "route2",
|
|
|
|
},
|
2024-04-09 21:20:02 +02:00
|
|
|
{
|
|
|
|
name: "current chosen route doesn't exist anymore",
|
2024-05-06 14:47:49 +02:00
|
|
|
statuses: map[route.ID]routerPeerStatus{
|
2024-04-09 21:20:02 +02:00
|
|
|
"route1": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
latency: 20 * time.Millisecond,
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
connected: true,
|
|
|
|
relayed: false,
|
|
|
|
latency: 10 * time.Millisecond,
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 14:47:49 +02:00
|
|
|
existingRoutes: map[route.ID]*route.Route{
|
2024-04-09 21:20:02 +02:00
|
|
|
"route1": {
|
|
|
|
ID: "route1",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer1",
|
|
|
|
},
|
|
|
|
"route2": {
|
|
|
|
ID: "route2",
|
|
|
|
Metric: route.MaxMetric,
|
|
|
|
Peer: "peer2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
currentRoute: "routeDoesntExistAnymore",
|
|
|
|
expectedRouteID: "route2",
|
|
|
|
},
|
2023-06-01 16:00:44 +02:00
|
|
|
}
|
|
|
|
|
2024-11-13 15:21:33 +01:00
|
|
|
// fill the test data with random routes
|
|
|
|
for _, tc := range testCases {
|
|
|
|
for i := 0; i < 50; i++ {
|
|
|
|
dummyRoute := &route.Route{
|
|
|
|
ID: route.ID(fmt.Sprintf("dummy_p1_%d", i)),
|
|
|
|
Metric: route.MinMetric,
|
|
|
|
Peer: fmt.Sprintf("dummy_p1_%d", i),
|
|
|
|
}
|
|
|
|
tc.existingRoutes[dummyRoute.ID] = dummyRoute
|
|
|
|
}
|
|
|
|
for i := 0; i < 50; i++ {
|
|
|
|
dummyRoute := &route.Route{
|
|
|
|
ID: route.ID(fmt.Sprintf("dummy_p2_%d", i)),
|
|
|
|
Metric: route.MinMetric,
|
|
|
|
Peer: fmt.Sprintf("dummy_p1_%d", i),
|
|
|
|
}
|
|
|
|
tc.existingRoutes[dummyRoute.ID] = dummyRoute
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < 50; i++ {
|
|
|
|
id := route.ID(fmt.Sprintf("dummy_p1_%d", i))
|
|
|
|
dummyStatus := routerPeerStatus{
|
|
|
|
connected: false,
|
|
|
|
relayed: true,
|
|
|
|
latency: 0,
|
|
|
|
}
|
|
|
|
tc.statuses[id] = dummyStatus
|
|
|
|
}
|
|
|
|
for i := 0; i < 50; i++ {
|
|
|
|
id := route.ID(fmt.Sprintf("dummy_p2_%d", i))
|
|
|
|
dummyStatus := routerPeerStatus{
|
|
|
|
connected: false,
|
|
|
|
relayed: true,
|
|
|
|
latency: 0,
|
|
|
|
}
|
|
|
|
tc.statuses[id] = dummyStatus
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-01 16:00:44 +02:00
|
|
|
for _, tc := range testCases {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
2024-04-09 21:20:02 +02:00
|
|
|
currentRoute := &route.Route{
|
|
|
|
ID: "routeDoesntExistAnymore",
|
|
|
|
}
|
|
|
|
if tc.currentRoute != "" {
|
|
|
|
currentRoute = tc.existingRoutes[tc.currentRoute]
|
|
|
|
}
|
|
|
|
|
2023-06-01 16:00:44 +02:00
|
|
|
// create new clientNetwork
|
|
|
|
client := &clientNetwork{
|
2024-06-13 13:24:24 +02:00
|
|
|
handler: static.NewRoute(&route.Route{Network: netip.MustParsePrefix("192.168.0.0/24")}, nil, nil),
|
|
|
|
routes: tc.existingRoutes,
|
|
|
|
currentChosen: currentRoute,
|
2023-06-01 16:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
chosenRoute := client.getBestRouteFromStatuses(tc.statuses)
|
|
|
|
if chosenRoute != tc.expectedRouteID {
|
|
|
|
t.Errorf("expected routeID %s, got %s", tc.expectedRouteID, chosenRoute)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|