1// Copyright 2011 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package net 6 7import ( 8 "fmt" 9 "reflect" 10 "runtime" 11 "testing" 12) 13 14// loopbackInterface returns an available logical network interface 15// for loopback tests. It returns nil if no suitable interface is 16// found. 17func loopbackInterface() *Interface { 18 ift, err := Interfaces() 19 if err != nil { 20 return nil 21 } 22 for _, ifi := range ift { 23 if ifi.Flags&FlagLoopback != 0 && ifi.Flags&FlagUp != 0 { 24 return &ifi 25 } 26 } 27 return nil 28} 29 30// ipv6LinkLocalUnicastAddr returns an IPv6 link-local unicast address 31// on the given network interface for tests. It returns "" if no 32// suitable address is found. 33func ipv6LinkLocalUnicastAddr(ifi *Interface) string { 34 if ifi == nil { 35 return "" 36 } 37 ifat, err := ifi.Addrs() 38 if err != nil { 39 return "" 40 } 41 for _, ifa := range ifat { 42 if ifa, ok := ifa.(*IPNet); ok { 43 if ifa.IP.To4() == nil && ifa.IP.IsLinkLocalUnicast() { 44 return ifa.IP.String() 45 } 46 } 47 } 48 return "" 49} 50 51func TestInterfaces(t *testing.T) { 52 ift, err := Interfaces() 53 if err != nil { 54 t.Fatal(err) 55 } 56 for _, ifi := range ift { 57 ifxi, err := InterfaceByIndex(ifi.Index) 58 if err != nil { 59 t.Fatal(err) 60 } 61 switch runtime.GOOS { 62 case "solaris", "illumos": 63 if ifxi.Index != ifi.Index { 64 t.Errorf("got %v; want %v", ifxi, ifi) 65 } 66 default: 67 if !reflect.DeepEqual(ifxi, &ifi) { 68 t.Errorf("got %v; want %v", ifxi, ifi) 69 } 70 } 71 ifxn, err := InterfaceByName(ifi.Name) 72 if err != nil { 73 t.Fatal(err) 74 } 75 if !reflect.DeepEqual(ifxn, &ifi) { 76 t.Errorf("got %v; want %v", ifxn, ifi) 77 } 78 t.Logf("%s: flags=%v index=%d mtu=%d hwaddr=%v", ifi.Name, ifi.Flags, ifi.Index, ifi.MTU, ifi.HardwareAddr) 79 } 80} 81 82func TestInterfaceAddrs(t *testing.T) { 83 ift, err := Interfaces() 84 if err != nil { 85 t.Fatal(err) 86 } 87 ifStats := interfaceStats(ift) 88 ifat, err := InterfaceAddrs() 89 if err != nil { 90 t.Fatal(err) 91 } 92 uniStats, err := validateInterfaceUnicastAddrs(ifat) 93 if err != nil { 94 t.Fatal(err) 95 } 96 if err := checkUnicastStats(ifStats, uniStats); err != nil { 97 t.Fatal(err) 98 } 99} 100 101func TestInterfaceUnicastAddrs(t *testing.T) { 102 ift, err := Interfaces() 103 if err != nil { 104 t.Fatal(err) 105 } 106 ifStats := interfaceStats(ift) 107 if err != nil { 108 t.Fatal(err) 109 } 110 var uniStats routeStats 111 for _, ifi := range ift { 112 ifat, err := ifi.Addrs() 113 if err != nil { 114 t.Fatal(ifi, err) 115 } 116 stats, err := validateInterfaceUnicastAddrs(ifat) 117 if err != nil { 118 t.Fatal(ifi, err) 119 } 120 uniStats.ipv4 += stats.ipv4 121 uniStats.ipv6 += stats.ipv6 122 } 123 if err := checkUnicastStats(ifStats, &uniStats); err != nil { 124 t.Fatal(err) 125 } 126} 127 128func TestInterfaceMulticastAddrs(t *testing.T) { 129 ift, err := Interfaces() 130 if err != nil { 131 t.Fatal(err) 132 } 133 ifStats := interfaceStats(ift) 134 ifat, err := InterfaceAddrs() 135 if err != nil { 136 t.Fatal(err) 137 } 138 uniStats, err := validateInterfaceUnicastAddrs(ifat) 139 if err != nil { 140 t.Fatal(err) 141 } 142 var multiStats routeStats 143 for _, ifi := range ift { 144 ifmat, err := ifi.MulticastAddrs() 145 if err != nil { 146 t.Fatal(ifi, err) 147 } 148 stats, err := validateInterfaceMulticastAddrs(ifmat) 149 if err != nil { 150 t.Fatal(ifi, err) 151 } 152 multiStats.ipv4 += stats.ipv4 153 multiStats.ipv6 += stats.ipv6 154 } 155 if err := checkMulticastStats(ifStats, uniStats, &multiStats); err != nil { 156 t.Fatal(err) 157 } 158} 159 160type ifStats struct { 161 loop int // # of active loopback interfaces 162 other int // # of active other interfaces 163} 164 165func interfaceStats(ift []Interface) *ifStats { 166 var stats ifStats 167 for _, ifi := range ift { 168 if ifi.Flags&FlagUp != 0 { 169 if ifi.Flags&FlagLoopback != 0 { 170 stats.loop++ 171 } else { 172 stats.other++ 173 } 174 } 175 } 176 return &stats 177} 178 179type routeStats struct { 180 ipv4, ipv6 int // # of active connected unicast, anycast or multicast routes 181} 182 183func validateInterfaceUnicastAddrs(ifat []Addr) (*routeStats, error) { 184 // Note: BSD variants allow assigning any IPv4/IPv6 address 185 // prefix to IP interface. For example, 186 // - 0.0.0.0/0 through 255.255.255.255/32 187 // - ::/0 through ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 188 // In other words, there is no tightly-coupled combination of 189 // interface address prefixes and connected routes. 190 stats := new(routeStats) 191 for _, ifa := range ifat { 192 switch ifa := ifa.(type) { 193 case *IPNet: 194 if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() || ifa.Mask == nil { 195 return nil, fmt.Errorf("unexpected value: %#v", ifa) 196 } 197 if len(ifa.IP) != IPv6len { 198 return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa) 199 } 200 prefixLen, maxPrefixLen := ifa.Mask.Size() 201 if ifa.IP.To4() != nil { 202 if 0 >= prefixLen || prefixLen > 8*IPv4len || maxPrefixLen != 8*IPv4len { 203 return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) 204 } 205 if ifa.IP.IsLoopback() && prefixLen < 8 { // see RFC 1122 206 return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) 207 } 208 stats.ipv4++ 209 } 210 if ifa.IP.To16() != nil && ifa.IP.To4() == nil { 211 if 0 >= prefixLen || prefixLen > 8*IPv6len || maxPrefixLen != 8*IPv6len { 212 return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) 213 } 214 if ifa.IP.IsLoopback() && prefixLen != 8*IPv6len { // see RFC 4291 215 return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) 216 } 217 stats.ipv6++ 218 } 219 case *IPAddr: 220 if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() { 221 return nil, fmt.Errorf("unexpected value: %#v", ifa) 222 } 223 if len(ifa.IP) != IPv6len { 224 return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa) 225 } 226 if ifa.IP.To4() != nil { 227 stats.ipv4++ 228 } 229 if ifa.IP.To16() != nil && ifa.IP.To4() == nil { 230 stats.ipv6++ 231 } 232 default: 233 return nil, fmt.Errorf("unexpected type: %T", ifa) 234 } 235 } 236 return stats, nil 237} 238 239func validateInterfaceMulticastAddrs(ifat []Addr) (*routeStats, error) { 240 stats := new(routeStats) 241 for _, ifa := range ifat { 242 switch ifa := ifa.(type) { 243 case *IPAddr: 244 if ifa == nil || ifa.IP == nil || ifa.IP.IsUnspecified() || !ifa.IP.IsMulticast() { 245 return nil, fmt.Errorf("unexpected value: %#v", ifa) 246 } 247 if len(ifa.IP) != IPv6len { 248 return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa) 249 } 250 if ifa.IP.To4() != nil { 251 stats.ipv4++ 252 } 253 if ifa.IP.To16() != nil && ifa.IP.To4() == nil { 254 stats.ipv6++ 255 } 256 default: 257 return nil, fmt.Errorf("unexpected type: %T", ifa) 258 } 259 } 260 return stats, nil 261} 262 263func checkUnicastStats(ifStats *ifStats, uniStats *routeStats) error { 264 // Test the existence of connected unicast routes for IPv4. 265 if supportsIPv4() && ifStats.loop+ifStats.other > 0 && uniStats.ipv4 == 0 { 266 return fmt.Errorf("num IPv4 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats) 267 } 268 // Test the existence of connected unicast routes for IPv6. 269 // We can assume the existence of ::1/128 when at least one 270 // loopback interface is installed. 271 if supportsIPv6() && ifStats.loop > 0 && uniStats.ipv6 == 0 { 272 return fmt.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats) 273 } 274 return nil 275} 276 277func checkMulticastStats(ifStats *ifStats, uniStats, multiStats *routeStats) error { 278 switch runtime.GOOS { 279 case "aix", "dragonfly", "netbsd", "openbsd", "plan9", "solaris", "illumos": 280 default: 281 // Test the existence of connected multicast route 282 // clones for IPv4. Unlike IPv6, IPv4 multicast 283 // capability is not a mandatory feature, and so IPv4 284 // multicast validation is ignored and we only check 285 // IPv6 below. 286 // 287 // Test the existence of connected multicast route 288 // clones for IPv6. Some platform never uses loopback 289 // interface as the nexthop for multicast routing. 290 // We can assume the existence of connected multicast 291 // route clones when at least two connected unicast 292 // routes, ::1/128 and other, are installed. 293 if supportsIPv6() && ifStats.loop > 0 && uniStats.ipv6 > 1 && multiStats.ipv6 == 0 { 294 return fmt.Errorf("num IPv6 multicast route clones = 0; want >0; summary: %+v, %+v, %+v", ifStats, uniStats, multiStats) 295 } 296 } 297 return nil 298} 299 300func BenchmarkInterfaces(b *testing.B) { 301 b.ReportAllocs() 302 testHookUninstaller.Do(uninstallTestHooks) 303 304 for i := 0; i < b.N; i++ { 305 if _, err := Interfaces(); err != nil { 306 b.Fatal(err) 307 } 308 } 309} 310 311func BenchmarkInterfaceByIndex(b *testing.B) { 312 b.ReportAllocs() 313 testHookUninstaller.Do(uninstallTestHooks) 314 315 ifi := loopbackInterface() 316 if ifi == nil { 317 b.Skip("loopback interface not found") 318 } 319 for i := 0; i < b.N; i++ { 320 if _, err := InterfaceByIndex(ifi.Index); err != nil { 321 b.Fatal(err) 322 } 323 } 324} 325 326func BenchmarkInterfaceByName(b *testing.B) { 327 b.ReportAllocs() 328 testHookUninstaller.Do(uninstallTestHooks) 329 330 ifi := loopbackInterface() 331 if ifi == nil { 332 b.Skip("loopback interface not found") 333 } 334 for i := 0; i < b.N; i++ { 335 if _, err := InterfaceByName(ifi.Name); err != nil { 336 b.Fatal(err) 337 } 338 } 339} 340 341func BenchmarkInterfaceAddrs(b *testing.B) { 342 b.ReportAllocs() 343 testHookUninstaller.Do(uninstallTestHooks) 344 345 for i := 0; i < b.N; i++ { 346 if _, err := InterfaceAddrs(); err != nil { 347 b.Fatal(err) 348 } 349 } 350} 351 352func BenchmarkInterfacesAndAddrs(b *testing.B) { 353 b.ReportAllocs() 354 testHookUninstaller.Do(uninstallTestHooks) 355 356 ifi := loopbackInterface() 357 if ifi == nil { 358 b.Skip("loopback interface not found") 359 } 360 for i := 0; i < b.N; i++ { 361 if _, err := ifi.Addrs(); err != nil { 362 b.Fatal(err) 363 } 364 } 365} 366 367func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) { 368 b.ReportAllocs() 369 testHookUninstaller.Do(uninstallTestHooks) 370 371 ifi := loopbackInterface() 372 if ifi == nil { 373 b.Skip("loopback interface not found") 374 } 375 for i := 0; i < b.N; i++ { 376 if _, err := ifi.MulticastAddrs(); err != nil { 377 b.Fatal(err) 378 } 379 } 380} 381