Skip to content

Commit f3dafbd

Browse files
committed
Add exclusion route method test
1 parent 8c4cdb2 commit f3dafbd

File tree

1 file changed

+149
-23
lines changed

1 file changed

+149
-23
lines changed

client/internal/routemanager/systemops/systemops_generic_test.go

Lines changed: 149 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"net"
1010
"net/netip"
11+
"strings"
1112
"syscall"
1213
"testing"
1314

@@ -37,7 +38,7 @@ func TestAddVPNRoute(t *testing.T) {
3738
},
3839
{
3940
name: "IPv4 Single host",
40-
prefix: netip.MustParsePrefix("8.8.8.8/32"),
41+
prefix: netip.MustParsePrefix("10.111.111.111/32"),
4142
},
4243
{
4344
name: "IPv4 RFC3927 test range",
@@ -46,11 +47,11 @@ func TestAddVPNRoute(t *testing.T) {
4647

4748
{
4849
name: "IPv6 Subnet",
49-
prefix: netip.MustParsePrefix("2001:db8:1000::/48"),
50+
prefix: netip.MustParsePrefix("fdb1:848a:7e16::/48"),
5051
},
5152
{
5253
name: "IPv6 Single host",
53-
prefix: netip.MustParsePrefix("2001:db8::1/128"),
54+
prefix: netip.MustParsePrefix("fdb1:848a:7e16:a::b/128"),
5455
},
5556

5657
// IPv4 addresses that should be rejected (matches validateRoute logic)
@@ -122,8 +123,6 @@ func TestAddVPNRoute(t *testing.T) {
122123
prefix: netip.MustParsePrefix("100.65.75.0/32"),
123124
expectError: true,
124125
},
125-
126-
127126
}
128127

129128
for n, testCase := range testCases {
@@ -145,8 +144,7 @@ func TestAddVPNRoute(t *testing.T) {
145144
// add the route
146145
err = r.AddVPNRoute(testCase.prefix, intf)
147146
if testCase.expectError {
148-
assert.ErrorIs(t, err, vars.ErrRouteNotAllowed,
149-
"Error should be ErrRouteNotAllowed, got: %v", err)
147+
assert.ErrorIs(t, err, vars.ErrRouteNotAllowed)
150148
return
151149
}
152150

@@ -158,13 +156,154 @@ func TestAddVPNRoute(t *testing.T) {
158156

159157
// remove route again
160158
err = r.RemoveVPNRoute(testCase.prefix, intf)
161-
require.NoError(t, err, "RemoveVPNRoute should not return err")
159+
require.NoError(t, err)
162160

163161
// validate it's gone
164162
nextHop, err = GetNextHop(testCase.prefix.Addr())
163+
require.True(t,
164+
errors.Is(err, vars.ErrRouteNotFound) || err == nil && nextHop.Intf != nil && nextHop.Intf.Name != wgInterface.Name(),
165+
"err: %v, next hop: %v", err, nextHop)
166+
})
167+
}
168+
}
169+
func TestAddRouteToNonVPNIntf(t *testing.T) {
170+
testCases := []struct {
171+
name string
172+
prefix netip.Prefix
173+
expectError bool
174+
errorType error
175+
}{
176+
{
177+
name: "IPv4 RFC3927 test range",
178+
prefix: netip.MustParsePrefix("198.51.100.0/24"),
179+
},
180+
{
181+
name: "IPv4 Single host",
182+
prefix: netip.MustParsePrefix("8.8.8.8/32"),
183+
},
184+
{
185+
name: "IPv6 External network route",
186+
prefix: netip.MustParsePrefix("2001:db8:1000::/48"),
187+
},
188+
{
189+
name: "IPv6 Single host",
190+
prefix: netip.MustParsePrefix("2001:db8::1/128"),
191+
},
192+
{
193+
name: "IPv6 Subnet",
194+
prefix: netip.MustParsePrefix("2a05:d014:1f8d::/48"),
195+
},
196+
{
197+
name: "IPv6 Single host",
198+
prefix: netip.MustParsePrefix("2a05:d014:1f8d:7302:ebca:ec15:b24d:d07e/128"),
199+
},
200+
201+
// Addresses that should be rejected
202+
{
203+
name: "IPv4 Loopback",
204+
prefix: netip.MustParsePrefix("127.0.0.1/32"),
205+
expectError: true,
206+
errorType: vars.ErrRouteNotAllowed,
207+
},
208+
{
209+
name: "IPv4 Link-local unicast",
210+
prefix: netip.MustParsePrefix("169.254.1.1/32"),
211+
expectError: true,
212+
errorType: vars.ErrRouteNotAllowed,
213+
},
214+
{
215+
name: "IPv4 Multicast",
216+
prefix: netip.MustParsePrefix("239.255.255.250/32"),
217+
expectError: true,
218+
errorType: vars.ErrRouteNotAllowed,
219+
},
220+
{
221+
name: "IPv4 Unspecified",
222+
prefix: netip.MustParsePrefix("0.0.0.0/32"),
223+
expectError: true,
224+
errorType: vars.ErrRouteNotAllowed,
225+
},
226+
{
227+
name: "IPv6 Loopback",
228+
prefix: netip.MustParsePrefix("::1/128"),
229+
expectError: true,
230+
errorType: vars.ErrRouteNotAllowed,
231+
},
232+
{
233+
name: "IPv6 Link-local unicast",
234+
prefix: netip.MustParsePrefix("fe80::1/128"),
235+
expectError: true,
236+
errorType: vars.ErrRouteNotAllowed,
237+
},
238+
{
239+
name: "IPv6 Multicast",
240+
prefix: netip.MustParsePrefix("ff00::1/128"),
241+
expectError: true,
242+
errorType: vars.ErrRouteNotAllowed,
243+
},
244+
{
245+
name: "IPv6 Unspecified",
246+
prefix: netip.MustParsePrefix("::/128"),
247+
expectError: true,
248+
errorType: vars.ErrRouteNotAllowed,
249+
},
250+
{
251+
name: "IPv4 WireGuard interface network overlap",
252+
prefix: netip.MustParsePrefix("100.65.75.0/24"),
253+
expectError: true,
254+
errorType: vars.ErrRouteNotAllowed,
255+
},
256+
}
257+
258+
for n, testCase := range testCases {
259+
t.Run(testCase.name, func(t *testing.T) {
260+
t.Setenv("NB_DISABLE_ROUTE_CACHE", "true")
261+
262+
wgInterface := createWGInterface(t, fmt.Sprintf("utun54%d", n), "100.65.75.2/24", 33200+n)
263+
264+
r := NewSysOps(wgInterface, nil)
265+
_, _, err := r.SetupRouting(nil, nil)
266+
require.NoError(t, err)
267+
t.Cleanup(func() {
268+
assert.NoError(t, r.CleanupRouting(nil))
269+
})
270+
271+
initialNextHopV4, err := GetNextHop(netip.IPv4Unspecified())
272+
require.NoError(t, err, "Should be able to get IPv4 default route")
273+
t.Logf("Initial IPv4 next hop: %s", initialNextHopV4)
274+
275+
initialNextHopV6, err := GetNextHop(netip.IPv6Unspecified())
276+
if testCase.prefix.Addr().Is6() &&
277+
(errors.Is(err, vars.ErrRouteNotFound) || initialNextHopV6.Intf != nil && strings.HasPrefix(initialNextHopV6.Intf.Name, "utun")) {
278+
t.Skip("Skipping test as no ipv6 default route is available")
279+
}
280+
if err != nil && !errors.Is(err, vars.ErrRouteNotFound) {
281+
t.Fatalf("Failed to get IPv6 default route: %v", err)
282+
}
283+
284+
var initialNextHop Nexthop
285+
if testCase.prefix.Addr().Is6() {
286+
initialNextHop = initialNextHopV6
287+
} else {
288+
initialNextHop = initialNextHopV4
289+
}
290+
291+
nexthop, err := r.addRouteToNonVPNIntf(testCase.prefix, wgInterface, initialNextHop)
292+
293+
if testCase.expectError {
294+
require.ErrorIs(t, err, vars.ErrRouteNotAllowed)
295+
return
296+
}
297+
require.NoError(t, err)
298+
t.Logf("Next hop for %s: %s", testCase.prefix, nexthop)
299+
300+
// Verify the route was added and points to non-VPN interface
301+
currentNextHop, err := GetNextHop(testCase.prefix.Addr())
165302
require.NoError(t, err)
166-
assert.NotNil(t, nextHop.Intf)
167-
assert.NotEqual(t, nextHop.Intf.Name, wgInterface.Name())
303+
assert.NotEqual(t, wgInterface.Name(), currentNextHop.Intf.Name, "Route should not point to VPN interface")
304+
305+
err = r.removeFromRouteTable(testCase.prefix, nexthop)
306+
assert.NoError(t, err)
168307
})
169308
}
170309
}
@@ -410,16 +549,3 @@ func TestIsVpnRoute(t *testing.T) {
410549
})
411550
}
412551
}
413-
414-
func existsInRouteTable(prefix netip.Prefix) (bool, error) {
415-
routes, err := GetRoutesFromTable()
416-
if err != nil {
417-
return false, fmt.Errorf("get routes from table: %w", err)
418-
}
419-
for _, tableRoute := range routes {
420-
if tableRoute == prefix {
421-
return true, nil
422-
}
423-
}
424-
return false, nil
425-
}

0 commit comments

Comments
 (0)