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