mirror of
https://github.com/netbirdio/netbird.git
synced 2025-02-18 11:11:20 +01:00
This PR adds supports for the WireGuard userspace implementation using Bind interface from wireguard-go. The newly introduced ICEBind struct implements Bind with UDPMux-based structs from pion/ice to handle hole punching using ICE. The core implementation was taken from StdBind of wireguard-go. The result is a single WireGuard port that is used for host and server reflexive candidates. Relay candidates are still handled separately and will be integrated in the following PRs. ICEBind checks the incoming packets for being STUN or WireGuard ones and routes them to UDPMux (to handle hole punching) or to WireGuard respectively.
427 lines
12 KiB
Go
427 lines
12 KiB
Go
package routemanager
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/pion/transport/v2/stdnet"
|
|
"net/netip"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/peer"
|
|
"github.com/netbirdio/netbird/iface"
|
|
"github.com/netbirdio/netbird/route"
|
|
)
|
|
|
|
// send 5 routes, one for server and 4 for clients, one normal and 2 HA and one small
|
|
// if linux host, should have one for server in map
|
|
// we should have 2 client manager
|
|
// 2 ranges in our routing table
|
|
|
|
const localPeerKey = "local"
|
|
const remotePeerKey1 = "remote1"
|
|
const remotePeerKey2 = "remote1"
|
|
|
|
func TestManagerUpdateRoutes(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
inputInitRoutes []*route.Route
|
|
inputRoutes []*route.Route
|
|
inputSerial uint64
|
|
shouldCheckServerRoutes bool
|
|
serverRoutesExpected int
|
|
clientNetworkWatchersExpected int
|
|
}{
|
|
{
|
|
name: "Should create 2 client networks",
|
|
inputInitRoutes: []*route.Route{},
|
|
inputRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "b",
|
|
NetID: "routeB",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("8.8.8.8/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputSerial: 1,
|
|
clientNetworkWatchersExpected: 2,
|
|
},
|
|
{
|
|
name: "Should Create 2 Server Routes",
|
|
inputRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: localPeerKey,
|
|
Network: netip.MustParsePrefix("100.64.252.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "b",
|
|
NetID: "routeB",
|
|
Peer: localPeerKey,
|
|
Network: netip.MustParsePrefix("8.8.8.9/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputSerial: 1,
|
|
shouldCheckServerRoutes: runtime.GOOS == "linux",
|
|
serverRoutesExpected: 2,
|
|
clientNetworkWatchersExpected: 0,
|
|
},
|
|
{
|
|
name: "Should Create 1 Route For Client And Server",
|
|
inputRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: localPeerKey,
|
|
Network: netip.MustParsePrefix("100.64.30.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "b",
|
|
NetID: "routeB",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("8.8.9.9/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputSerial: 1,
|
|
shouldCheckServerRoutes: runtime.GOOS == "linux",
|
|
serverRoutesExpected: 1,
|
|
clientNetworkWatchersExpected: 1,
|
|
},
|
|
{
|
|
name: "Should Create 1 HA Route and 1 Standalone",
|
|
inputRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("8.8.20.0/24"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "b",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey2,
|
|
Network: netip.MustParsePrefix("8.8.20.0/24"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "c",
|
|
NetID: "routeB",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("8.8.9.9/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputSerial: 1,
|
|
clientNetworkWatchersExpected: 2,
|
|
},
|
|
{
|
|
name: "No Small Client Route Should Be Added",
|
|
inputRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("0.0.0.0/0"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputSerial: 1,
|
|
clientNetworkWatchersExpected: 0,
|
|
},
|
|
{
|
|
name: "No Server Routes Should Be Added To Non Linux",
|
|
inputRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: localPeerKey,
|
|
Network: netip.MustParsePrefix("1.2.3.4/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputSerial: 1,
|
|
shouldCheckServerRoutes: runtime.GOOS != "linux",
|
|
serverRoutesExpected: 0,
|
|
clientNetworkWatchersExpected: 0,
|
|
},
|
|
{
|
|
name: "Remove 1 Client Route",
|
|
inputInitRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "b",
|
|
NetID: "routeB",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("8.8.8.8/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputSerial: 1,
|
|
clientNetworkWatchersExpected: 1,
|
|
},
|
|
{
|
|
name: "Update Route to HA",
|
|
inputInitRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "b",
|
|
NetID: "routeB",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("8.8.8.8/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "b",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey2,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputSerial: 1,
|
|
clientNetworkWatchersExpected: 1,
|
|
},
|
|
{
|
|
name: "Remove Client Routes",
|
|
inputInitRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "b",
|
|
NetID: "routeB",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("8.8.8.8/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputRoutes: []*route.Route{},
|
|
inputSerial: 1,
|
|
clientNetworkWatchersExpected: 0,
|
|
},
|
|
{
|
|
name: "Remove All Routes",
|
|
inputInitRoutes: []*route.Route{
|
|
{
|
|
ID: "a",
|
|
NetID: "routeA",
|
|
Peer: localPeerKey,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "b",
|
|
NetID: "routeB",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("8.8.8.8/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputRoutes: []*route.Route{},
|
|
inputSerial: 1,
|
|
shouldCheckServerRoutes: true,
|
|
serverRoutesExpected: 0,
|
|
clientNetworkWatchersExpected: 0,
|
|
},
|
|
{
|
|
name: "HA server should not register routes from the same HA group",
|
|
inputRoutes: []*route.Route{
|
|
{
|
|
ID: "l1",
|
|
NetID: "routeA",
|
|
Peer: localPeerKey,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "l2",
|
|
NetID: "routeA",
|
|
Peer: localPeerKey,
|
|
Network: netip.MustParsePrefix("8.8.9.8/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "r1",
|
|
NetID: "routeA",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("100.64.251.250/30"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
ID: "r2",
|
|
NetID: "routeC",
|
|
Peer: remotePeerKey1,
|
|
Network: netip.MustParsePrefix("8.8.9.9/32"),
|
|
NetworkType: route.IPv4Network,
|
|
Metric: 9999,
|
|
Masquerade: false,
|
|
Enabled: true,
|
|
},
|
|
},
|
|
inputSerial: 1,
|
|
shouldCheckServerRoutes: runtime.GOOS == "linux",
|
|
serverRoutesExpected: 2,
|
|
clientNetworkWatchersExpected: 1,
|
|
},
|
|
}
|
|
|
|
for n, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
newNet, err := stdnet.NewNet()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", iface.DefaultMTU, nil, newNet)
|
|
require.NoError(t, err, "should create testing WGIface interface")
|
|
defer wgInterface.Close()
|
|
|
|
err = wgInterface.Create()
|
|
require.NoError(t, err, "should create testing wireguard interface")
|
|
|
|
statusRecorder := peer.NewRecorder("https://mgm")
|
|
ctx := context.TODO()
|
|
routeManager := NewManager(ctx, localPeerKey, wgInterface, statusRecorder)
|
|
defer routeManager.Stop()
|
|
|
|
if len(testCase.inputInitRoutes) > 0 {
|
|
err = routeManager.UpdateRoutes(testCase.inputSerial, testCase.inputRoutes)
|
|
require.NoError(t, err, "should update routes with init routes")
|
|
}
|
|
|
|
err = routeManager.UpdateRoutes(testCase.inputSerial+uint64(len(testCase.inputInitRoutes)), testCase.inputRoutes)
|
|
require.NoError(t, err, "should update routes")
|
|
|
|
require.Len(t, routeManager.clientNetworks, testCase.clientNetworkWatchersExpected, "client networks size should match")
|
|
|
|
if testCase.shouldCheckServerRoutes {
|
|
require.Len(t, routeManager.serverRoutes, testCase.serverRoutesExpected, "server networks size should match")
|
|
}
|
|
})
|
|
}
|
|
}
|