8
8
"fmt"
9
9
"net"
10
10
"net/netip"
11
+ "strings"
11
12
"syscall"
12
13
"testing"
13
14
@@ -37,7 +38,7 @@ func TestAddVPNRoute(t *testing.T) {
37
38
},
38
39
{
39
40
name : "IPv4 Single host" ,
40
- prefix : netip .MustParsePrefix ("8.8.8.8 /32" ),
41
+ prefix : netip .MustParsePrefix ("10.111.111.111 /32" ),
41
42
},
42
43
{
43
44
name : "IPv4 RFC3927 test range" ,
@@ -46,11 +47,11 @@ func TestAddVPNRoute(t *testing.T) {
46
47
47
48
{
48
49
name : "IPv6 Subnet" ,
49
- prefix : netip .MustParsePrefix ("2001:db8:1000 ::/48" ),
50
+ prefix : netip .MustParsePrefix ("fdb1:848a:7e16 ::/48" ),
50
51
},
51
52
{
52
53
name : "IPv6 Single host" ,
53
- prefix : netip .MustParsePrefix ("2001:db8::1 /128" ),
54
+ prefix : netip .MustParsePrefix ("fdb1:848a:7e16:a::b /128" ),
54
55
},
55
56
56
57
// IPv4 addresses that should be rejected (matches validateRoute logic)
@@ -122,8 +123,6 @@ func TestAddVPNRoute(t *testing.T) {
122
123
prefix : netip .MustParsePrefix ("100.65.75.0/32" ),
123
124
expectError : true ,
124
125
},
125
-
126
-
127
126
}
128
127
129
128
for n , testCase := range testCases {
@@ -145,8 +144,7 @@ func TestAddVPNRoute(t *testing.T) {
145
144
// add the route
146
145
err = r .AddVPNRoute (testCase .prefix , intf )
147
146
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 )
150
148
return
151
149
}
152
150
@@ -158,13 +156,154 @@ func TestAddVPNRoute(t *testing.T) {
158
156
159
157
// remove route again
160
158
err = r .RemoveVPNRoute (testCase .prefix , intf )
161
- require .NoError (t , err , "RemoveVPNRoute should not return err" )
159
+ require .NoError (t , err )
162
160
163
161
// validate it's gone
164
162
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 ())
165
302
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 )
168
307
})
169
308
}
170
309
}
@@ -410,16 +549,3 @@ func TestIsVpnRoute(t *testing.T) {
410
549
})
411
550
}
412
551
}
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