1
16
17 package net
18
19 import (
20 "fmt"
21 "net"
22 "os"
23 "strings"
24 "testing"
25
26 netutils "k8s.io/utils/net"
27 )
28
29 const gatewayfirst = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
30 eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
31 eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
32 docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
33 virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
34 `
35 const gatewaylast = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
36 docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
37 virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
38 eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
39 eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
40 `
41 const gatewaymiddle = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
42 eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
43 docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
44 eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
45 virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
46 `
47 const noInternetConnection = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
48 docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
49 virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
50 `
51 const nothing = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
52 `
53 const badDestination = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
54 eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
55 eth3 0000FE0AA1 00000000 0001 0 0 0 0080FFFF 0 0 0
56 docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
57 virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
58 `
59 const badGateway = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
60 eth3 00000000 0100FE0AA1 0003 0 0 1024 00000000 0 0 0
61 eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
62 docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
63 virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
64 `
65 const route_Invalidhex = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
66 eth3 00000000 0100FE0AA 0003 0 0 1024 00000000 0 0 0
67 eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
68 docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
69 virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
70 `
71
72 const v6gatewayfirst = `00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
73 20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
74 00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
75 `
76 const v6gatewaylast = `20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
77 00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
78 00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
79 `
80 const v6gatewaymiddle = `20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
81 00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
82 00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
83 `
84 const v6noDefaultRoutes = `00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
85 20010001000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00000001 docker0
86 20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
87 fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
88 `
89 const v6nothing = ``
90 const v6badDestination = `2001000200000000 7a 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
91 `
92 const v6badGateway = `00000000000000000000000000000000 00 00000000000000000000000000000000 00 200100010000000000000000000000000012 00000064 00000000 00000000 00000003 eth3
93 `
94 const v6route_Invalidhex = `000000000000000000000000000000000 00 00000000000000000000000000000000 00 fe80000000000000021fcafffea0ec00 00000064 00000000 00000000 00000003 enp1s0f0
95
96 `
97
98 const (
99 flagUp = net.FlagUp | net.FlagBroadcast | net.FlagMulticast
100 flagDown = net.FlagBroadcast | net.FlagMulticast
101 flagLoopback = net.FlagUp | net.FlagLoopback
102 flagP2P = net.FlagUp | net.FlagPointToPoint
103 )
104
105 func makeIntf(index int, name string, flags net.Flags) net.Interface {
106 mac := net.HardwareAddr{0, 0x32, 0x7d, 0x69, 0xf7, byte(0x30 + index)}
107 return net.Interface{
108 Index: index,
109 MTU: 1500,
110 Name: name,
111 HardwareAddr: mac,
112 Flags: flags}
113 }
114
115 var (
116 downIntf = makeIntf(1, "eth3", flagDown)
117 loopbackIntf = makeIntf(1, "lo", flagLoopback)
118 p2pIntf = makeIntf(1, "lo", flagP2P)
119 upIntf = makeIntf(1, "eth3", flagUp)
120 )
121
122 var (
123 ipv4Route = Route{Interface: "eth3", Destination: netutils.ParseIPSloppy("0.0.0.0"), Gateway: netutils.ParseIPSloppy("10.254.0.1"), Family: familyIPv4}
124 ipv6Route = Route{Interface: "eth3", Destination: netutils.ParseIPSloppy("::"), Gateway: netutils.ParseIPSloppy("2001:1::1"), Family: familyIPv6}
125 )
126
127 var (
128 noRoutes = []Route{}
129 routeV4 = []Route{ipv4Route}
130 routeV6 = []Route{ipv6Route}
131 bothRoutes = []Route{ipv4Route, ipv6Route}
132 )
133
134 func TestGetIPv4Routes(t *testing.T) {
135 testCases := []struct {
136 tcase string
137 route string
138 count int
139 expected *Route
140 errStrFrag string
141 }{
142 {"gatewayfirst", gatewayfirst, 1, &ipv4Route, ""},
143 {"gatewaymiddle", gatewaymiddle, 1, &ipv4Route, ""},
144 {"gatewaylast", gatewaylast, 1, &ipv4Route, ""},
145 {"no routes", nothing, 0, nil, ""},
146 {"badDestination", badDestination, 0, nil, "invalid IPv4"},
147 {"badGateway", badGateway, 0, nil, "invalid IPv4"},
148 {"route_Invalidhex", route_Invalidhex, 0, nil, "odd length hex string"},
149 {"no default routes", noInternetConnection, 0, nil, ""},
150 }
151 for _, tc := range testCases {
152 r := strings.NewReader(tc.route)
153 routes, err := getIPv4DefaultRoutes(r)
154 if err != nil {
155 if !strings.Contains(err.Error(), tc.errStrFrag) {
156 t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
157 }
158 } else if tc.errStrFrag != "" {
159 t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
160 } else {
161 if tc.count != len(routes) {
162 t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
163 } else if tc.count == 1 {
164 if !tc.expected.Gateway.Equal(routes[0].Gateway) {
165 t.Errorf("case[%s]: expected %v, got %v .err : %v", tc.tcase, tc.expected, routes, err)
166 }
167 if !routes[0].Destination.Equal(net.IPv4zero) {
168 t.Errorf("case[%s}: destination is not for default route (not zero)", tc.tcase)
169 }
170
171 }
172 }
173 }
174 }
175
176 func TestGetIPv6Routes(t *testing.T) {
177 testCases := []struct {
178 tcase string
179 route string
180 count int
181 expected *Route
182 errStrFrag string
183 }{
184 {"v6 gatewayfirst", v6gatewayfirst, 1, &ipv6Route, ""},
185 {"v6 gatewaymiddle", v6gatewaymiddle, 1, &ipv6Route, ""},
186 {"v6 gatewaylast", v6gatewaylast, 1, &ipv6Route, ""},
187 {"v6 no routes", v6nothing, 0, nil, ""},
188 {"v6 badDestination", v6badDestination, 0, nil, "invalid IPv6"},
189 {"v6 badGateway", v6badGateway, 0, nil, "invalid IPv6"},
190 {"v6 route_Invalidhex", v6route_Invalidhex, 0, nil, "odd length hex string"},
191 {"v6 no default routes", v6noDefaultRoutes, 0, nil, ""},
192 }
193 for _, tc := range testCases {
194 r := strings.NewReader(tc.route)
195 routes, err := getIPv6DefaultRoutes(r)
196 if err != nil {
197 if !strings.Contains(err.Error(), tc.errStrFrag) {
198 t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
199 }
200 } else if tc.errStrFrag != "" {
201 t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
202 } else {
203 if tc.count != len(routes) {
204 t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
205 } else if tc.count == 1 {
206 if !tc.expected.Gateway.Equal(routes[0].Gateway) {
207 t.Errorf("case[%s]: expected %v, got %v .err : %v", tc.tcase, tc.expected, routes, err)
208 }
209 if !routes[0].Destination.Equal(net.IPv6zero) {
210 t.Errorf("case[%s}: destination is not for default route (not zero)", tc.tcase)
211 }
212 }
213 }
214 }
215 }
216
217 func TestParseIP(t *testing.T) {
218 testCases := []struct {
219 tcase string
220 ip string
221 family AddressFamily
222 success bool
223 expected net.IP
224 }{
225 {"empty", "", familyIPv4, false, nil},
226 {"too short", "AA", familyIPv4, false, nil},
227 {"too long", "0011223344", familyIPv4, false, nil},
228 {"invalid", "invalid!", familyIPv4, false, nil},
229 {"zero", "00000000", familyIPv4, true, net.IP{0, 0, 0, 0}},
230 {"ffff", "FFFFFFFF", familyIPv4, true, net.IP{0xff, 0xff, 0xff, 0xff}},
231 {"valid v4", "12345678", familyIPv4, true, net.IP{120, 86, 52, 18}},
232 {"valid v6", "fe800000000000000000000000000000", familyIPv6, true, net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
233 {"v6 too short", "fe80000000000000021fcafffea0ec0", familyIPv6, false, nil},
234 {"v6 too long", "fe80000000000000021fcafffea0ec002", familyIPv6, false, nil},
235 }
236 for _, tc := range testCases {
237 ip, err := parseIP(tc.ip, tc.family)
238 if !ip.Equal(tc.expected) {
239 t.Errorf("case[%v]: expected %q, got %q . err : %v", tc.tcase, tc.expected, ip, err)
240 }
241 }
242 }
243
244 func TestIsInterfaceUp(t *testing.T) {
245 testCases := []struct {
246 tcase string
247 intf *net.Interface
248 expected bool
249 }{
250 {"up", &net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}, true},
251 {"down", &net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}, false},
252 {"no interface", nil, false},
253 }
254 for _, tc := range testCases {
255 it := isInterfaceUp(tc.intf)
256 if it != tc.expected {
257 t.Errorf("case[%v]: expected %v, got %v .", tc.tcase, tc.expected, it)
258 }
259 }
260 }
261
262 type addrStruct struct{ val string }
263
264 func (a addrStruct) Network() string {
265 return a.val
266 }
267 func (a addrStruct) String() string {
268 return a.val
269 }
270
271 func TestFinalIP(t *testing.T) {
272 testCases := []struct {
273 tcase string
274 addr []net.Addr
275 family AddressFamily
276 expected net.IP
277 }{
278 {"no ipv4", []net.Addr{addrStruct{val: "2001::5/64"}}, familyIPv4, nil},
279 {"no ipv6", []net.Addr{addrStruct{val: "10.128.0.4/32"}}, familyIPv6, nil},
280 {"invalidV4CIDR", []net.Addr{addrStruct{val: "10.20.30.40.50/24"}}, familyIPv4, nil},
281 {"invalidV6CIDR", []net.Addr{addrStruct{val: "fe80::2f7:67fff:fe6e:2956/64"}}, familyIPv6, nil},
282 {"loopback", []net.Addr{addrStruct{val: "127.0.0.1/24"}}, familyIPv4, nil},
283 {"loopbackv6", []net.Addr{addrStruct{val: "::1/128"}}, familyIPv6, nil},
284 {"link local v4", []net.Addr{addrStruct{val: "169.254.1.10/16"}}, familyIPv4, nil},
285 {"link local v6", []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}}, familyIPv6, nil},
286 {"ip4", []net.Addr{addrStruct{val: "10.254.12.132/17"}}, familyIPv4, netutils.ParseIPSloppy("10.254.12.132")},
287 {"ip6", []net.Addr{addrStruct{val: "2001::5/64"}}, familyIPv6, netutils.ParseIPSloppy("2001::5")},
288
289 {"no addresses", []net.Addr{}, familyIPv4, nil},
290 }
291 for _, tc := range testCases {
292 ip, err := getMatchingGlobalIP(tc.addr, tc.family)
293 if !ip.Equal(tc.expected) {
294 t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, ip, err)
295 }
296 }
297 }
298
299 func TestAddrs(t *testing.T) {
300 var nw networkInterfacer = validNetworkInterface{}
301 intf := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}
302 addrs, err := nw.Addrs(&intf)
303 if err != nil {
304 t.Errorf("expected no error got : %v", err)
305 }
306 if len(addrs) != 2 {
307 t.Errorf("expected addrs: 2 got null")
308 }
309 }
310
311
312 type validNetworkInterface struct {
313 }
314
315 func (_ validNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
316 return &upIntf, nil
317 }
318 func (_ validNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
319 var ifat []net.Addr
320 ifat = []net.Addr{
321 addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}}
322 return ifat, nil
323 }
324 func (_ validNetworkInterface) Interfaces() ([]net.Interface, error) {
325 return []net.Interface{upIntf}, nil
326 }
327
328
329 type v4v6NetworkInterface struct {
330 }
331
332 func (_ v4v6NetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
333 return &upIntf, nil
334 }
335 func (_ v4v6NetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
336 var ifat []net.Addr
337 ifat = []net.Addr{
338 addrStruct{val: "2001::10/64"}, addrStruct{val: "10.254.71.145/17"}}
339 return ifat, nil
340 }
341 func (_ v4v6NetworkInterface) Interfaces() ([]net.Interface, error) {
342 return []net.Interface{upIntf}, nil
343 }
344
345
346 type ipv6NetworkInterface struct {
347 }
348
349 func (_ ipv6NetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
350 return &upIntf, nil
351 }
352 func (_ ipv6NetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
353 var ifat []net.Addr
354 ifat = []net.Addr{addrStruct{val: "2001::200/64"}}
355 return ifat, nil
356 }
357
358 func (_ ipv6NetworkInterface) Interfaces() ([]net.Interface, error) {
359 return []net.Interface{upIntf}, nil
360 }
361
362
363 type networkInterfaceWithOnlyLinkLocals struct {
364 }
365
366 func (_ networkInterfaceWithOnlyLinkLocals) InterfaceByName(intfName string) (*net.Interface, error) {
367 return &upIntf, nil
368 }
369 func (_ networkInterfaceWithOnlyLinkLocals) Addrs(intf *net.Interface) ([]net.Addr, error) {
370 var ifat []net.Addr
371 ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "fe80::200/10"}}
372 return ifat, nil
373 }
374 func (_ networkInterfaceWithOnlyLinkLocals) Interfaces() ([]net.Interface, error) {
375 return []net.Interface{upIntf}, nil
376 }
377
378
379 type failGettingNetworkInterface struct {
380 }
381
382 func (_ failGettingNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
383 return nil, fmt.Errorf("unable get Interface")
384 }
385 func (_ failGettingNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
386 return nil, nil
387 }
388 func (_ failGettingNetworkInterface) Interfaces() ([]net.Interface, error) {
389 return nil, fmt.Errorf("mock failed getting all interfaces")
390 }
391
392
393 type noNetworkInterface struct {
394 }
395
396 func (_ noNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
397 return nil, fmt.Errorf("no such network interface")
398 }
399 func (_ noNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
400 return nil, nil
401 }
402 func (_ noNetworkInterface) Interfaces() ([]net.Interface, error) {
403 return []net.Interface{}, nil
404 }
405
406
407 type downNetworkInterface struct {
408 }
409
410 func (_ downNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
411 return &downIntf, nil
412 }
413 func (_ downNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
414 var ifat []net.Addr
415 ifat = []net.Addr{
416 addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}}
417 return ifat, nil
418 }
419 func (_ downNetworkInterface) Interfaces() ([]net.Interface, error) {
420 return []net.Interface{downIntf}, nil
421 }
422
423
424 type loopbackNetworkInterface struct {
425 }
426
427 func (_ loopbackNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
428 return &loopbackIntf, nil
429 }
430 func (_ loopbackNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
431 var ifat []net.Addr
432 ifat = []net.Addr{
433 addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"}}
434 return ifat, nil
435 }
436 func (_ loopbackNetworkInterface) Interfaces() ([]net.Interface, error) {
437 return []net.Interface{loopbackIntf}, nil
438 }
439
440
441 type p2pNetworkInterface struct {
442 }
443
444 func (_ p2pNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
445 return &p2pIntf, nil
446 }
447 func (_ p2pNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
448 var ifat []net.Addr
449 ifat = []net.Addr{
450 addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"}}
451 return ifat, nil
452 }
453 func (_ p2pNetworkInterface) Interfaces() ([]net.Interface, error) {
454 return []net.Interface{p2pIntf}, nil
455 }
456
457
458 type linkLocalLoopbackNetworkInterface struct {
459 }
460
461 func (_ linkLocalLoopbackNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
462 if intfName == LoopbackInterfaceName {
463 return &loopbackIntf, nil
464 }
465 return &upIntf, nil
466 }
467 func (_ linkLocalLoopbackNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
468 var ifat []net.Addr
469 ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "fe80::200/10"}}
470 if intf.Name == LoopbackInterfaceName {
471 ifat = []net.Addr{addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"},
472
473 addrStruct{val: "10.1.1.1/32"}, addrStruct{val: "fd00:1:1::1/128"}}
474 }
475 return ifat, nil
476 }
477 func (_ linkLocalLoopbackNetworkInterface) Interfaces() ([]net.Interface, error) {
478 return []net.Interface{upIntf, loopbackIntf}, nil
479 }
480
481
482 type globalsNetworkInterface struct {
483 }
484
485 func (_ globalsNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
486 if intfName == LoopbackInterfaceName {
487 return &loopbackIntf, nil
488 }
489 return &upIntf, nil
490 }
491 func (_ globalsNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
492 var ifat []net.Addr
493 ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "fe80::200/10"},
494 addrStruct{val: "192.168.1.1/31"}, addrStruct{val: "fd00::200/127"}}
495 if intf.Name == LoopbackInterfaceName {
496 ifat = []net.Addr{addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"},
497
498 addrStruct{val: "10.1.1.1/32"}, addrStruct{val: "fd00:1:1::1/128"}}
499 }
500 return ifat, nil
501 }
502 func (_ globalsNetworkInterface) Interfaces() ([]net.Interface, error) {
503 return []net.Interface{upIntf, loopbackIntf}, nil
504 }
505
506
507 type networkInterfaceFailGetAddrs struct {
508 }
509
510 func (_ networkInterfaceFailGetAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
511 return &upIntf, nil
512 }
513 func (_ networkInterfaceFailGetAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
514 return nil, fmt.Errorf("unable to get Addrs")
515 }
516 func (_ networkInterfaceFailGetAddrs) Interfaces() ([]net.Interface, error) {
517 return []net.Interface{upIntf}, nil
518 }
519
520
521 type networkInterfaceWithNoAddrs struct {
522 }
523
524 func (_ networkInterfaceWithNoAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
525 return &upIntf, nil
526 }
527 func (_ networkInterfaceWithNoAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
528 ifat := []net.Addr{}
529 return ifat, nil
530 }
531 func (_ networkInterfaceWithNoAddrs) Interfaces() ([]net.Interface, error) {
532 return []net.Interface{upIntf}, nil
533 }
534
535
536 type networkInterfaceWithInvalidAddr struct {
537 }
538
539 func (_ networkInterfaceWithInvalidAddr) InterfaceByName(intfName string) (*net.Interface, error) {
540 return &upIntf, nil
541 }
542 func (_ networkInterfaceWithInvalidAddr) Addrs(intf *net.Interface) ([]net.Addr, error) {
543 var ifat []net.Addr
544 ifat = []net.Addr{addrStruct{val: "10.20.30.40.50/24"}}
545 return ifat, nil
546 }
547 func (_ networkInterfaceWithInvalidAddr) Interfaces() ([]net.Interface, error) {
548 return []net.Interface{upIntf}, nil
549 }
550
551 func TestGetIPFromInterface(t *testing.T) {
552 testCases := []struct {
553 tcase string
554 nwname string
555 family AddressFamily
556 nw networkInterfacer
557 expected net.IP
558 errStrFrag string
559 }{
560 {"ipv4", "eth3", familyIPv4, validNetworkInterface{}, netutils.ParseIPSloppy("10.254.71.145"), ""},
561 {"ipv6", "eth3", familyIPv6, ipv6NetworkInterface{}, netutils.ParseIPSloppy("2001::200"), ""},
562 {"no ipv4", "eth3", familyIPv4, ipv6NetworkInterface{}, nil, ""},
563 {"no ipv6", "eth3", familyIPv6, validNetworkInterface{}, nil, ""},
564 {"I/F down", "eth3", familyIPv4, downNetworkInterface{}, nil, ""},
565 {"I/F get fail", "eth3", familyIPv4, noNetworkInterface{}, nil, "no such network interface"},
566 {"fail get addr", "eth3", familyIPv4, networkInterfaceFailGetAddrs{}, nil, "unable to get Addrs"},
567 {"bad addr", "eth3", familyIPv4, networkInterfaceWithInvalidAddr{}, nil, "invalid CIDR"},
568 }
569 for _, tc := range testCases {
570 ip, err := getIPFromInterface(tc.nwname, tc.family, tc.nw)
571 if err != nil {
572 if !strings.Contains(err.Error(), tc.errStrFrag) {
573 t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
574 }
575 } else if tc.errStrFrag != "" {
576 t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
577 } else if !ip.Equal(tc.expected) {
578 t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
579 }
580 }
581 }
582
583 func TestGetIPFromLoopbackInterface(t *testing.T) {
584 testCases := []struct {
585 tcase string
586 family AddressFamily
587 nw networkInterfacer
588 expected net.IP
589 errStrFrag string
590 }{
591 {"ipv4", familyIPv4, linkLocalLoopbackNetworkInterface{}, netutils.ParseIPSloppy("10.1.1.1"), ""},
592 {"ipv6", familyIPv6, linkLocalLoopbackNetworkInterface{}, netutils.ParseIPSloppy("fd00:1:1::1"), ""},
593 {"no global ipv4", familyIPv4, loopbackNetworkInterface{}, nil, ""},
594 {"no global ipv6", familyIPv6, loopbackNetworkInterface{}, nil, ""},
595 }
596 for _, tc := range testCases {
597 ip, err := getIPFromLoopbackInterface(tc.family, tc.nw)
598 if err != nil {
599 if !strings.Contains(err.Error(), tc.errStrFrag) {
600 t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
601 }
602 } else if tc.errStrFrag != "" {
603 t.Errorf("case[%s]: Error %q expected, but seen %v", tc.tcase, tc.errStrFrag, err)
604 } else if !ip.Equal(tc.expected) {
605 t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
606 }
607 }
608 }
609
610 func TestChooseHostInterfaceFromRoute(t *testing.T) {
611 testCases := []struct {
612 tcase string
613 routes []Route
614 nw networkInterfacer
615 order AddressFamilyPreference
616 expected net.IP
617 }{
618 {"single-stack ipv4", routeV4, validNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.254.71.145")},
619 {"single-stack ipv4, prefer v6", routeV4, validNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("10.254.71.145")},
620 {"single-stack ipv6", routeV6, ipv6NetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("2001::200")},
621 {"single-stack ipv6, prefer v6", routeV6, ipv6NetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("2001::200")},
622 {"dual stack", bothRoutes, v4v6NetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.254.71.145")},
623 {"dual stack, prefer v6", bothRoutes, v4v6NetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("2001::10")},
624 {"LLA and loopback with global, IPv4", routeV4, linkLocalLoopbackNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.1.1.1")},
625 {"LLA and loopback with global, IPv6", routeV6, linkLocalLoopbackNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("fd00:1:1::1")},
626 {"LLA and loopback with global, dual stack prefer IPv4", bothRoutes, linkLocalLoopbackNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.1.1.1")},
627 {"LLA and loopback with global, dual stack prefer IPv6", bothRoutes, linkLocalLoopbackNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("fd00:1:1::1")},
628 {"LLA and loopback with global, no routes", noRoutes, linkLocalLoopbackNetworkInterface{}, preferIPv6, nil},
629 {"interface and loopback with global, IPv4", routeV4, globalsNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("192.168.1.1")},
630 {"interface and loopback with global, IPv6", routeV6, globalsNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("fd00::200")},
631 {"interface and loopback with global, dual stack prefer IPv4", bothRoutes, globalsNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("192.168.1.1")},
632 {"interface and loopback with global, dual stack prefer IPv6", bothRoutes, globalsNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("fd00::200")},
633 {"interface and loopback with global, no routes", noRoutes, globalsNetworkInterface{}, preferIPv6, nil},
634 {"all LLA", routeV4, networkInterfaceWithOnlyLinkLocals{}, preferIPv4, nil},
635 {"no routes", noRoutes, validNetworkInterface{}, preferIPv4, nil},
636 {"fail get IP", routeV4, networkInterfaceFailGetAddrs{}, preferIPv4, nil},
637 }
638 for _, tc := range testCases {
639 ip, err := chooseHostInterfaceFromRoute(tc.routes, tc.nw, tc.order)
640 if !ip.Equal(tc.expected) {
641 t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
642 }
643 }
644 }
645
646 func TestMemberOf(t *testing.T) {
647 testCases := []struct {
648 tcase string
649 ip net.IP
650 family AddressFamily
651 expected bool
652 }{
653 {"ipv4 is 4", netutils.ParseIPSloppy("10.20.30.40"), familyIPv4, true},
654 {"ipv4 is 6", netutils.ParseIPSloppy("10.10.10.10"), familyIPv6, false},
655 {"ipv6 is 4", netutils.ParseIPSloppy("2001::100"), familyIPv4, false},
656 {"ipv6 is 6", netutils.ParseIPSloppy("2001::100"), familyIPv6, true},
657 }
658 for _, tc := range testCases {
659 if memberOf(tc.ip, tc.family) != tc.expected {
660 t.Errorf("case[%s]: expected %+v", tc.tcase, tc.expected)
661 }
662 }
663 }
664
665 func TestGetIPFromHostInterfaces(t *testing.T) {
666 testCases := []struct {
667 tcase string
668 nw networkInterfacer
669 order AddressFamilyPreference
670 expected net.IP
671 errStrFrag string
672 }{
673 {"fail get I/Fs", failGettingNetworkInterface{}, preferIPv4, nil, "failed getting all interfaces"},
674 {"no interfaces", noNetworkInterface{}, preferIPv4, nil, "no interfaces"},
675 {"I/F not up", downNetworkInterface{}, preferIPv4, nil, "no acceptable"},
676 {"loopback only", loopbackNetworkInterface{}, preferIPv4, nil, "no acceptable"},
677 {"P2P I/F only", p2pNetworkInterface{}, preferIPv4, nil, "no acceptable"},
678 {"fail get addrs", networkInterfaceFailGetAddrs{}, preferIPv4, nil, "unable to get Addrs"},
679 {"no addresses", networkInterfaceWithNoAddrs{}, preferIPv4, nil, "no acceptable"},
680 {"invalid addr", networkInterfaceWithInvalidAddr{}, preferIPv4, nil, "invalid CIDR"},
681 {"no matches", networkInterfaceWithOnlyLinkLocals{}, preferIPv4, nil, "no acceptable"},
682 {"single-stack ipv4", validNetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.254.71.145"), ""},
683 {"single-stack ipv4, prefer ipv6", validNetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("10.254.71.145"), ""},
684 {"single-stack ipv6", ipv6NetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("2001::200"), ""},
685 {"single-stack ipv6, prefer ipv6", ipv6NetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("2001::200"), ""},
686 {"dual stack", v4v6NetworkInterface{}, preferIPv4, netutils.ParseIPSloppy("10.254.71.145"), ""},
687 {"dual stack, prefer ipv6", v4v6NetworkInterface{}, preferIPv6, netutils.ParseIPSloppy("2001::10"), ""},
688 }
689
690 for _, tc := range testCases {
691 ip, err := chooseIPFromHostInterfaces(tc.nw, tc.order)
692 if !ip.Equal(tc.expected) {
693 t.Errorf("case[%s]: expected %+v, got %+v with err : %v", tc.tcase, tc.expected, ip, err)
694 }
695 if err != nil && !strings.Contains(err.Error(), tc.errStrFrag) {
696 t.Errorf("case[%s]: unable to find %q in error string %q", tc.tcase, tc.errStrFrag, err.Error())
697 }
698 }
699 }
700
701 func makeRouteFile(content string, t *testing.T) (*os.File, error) {
702 routeFile, err := os.CreateTemp("", "route")
703 if err != nil {
704 return nil, err
705 }
706
707 if _, err := routeFile.Write([]byte(content)); err != nil {
708 return routeFile, err
709 }
710 err = routeFile.Close()
711 return routeFile, err
712 }
713
714 func TestFailGettingIPv4Routes(t *testing.T) {
715 defer func() { v4File.name = ipv4RouteFile }()
716
717
718 v4File.name = "no-such-file"
719 errStrFrag := "no such file"
720 _, err := v4File.extract()
721 if err == nil {
722 t.Errorf("Expected error trying to read non-existent v4 route file")
723 }
724 if !strings.Contains(err.Error(), errStrFrag) {
725 t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
726 }
727 }
728
729 func TestFailGettingIPv6Routes(t *testing.T) {
730 defer func() { v6File.name = ipv6RouteFile }()
731
732
733 v6File.name = "no-such-file"
734 errStrFrag := "no such file"
735 _, err := v6File.extract()
736 if err == nil {
737 t.Errorf("Expected error trying to read non-existent v6 route file")
738 }
739 if !strings.Contains(err.Error(), errStrFrag) {
740 t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
741 }
742 }
743
744 func TestGetAllDefaultRoutesFailNoV4RouteFile(t *testing.T) {
745 defer func() { v4File.name = ipv4RouteFile }()
746
747
748 v4File.name = "no-such-file"
749 errStrFrag := "no such file"
750 _, err := getAllDefaultRoutes()
751 if err == nil {
752 t.Errorf("Expected error trying to read non-existent v4 route file")
753 }
754 if !strings.Contains(err.Error(), errStrFrag) {
755 t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
756 }
757 }
758
759 func TestGetAllDefaultRoutes(t *testing.T) {
760 testCases := []struct {
761 tcase string
762 v4Info string
763 v6Info string
764 count int
765 expected []Route
766 errStrFrag string
767 }{
768 {"no routes", noInternetConnection, v6noDefaultRoutes, 0, nil, "no default routes"},
769 {"only v4 route", gatewayfirst, v6noDefaultRoutes, 1, routeV4, ""},
770 {"only v6 route", noInternetConnection, v6gatewayfirst, 1, routeV6, ""},
771 {"v4 and v6 routes", gatewayfirst, v6gatewayfirst, 2, bothRoutes, ""},
772 }
773 defer func() {
774 v4File.name = ipv4RouteFile
775 v6File.name = ipv6RouteFile
776 }()
777
778 for _, tc := range testCases {
779 routeFile, err := makeRouteFile(tc.v4Info, t)
780 if routeFile != nil {
781 defer os.Remove(routeFile.Name())
782 }
783 if err != nil {
784 t.Errorf("case[%s]: test setup failure for IPv4 route file: %v", tc.tcase, err)
785 }
786 v4File.name = routeFile.Name()
787 v6routeFile, err := makeRouteFile(tc.v6Info, t)
788 if v6routeFile != nil {
789 defer os.Remove(v6routeFile.Name())
790 }
791 if err != nil {
792 t.Errorf("case[%s]: test setup failure for IPv6 route file: %v", tc.tcase, err)
793 }
794 v6File.name = v6routeFile.Name()
795
796 routes, err := getAllDefaultRoutes()
797 if err != nil {
798 if !strings.Contains(err.Error(), tc.errStrFrag) {
799 t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
800 }
801 } else if tc.errStrFrag != "" {
802 t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
803 } else {
804 if tc.count != len(routes) {
805 t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
806 }
807 for i, expected := range tc.expected {
808 if !expected.Gateway.Equal(routes[i].Gateway) {
809 t.Errorf("case[%s]: at %d expected %v, got %v .err : %v", tc.tcase, i, tc.expected, routes, err)
810 }
811 zeroIP := net.IPv4zero
812 if expected.Family == familyIPv6 {
813 zeroIP = net.IPv6zero
814 }
815 if !routes[i].Destination.Equal(zeroIP) {
816 t.Errorf("case[%s}: at %d destination is not for default route (not %v)", tc.tcase, i, zeroIP)
817 }
818 }
819 }
820 }
821 }
822
View as plain text