k8s.io/apimachinery@v0.29.2/pkg/util/net/interface_test.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 // Has a valid IPv4 address (IPv6 is LLA) 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 // Both IPv4 and IPv6 addresses (expecting IPv4 to be used) 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 // Interface with only IPv6 address 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 // Only with link local addresses 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 // Unable to get interface(s) 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 // No interfaces 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 // Interface is down 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 // Loopback interface 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 // Point to point interface 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 // Interface with link locals and loopback interface with global addresses 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 // global addresses on loopback interface 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 // Interface and loopback interface with global addresses 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 // global addresses on loopback interface 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 // Unable to get IP addresses for interface 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 // No addresses for interface 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 // Invalid addresses for interface 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 // Try failure to open file (should not occur, as caller ensures we have IPv4 route file, but being thorough) 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 // Try failure to open file (this would be ignored by caller) 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 // Should not occur, as caller ensures we have IPv4 route file, but being thorough 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 }