123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- //go:build !js
- package net
- import (
- "fmt"
- "reflect"
- "runtime"
- "testing"
- )
- // loopbackInterface returns an available logical network interface
- // for loopback tests. It returns nil if no suitable interface is
- // found.
- func loopbackInterface() *Interface {
- ift, err := Interfaces()
- if err != nil {
- return nil
- }
- for _, ifi := range ift {
- if ifi.Flags&FlagLoopback != 0 && ifi.Flags&FlagUp != 0 {
- return &ifi
- }
- }
- return nil
- }
- // ipv6LinkLocalUnicastAddr returns an IPv6 link-local unicast address
- // on the given network interface for tests. It returns "" if no
- // suitable address is found.
- func ipv6LinkLocalUnicastAddr(ifi *Interface) string {
- if ifi == nil {
- return ""
- }
- ifat, err := ifi.Addrs()
- if err != nil {
- return ""
- }
- for _, ifa := range ifat {
- if ifa, ok := ifa.(*IPNet); ok {
- if ifa.IP.To4() == nil && ifa.IP.IsLinkLocalUnicast() {
- return ifa.IP.String()
- }
- }
- }
- return ""
- }
- func TestInterfaces(t *testing.T) {
- ift, err := Interfaces()
- if err != nil {
- t.Fatal(err)
- }
- for _, ifi := range ift {
- ifxi, err := InterfaceByIndex(ifi.Index)
- if err != nil {
- t.Fatal(err)
- }
- switch runtime.GOOS {
- case "solaris", "illumos":
- if ifxi.Index != ifi.Index {
- t.Errorf("got %v; want %v", ifxi, ifi)
- }
- default:
- if !reflect.DeepEqual(ifxi, &ifi) {
- t.Errorf("got %v; want %v", ifxi, ifi)
- }
- }
- ifxn, err := InterfaceByName(ifi.Name)
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(ifxn, &ifi) {
- t.Errorf("got %v; want %v", ifxn, ifi)
- }
- t.Logf("%s: flags=%v index=%d mtu=%d hwaddr=%v", ifi.Name, ifi.Flags, ifi.Index, ifi.MTU, ifi.HardwareAddr)
- }
- }
- func TestInterfaceAddrs(t *testing.T) {
- ift, err := Interfaces()
- if err != nil {
- t.Fatal(err)
- }
- ifStats := interfaceStats(ift)
- ifat, err := InterfaceAddrs()
- if err != nil {
- t.Fatal(err)
- }
- uniStats, err := validateInterfaceUnicastAddrs(ifat)
- if err != nil {
- t.Fatal(err)
- }
- if err := checkUnicastStats(ifStats, uniStats); err != nil {
- t.Fatal(err)
- }
- }
- func TestInterfaceUnicastAddrs(t *testing.T) {
- ift, err := Interfaces()
- if err != nil {
- t.Fatal(err)
- }
- ifStats := interfaceStats(ift)
- if err != nil {
- t.Fatal(err)
- }
- var uniStats routeStats
- for _, ifi := range ift {
- ifat, err := ifi.Addrs()
- if err != nil {
- t.Fatal(ifi, err)
- }
- stats, err := validateInterfaceUnicastAddrs(ifat)
- if err != nil {
- t.Fatal(ifi, err)
- }
- uniStats.ipv4 += stats.ipv4
- uniStats.ipv6 += stats.ipv6
- }
- if err := checkUnicastStats(ifStats, &uniStats); err != nil {
- t.Fatal(err)
- }
- }
- func TestInterfaceMulticastAddrs(t *testing.T) {
- ift, err := Interfaces()
- if err != nil {
- t.Fatal(err)
- }
- ifStats := interfaceStats(ift)
- ifat, err := InterfaceAddrs()
- if err != nil {
- t.Fatal(err)
- }
- uniStats, err := validateInterfaceUnicastAddrs(ifat)
- if err != nil {
- t.Fatal(err)
- }
- var multiStats routeStats
- for _, ifi := range ift {
- ifmat, err := ifi.MulticastAddrs()
- if err != nil {
- t.Fatal(ifi, err)
- }
- stats, err := validateInterfaceMulticastAddrs(ifmat)
- if err != nil {
- t.Fatal(ifi, err)
- }
- multiStats.ipv4 += stats.ipv4
- multiStats.ipv6 += stats.ipv6
- }
- if err := checkMulticastStats(ifStats, uniStats, &multiStats); err != nil {
- t.Fatal(err)
- }
- }
- type ifStats struct {
- loop int // # of active loopback interfaces
- other int // # of active other interfaces
- }
- func interfaceStats(ift []Interface) *ifStats {
- var stats ifStats
- for _, ifi := range ift {
- if ifi.Flags&FlagUp != 0 {
- if ifi.Flags&FlagLoopback != 0 {
- stats.loop++
- } else {
- stats.other++
- }
- }
- }
- return &stats
- }
- type routeStats struct {
- ipv4, ipv6 int // # of active connected unicast, anycast or multicast routes
- }
- func validateInterfaceUnicastAddrs(ifat []Addr) (*routeStats, error) {
- // Note: BSD variants allow assigning any IPv4/IPv6 address
- // prefix to IP interface. For example,
- // - 0.0.0.0/0 through 255.255.255.255/32
- // - ::/0 through ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128
- // In other words, there is no tightly-coupled combination of
- // interface address prefixes and connected routes.
- stats := new(routeStats)
- for _, ifa := range ifat {
- switch ifa := ifa.(type) {
- case *IPNet:
- if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() || ifa.Mask == nil {
- return nil, fmt.Errorf("unexpected value: %#v", ifa)
- }
- if len(ifa.IP) != IPv6len {
- return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa)
- }
- prefixLen, maxPrefixLen := ifa.Mask.Size()
- if ifa.IP.To4() != nil {
- if 0 >= prefixLen || prefixLen > 8*IPv4len || maxPrefixLen != 8*IPv4len {
- return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa)
- }
- if ifa.IP.IsLoopback() && prefixLen < 8 { // see RFC 1122
- return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa)
- }
- stats.ipv4++
- }
- if ifa.IP.To16() != nil && ifa.IP.To4() == nil {
- if 0 >= prefixLen || prefixLen > 8*IPv6len || maxPrefixLen != 8*IPv6len {
- return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa)
- }
- if ifa.IP.IsLoopback() && prefixLen != 8*IPv6len { // see RFC 4291
- return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa)
- }
- stats.ipv6++
- }
- case *IPAddr:
- if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() {
- return nil, fmt.Errorf("unexpected value: %#v", ifa)
- }
- if len(ifa.IP) != IPv6len {
- return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa)
- }
- if ifa.IP.To4() != nil {
- stats.ipv4++
- }
- if ifa.IP.To16() != nil && ifa.IP.To4() == nil {
- stats.ipv6++
- }
- default:
- return nil, fmt.Errorf("unexpected type: %T", ifa)
- }
- }
- return stats, nil
- }
- func validateInterfaceMulticastAddrs(ifat []Addr) (*routeStats, error) {
- stats := new(routeStats)
- for _, ifa := range ifat {
- switch ifa := ifa.(type) {
- case *IPAddr:
- if ifa == nil || ifa.IP == nil || ifa.IP.IsUnspecified() || !ifa.IP.IsMulticast() {
- return nil, fmt.Errorf("unexpected value: %#v", ifa)
- }
- if len(ifa.IP) != IPv6len {
- return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa)
- }
- if ifa.IP.To4() != nil {
- stats.ipv4++
- }
- if ifa.IP.To16() != nil && ifa.IP.To4() == nil {
- stats.ipv6++
- }
- default:
- return nil, fmt.Errorf("unexpected type: %T", ifa)
- }
- }
- return stats, nil
- }
- func checkUnicastStats(ifStats *ifStats, uniStats *routeStats) error {
- // Test the existence of connected unicast routes for IPv4.
- if supportsIPv4() && ifStats.loop+ifStats.other > 0 && uniStats.ipv4 == 0 {
- return fmt.Errorf("num IPv4 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats)
- }
- // Test the existence of connected unicast routes for IPv6.
- // We can assume the existence of ::1/128 when at least one
- // loopback interface is installed.
- if supportsIPv6() && ifStats.loop > 0 && uniStats.ipv6 == 0 {
- return fmt.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats)
- }
- return nil
- }
- func checkMulticastStats(ifStats *ifStats, uniStats, multiStats *routeStats) error {
- switch runtime.GOOS {
- case "aix", "dragonfly", "netbsd", "openbsd", "plan9", "solaris", "illumos":
- default:
- // Test the existence of connected multicast route
- // clones for IPv4. Unlike IPv6, IPv4 multicast
- // capability is not a mandatory feature, and so IPv4
- // multicast validation is ignored and we only check
- // IPv6 below.
- //
- // Test the existence of connected multicast route
- // clones for IPv6. Some platform never uses loopback
- // interface as the nexthop for multicast routing.
- // We can assume the existence of connected multicast
- // route clones when at least two connected unicast
- // routes, ::1/128 and other, are installed.
- if supportsIPv6() && ifStats.loop > 0 && uniStats.ipv6 > 1 && multiStats.ipv6 == 0 {
- return fmt.Errorf("num IPv6 multicast route clones = 0; want >0; summary: %+v, %+v, %+v", ifStats, uniStats, multiStats)
- }
- }
- return nil
- }
- func BenchmarkInterfaces(b *testing.B) {
- testHookUninstaller.Do(uninstallTestHooks)
- for i := 0; i < b.N; i++ {
- if _, err := Interfaces(); err != nil {
- b.Fatal(err)
- }
- }
- }
- func BenchmarkInterfaceByIndex(b *testing.B) {
- testHookUninstaller.Do(uninstallTestHooks)
- ifi := loopbackInterface()
- if ifi == nil {
- b.Skip("loopback interface not found")
- }
- for i := 0; i < b.N; i++ {
- if _, err := InterfaceByIndex(ifi.Index); err != nil {
- b.Fatal(err)
- }
- }
- }
- func BenchmarkInterfaceByName(b *testing.B) {
- testHookUninstaller.Do(uninstallTestHooks)
- ifi := loopbackInterface()
- if ifi == nil {
- b.Skip("loopback interface not found")
- }
- for i := 0; i < b.N; i++ {
- if _, err := InterfaceByName(ifi.Name); err != nil {
- b.Fatal(err)
- }
- }
- }
- func BenchmarkInterfaceAddrs(b *testing.B) {
- testHookUninstaller.Do(uninstallTestHooks)
- for i := 0; i < b.N; i++ {
- if _, err := InterfaceAddrs(); err != nil {
- b.Fatal(err)
- }
- }
- }
- func BenchmarkInterfacesAndAddrs(b *testing.B) {
- testHookUninstaller.Do(uninstallTestHooks)
- ifi := loopbackInterface()
- if ifi == nil {
- b.Skip("loopback interface not found")
- }
- for i := 0; i < b.N; i++ {
- if _, err := ifi.Addrs(); err != nil {
- b.Fatal(err)
- }
- }
- }
- func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) {
- testHookUninstaller.Do(uninstallTestHooks)
- ifi := loopbackInterface()
- if ifi == nil {
- b.Skip("loopback interface not found")
- }
- for i := 0; i < b.N; i++ {
- if _, err := ifi.MulticastAddrs(); err != nil {
- b.Fatal(err)
- }
- }
- }
|