Home | History | Annotate | Download | only in net
      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 		switch runtime.GOOS {
     62 		case "solaris":
     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 
     82 func 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 
    101 func 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 
    128 func 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 
    160 type ifStats struct {
    161 	loop  int // # of active loopback interfaces
    162 	other int // # of active other interfaces
    163 }
    164 
    165 func 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 
    179 type routeStats struct {
    180 	ipv4, ipv6 int // # of active connected unicast, anycast or multicast routes
    181 }
    182 
    183 func 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 && prefixLen != 8*IPv4len) { // 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 
    239 func 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 
    263 func 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 
    277 func checkMulticastStats(ifStats *ifStats, uniStats, multiStats *routeStats) error {
    278 	switch runtime.GOOS {
    279 	case "dragonfly", "nacl", "netbsd", "openbsd", "plan9", "solaris":
    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 
    300 func BenchmarkInterfaces(b *testing.B) {
    301 	testHookUninstaller.Do(uninstallTestHooks)
    302 
    303 	for i := 0; i < b.N; i++ {
    304 		if _, err := Interfaces(); err != nil {
    305 			b.Fatal(err)
    306 		}
    307 	}
    308 }
    309 
    310 func BenchmarkInterfaceByIndex(b *testing.B) {
    311 	testHookUninstaller.Do(uninstallTestHooks)
    312 
    313 	ifi := loopbackInterface()
    314 	if ifi == nil {
    315 		b.Skip("loopback interface not found")
    316 	}
    317 	for i := 0; i < b.N; i++ {
    318 		if _, err := InterfaceByIndex(ifi.Index); err != nil {
    319 			b.Fatal(err)
    320 		}
    321 	}
    322 }
    323 
    324 func BenchmarkInterfaceByName(b *testing.B) {
    325 	testHookUninstaller.Do(uninstallTestHooks)
    326 
    327 	ifi := loopbackInterface()
    328 	if ifi == nil {
    329 		b.Skip("loopback interface not found")
    330 	}
    331 	for i := 0; i < b.N; i++ {
    332 		if _, err := InterfaceByName(ifi.Name); err != nil {
    333 			b.Fatal(err)
    334 		}
    335 	}
    336 }
    337 
    338 func BenchmarkInterfaceAddrs(b *testing.B) {
    339 	testHookUninstaller.Do(uninstallTestHooks)
    340 
    341 	for i := 0; i < b.N; i++ {
    342 		if _, err := InterfaceAddrs(); err != nil {
    343 			b.Fatal(err)
    344 		}
    345 	}
    346 }
    347 
    348 func BenchmarkInterfacesAndAddrs(b *testing.B) {
    349 	testHookUninstaller.Do(uninstallTestHooks)
    350 
    351 	ifi := loopbackInterface()
    352 	if ifi == nil {
    353 		b.Skip("loopback interface not found")
    354 	}
    355 	for i := 0; i < b.N; i++ {
    356 		if _, err := ifi.Addrs(); err != nil {
    357 			b.Fatal(err)
    358 		}
    359 	}
    360 }
    361 
    362 func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) {
    363 	testHookUninstaller.Do(uninstallTestHooks)
    364 
    365 	ifi := loopbackInterface()
    366 	if ifi == nil {
    367 		b.Skip("loopback interface not found")
    368 	}
    369 	for i := 0; i < b.N; i++ {
    370 		if _, err := ifi.MulticastAddrs(); err != nil {
    371 			b.Fatal(err)
    372 		}
    373 	}
    374 }
    375