]> Cypherpunks repositories - gostls13.git/commitdiff
net: fix slow network interface manipulations
authorMikio Hara <mikioh.mikioh@gmail.com>
Thu, 28 Feb 2013 05:58:41 +0000 (14:58 +0900)
committerMikio Hara <mikioh.mikioh@gmail.com>
Thu, 28 Feb 2013 05:58:41 +0000 (14:58 +0900)
This CL reduces unnecessary network facility lookups introduced
by recent changes below.

changeset: 15798:53a4da6a4f4a
net: return correct point-to-point interface address on linux

changeset: 15799:a81ef8e0cc05
net: set up IPv6 scoped addressing zone for network facilities

Also adds a test case for issue 4839.

Benchmark results on linux/amd64, virtual machine:
benchmark                                 old ns/op    new ns/op    delta
BenchmarkInterfaces-2                         80487        80382   -0.13%
BenchmarkInterfaceByIndex-2                   72013        71391   -0.86%
BenchmarkInterfaceByName-2                    79865        80101   +0.30%
BenchmarkInterfaceAddrs-2                     42071       829677  +1872.09%
BenchmarkInterfacesAndAddrs-2                 35016       607622  +1635.27%
BenchmarkInterfacesAndMulticastAddrs-2       169849       169082   -0.45%
old: 15797:9c3930413c1b, new: tip

Benchmark results on linux/amd64, virtual machine:
benchmark                                 old ns/op    new ns/op    delta
BenchmarkInterfaces-2                         80487        81459   +1.21%
BenchmarkInterfaceByIndex-2                   72013        71512   -0.70%
BenchmarkInterfaceByName-2                    79865        80567   +0.88%
BenchmarkInterfaceAddrs-2                     42071       120108  +185.49%
BenchmarkInterfacesAndAddrs-2                 35016        33259   -5.02%
BenchmarkInterfacesAndMulticastAddrs-2       169849        82391  -51.49%
old: 15797:9c3930413c1b, new: tip+CL7400055

Benchmark results on darwin/amd64:
benchmark                                 old ns/op    new ns/op    delta
BenchmarkInterfaces-2                         34402        34231   -0.50%
BenchmarkInterfaceByIndex-2                   13192        12956   -1.79%
BenchmarkInterfaceByName-2                    34791        34388   -1.16%
BenchmarkInterfaceAddrs-2                     36565        63906  +74.77%
BenchmarkInterfacesAndAddrs-2                 17497        31068  +77.56%
BenchmarkInterfacesAndMulticastAddrs-2        25276        66711  +163.93%
old: 15797:9c3930413c1b, new: tip

Benchmark results on darwin/amd64:
benchmark                                 old ns/op    new ns/op    delta
BenchmarkInterfaces-2                         34402        31854   -7.41%
BenchmarkInterfaceByIndex-2                   13192        12950   -1.83%
BenchmarkInterfaceByName-2                    34791        31926   -8.23%
BenchmarkInterfaceAddrs-2                     36565        42144  +15.26%
BenchmarkInterfacesAndAddrs-2                 17497        17329   -0.96%
BenchmarkInterfacesAndMulticastAddrs-2        25276        24870   -1.61%
old: 15797:9c3930413c1b, new: tip+CL7400055

Update #4234.
Fixes #4839 (again).
Fixes #4866.

R=golang-dev, fullung
CC=golang-dev
https://golang.org/cl/7400055

12 files changed:
src/pkg/net/interface.go
src/pkg/net/interface_bsd.go
src/pkg/net/interface_bsd_test.go [new file with mode: 0644]
src/pkg/net/interface_darwin.go
src/pkg/net/interface_freebsd.go
src/pkg/net/interface_linux.go
src/pkg/net/interface_linux_test.go
src/pkg/net/interface_netbsd.go
src/pkg/net/interface_openbsd.go
src/pkg/net/interface_stub.go
src/pkg/net/interface_unix_test.go [new file with mode: 0644]
src/pkg/net/interface_windows.go

index ee23570a96da6ddd445960a4a44198fd0cc2c76a..0713e9cd6a973b7b1082b83a5f19958040027e07 100644 (file)
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Network interface identification
-
 package net
 
 import "errors"
@@ -66,7 +64,7 @@ func (ifi *Interface) Addrs() ([]Addr, error) {
        if ifi == nil {
                return nil, errInvalidInterface
        }
-       return interfaceAddrTable(ifi.Index)
+       return interfaceAddrTable(ifi)
 }
 
 // MulticastAddrs returns multicast, joined group addresses for
@@ -75,7 +73,7 @@ func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
        if ifi == nil {
                return nil, errInvalidInterface
        }
-       return interfaceMulticastAddrTable(ifi.Index)
+       return interfaceMulticastAddrTable(ifi)
 }
 
 // Interfaces returns a list of the system's network interfaces.
@@ -86,7 +84,7 @@ func Interfaces() ([]Interface, error) {
 // InterfaceAddrs returns a list of the system's network interface
 // addresses.
 func InterfaceAddrs() ([]Addr, error) {
-       return interfaceAddrTable(0)
+       return interfaceAddrTable(nil)
 }
 
 // InterfaceByIndex returns the interface specified by index.
@@ -98,8 +96,14 @@ func InterfaceByIndex(index int) (*Interface, error) {
        if err != nil {
                return nil, err
        }
+       return interfaceByIndex(ift, index)
+}
+
+func interfaceByIndex(ift []Interface, index int) (*Interface, error) {
        for _, ifi := range ift {
-               return &ifi, nil
+               if index == ifi.Index {
+                       return &ifi, nil
+               }
        }
        return nil, errNoSuchInterface
 }
index 9e74845762c9e00d06ca3fca006306c29e27b091..f58065a85b556448861b0684ff630dc5a50b30f5 100644 (file)
@@ -4,8 +4,6 @@
 
 // +build darwin freebsd netbsd openbsd
 
-// Network interface identification for BSD variants
-
 package net
 
 import (
@@ -26,7 +24,12 @@ func interfaceTable(ifindex int) ([]Interface, error) {
        if err != nil {
                return nil, os.NewSyscallError("route message", err)
        }
+       return parseInterfaceTable(ifindex, msgs)
+}
+
+func parseInterfaceTable(ifindex int, msgs []syscall.RoutingMessage) ([]Interface, error) {
        var ift []Interface
+loop:
        for _, m := range msgs {
                switch m := m.(type) {
                case *syscall.InterfaceMessage:
@@ -35,26 +38,28 @@ func interfaceTable(ifindex int) ([]Interface, error) {
                                if err != nil {
                                        return nil, err
                                }
-                               ift = append(ift, ifi...)
+                               ift = append(ift, *ifi)
+                               if ifindex == int(m.Header.Index) {
+                                       break loop
+                               }
                        }
                }
        }
        return ift, nil
 }
 
-func newLink(m *syscall.InterfaceMessage) ([]Interface, error) {
+func newLink(m *syscall.InterfaceMessage) (*Interface, error) {
        sas, err := syscall.ParseRoutingSockaddr(m)
        if err != nil {
                return nil, os.NewSyscallError("route sockaddr", err)
        }
-       var ift []Interface
+       ifi := &Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)}
        for _, sa := range sas {
                switch sa := sa.(type) {
                case *syscall.SockaddrDatalink:
                        // NOTE: SockaddrDatalink.Data is minimum work area,
                        // can be larger.
                        m.Data = m.Data[unsafe.Offsetof(sa.Data):]
-                       ifi := Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)}
                        var name [syscall.IFNAMSIZ]byte
                        for i := 0; i < int(sa.Nlen); i++ {
                                name[i] = byte(m.Data[i])
@@ -66,10 +71,9 @@ func newLink(m *syscall.InterfaceMessage) ([]Interface, error) {
                                addr[i] = byte(m.Data[int(sa.Nlen)+i])
                        }
                        ifi.HardwareAddr = addr[:sa.Alen]
-                       ift = append(ift, ifi)
                }
        }
-       return ift, nil
+       return ifi, nil
 }
 
 func linkFlags(rawFlags int32) Flags {
@@ -92,11 +96,15 @@ func linkFlags(rawFlags int32) Flags {
        return f
 }
 
-// If the ifindex is zero, interfaceAddrTable returns addresses
-// for all network interfaces.  Otherwise it returns addresses
-// for a specific interface.
-func interfaceAddrTable(ifindex int) ([]Addr, error) {
-       tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces.  Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
+       index := 0
+       if ifi != nil {
+               index = ifi.Index
+       }
+       tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, index)
        if err != nil {
                return nil, os.NewSyscallError("route rib", err)
        }
@@ -104,12 +112,26 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) {
        if err != nil {
                return nil, os.NewSyscallError("route message", err)
        }
+       var ift []Interface
+       if index == 0 {
+               ift, err = parseInterfaceTable(index, msgs)
+               if err != nil {
+                       return nil, err
+               }
+       }
        var ifat []Addr
        for _, m := range msgs {
                switch m := m.(type) {
                case *syscall.InterfaceAddrMessage:
-                       if ifindex == 0 || ifindex == int(m.Header.Index) {
-                               ifa, err := newAddr(m)
+                       if index == 0 || index == int(m.Header.Index) {
+                               if index == 0 {
+                                       var err error
+                                       ifi, err = interfaceByIndex(ift, int(m.Header.Index))
+                                       if err != nil {
+                                               return nil, err
+                                       }
+                               }
+                               ifa, err := newAddr(ifi, m)
                                if err != nil {
                                        return nil, err
                                }
@@ -122,7 +144,7 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) {
        return ifat, nil
 }
 
-func newAddr(m *syscall.InterfaceAddrMessage) (Addr, error) {
+func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (Addr, error) {
        sas, err := syscall.ParseRoutingSockaddr(m)
        if err != nil {
                return nil, os.NewSyscallError("route sockaddr", err)
@@ -149,7 +171,7 @@ func newAddr(m *syscall.InterfaceAddrMessage) (Addr, error) {
                                // the interface index in the interface-local or link-
                                // local address as the kernel-internal form.
                                if ifa.IP.IsLinkLocalUnicast() {
-                                       ifa.Zone = zoneToString(int(ifa.IP[2]<<8 | ifa.IP[3]))
+                                       ifa.Zone = ifi.Name
                                        ifa.IP[2], ifa.IP[3] = 0, 0
                                }
                        }
diff --git a/src/pkg/net/interface_bsd_test.go b/src/pkg/net/interface_bsd_test.go
new file mode 100644 (file)
index 0000000..c6e1bf7
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright 2013 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.
+
+// +build darwin freebsd netbsd openbsd
+
+package net
+
+import (
+       "fmt"
+       "os/exec"
+)
+
+func (ti *testInterface) setBroadcast(suffix int) {
+       ti.name = fmt.Sprintf("vlan%d", suffix)
+       xname, err := exec.LookPath("ifconfig")
+       if err != nil {
+               xname = "ifconfig"
+       }
+       ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ifconfig", ti.name, "create"},
+       })
+       ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ifconfig", ti.name, "destroy"},
+       })
+}
+
+func (ti *testInterface) setPointToPoint(suffix int, local, remote string) {
+       ti.name = fmt.Sprintf("gif%d", suffix)
+       ti.local = local
+       ti.remote = remote
+       xname, err := exec.LookPath("ifconfig")
+       if err != nil {
+               xname = "ifconfig"
+       }
+       ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ifconfig", ti.name, "create"},
+       })
+       ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ifconfig", ti.name, "inet", ti.local, ti.remote},
+       })
+       ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ifconfig", ti.name, "destroy"},
+       })
+}
index edf4d74dfaf29de760b72355171b3961512ec28a..83e483ba20c64a8e6292b136d3e4b99f07678900 100644 (file)
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Network interface identification for Darwin
-
 package net
 
 import (
@@ -11,11 +9,10 @@ import (
        "syscall"
 )
 
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces.  Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
-       tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifindex)
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+       tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifi.Index)
        if err != nil {
                return nil, os.NewSyscallError("route rib", err)
        }
@@ -27,8 +24,8 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
        for _, m := range msgs {
                switch m := m.(type) {
                case *syscall.InterfaceMulticastAddrMessage:
-                       if ifindex == 0 || ifindex == int(m.Header.Index) {
-                               ifma, err := newMulticastAddr(m)
+                       if ifi.Index == int(m.Header.Index) {
+                               ifma, err := newMulticastAddr(ifi, m)
                                if err != nil {
                                        return nil, err
                                }
@@ -39,7 +36,7 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
        return ifmat, nil
 }
 
-func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) {
+func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) {
        sas, err := syscall.ParseRoutingSockaddr(m)
        if err != nil {
                return nil, os.NewSyscallError("route sockaddr", err)
@@ -57,7 +54,7 @@ func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error)
                        // the interface index in the interface-local or link-
                        // local address as the kernel-internal form.
                        if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() {
-                               ifma.Zone = zoneToString(int(ifma.IP[2]<<8 | ifma.IP[3]))
+                               ifma.Zone = ifi.Name
                                ifma.IP[2], ifma.IP[3] = 0, 0
                        }
                        ifmat = append(ifmat, ifma.toAddr())
index af3be4d3c2892f22d77ac6f5d11daec399180fe4..1bf5ae72b83911164feb58615aa2e5c63b4825e5 100644 (file)
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Network interface identification for FreeBSD
-
 package net
 
 import (
@@ -11,11 +9,10 @@ import (
        "syscall"
 )
 
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces.  Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
-       tab, err := syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifindex)
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+       tab, err := syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifi.Index)
        if err != nil {
                return nil, os.NewSyscallError("route rib", err)
        }
@@ -27,8 +24,8 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
        for _, m := range msgs {
                switch m := m.(type) {
                case *syscall.InterfaceMulticastAddrMessage:
-                       if ifindex == 0 || ifindex == int(m.Header.Index) {
-                               ifma, err := newMulticastAddr(m)
+                       if ifi.Index == int(m.Header.Index) {
+                               ifma, err := newMulticastAddr(ifi, m)
                                if err != nil {
                                        return nil, err
                                }
@@ -39,7 +36,7 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
        return ifmat, nil
 }
 
-func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) {
+func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) {
        sas, err := syscall.ParseRoutingSockaddr(m)
        if err != nil {
                return nil, os.NewSyscallError("route sockaddr", err)
@@ -57,7 +54,7 @@ func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error)
                        // the interface index in the interface-local or link-
                        // local address as the kernel-internal form.
                        if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() {
-                               ifma.Zone = zoneToString(int(ifma.IP[2]<<8 | ifma.IP[3]))
+                               ifma.Zone = ifi.Name
                                ifma.IP[2], ifma.IP[3] = 0, 0
                        }
                        ifmat = append(ifmat, ifma.toAddr())
index 5c7590b3c2a94b810a0514e415284fc59e63ddb7..e66daef06a068b07f1aaa91a04662fcf4b4c4c2a 100644 (file)
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Network interface identification for Linux
-
 package net
 
 import (
@@ -37,16 +35,18 @@ loop:
                                if err != nil {
                                        return nil, os.NewSyscallError("netlink routeattr", err)
                                }
-                               ifi := newLink(ifim, attrs)
-                               ift = append(ift, ifi)
+                               ift = append(ift, *newLink(ifim, attrs))
+                               if ifindex == int(ifim.Index) {
+                                       break loop
+                               }
                        }
                }
        }
        return ift, nil
 }
 
-func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) Interface {
-       ifi := Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)}
+func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) *Interface {
+       ifi := &Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)}
        for _, a := range attrs {
                switch a.Attr.Type {
                case syscall.IFLA_ADDRESS:
@@ -88,10 +88,10 @@ func linkFlags(rawFlags uint32) Flags {
        return f
 }
 
-// If the ifindex is zero, interfaceAddrTable returns addresses
-// for all network interfaces.  Otherwise it returns addresses
-// for a specific interface.
-func interfaceAddrTable(ifindex int) ([]Addr, error) {
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces.  Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
        tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
        if err != nil {
                return nil, os.NewSyscallError("netlink rib", err)
@@ -100,14 +100,22 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) {
        if err != nil {
                return nil, os.NewSyscallError("netlink message", err)
        }
-       ifat, err := addrTable(msgs, ifindex)
+       var ift []Interface
+       if ifi == nil {
+               var err error
+               ift, err = interfaceTable(0)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       ifat, err := addrTable(ift, ifi, msgs)
        if err != nil {
                return nil, err
        }
        return ifat, nil
 }
 
-func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, error) {
+func addrTable(ift []Interface, ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) {
        var ifat []Addr
 loop:
        for _, m := range msgs {
@@ -116,58 +124,51 @@ loop:
                        break loop
                case syscall.RTM_NEWADDR:
                        ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
-                       ifi, err := InterfaceByIndex(int(ifam.Index))
-                       if err != nil {
-                               return nil, err
-                       }
-                       if ifindex == 0 || ifindex == int(ifam.Index) {
+                       if len(ift) != 0 || ifi.Index == int(ifam.Index) {
+                               if len(ift) != 0 {
+                                       var err error
+                                       ifi, err = interfaceByIndex(ift, int(ifam.Index))
+                                       if err != nil {
+                                               return nil, err
+                                       }
+                               }
                                attrs, err := syscall.ParseNetlinkRouteAttr(&m)
                                if err != nil {
                                        return nil, os.NewSyscallError("netlink routeattr", err)
                                }
-                               ifat = append(ifat, newAddr(attrs, ifi, ifam))
+                               ifa := newAddr(ifi, ifam, attrs)
+                               if ifa != nil {
+                                       ifat = append(ifat, ifa)
+                               }
                        }
                }
        }
        return ifat, nil
 }
 
-func newAddr(attrs []syscall.NetlinkRouteAttr, ifi *Interface, ifam *syscall.IfAddrmsg) Addr {
-       ifa := &IPNet{}
+func newAddr(ifi *Interface, ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) Addr {
        for _, a := range attrs {
                if ifi.Flags&FlagPointToPoint != 0 && a.Attr.Type == syscall.IFA_LOCAL ||
                        ifi.Flags&FlagPointToPoint == 0 && a.Attr.Type == syscall.IFA_ADDRESS {
                        switch ifam.Family {
                        case syscall.AF_INET:
-                               ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3])
-                               ifa.Mask = CIDRMask(int(ifam.Prefixlen), 8*IPv4len)
+                               return &IPNet{IP: IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv4len)}
                        case syscall.AF_INET6:
-                               ifa.IP = make(IP, IPv6len)
+                               ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)}
                                copy(ifa.IP, a.Value[:])
-                               ifa.Mask = CIDRMask(int(ifam.Prefixlen), 8*IPv6len)
                                if ifam.Scope == syscall.RT_SCOPE_HOST || ifam.Scope == syscall.RT_SCOPE_LINK {
-                                       ifa.Zone = zoneToString(int(ifam.Index))
+                                       ifa.Zone = ifi.Name
                                }
+                               return ifa
                        }
                }
        }
-       return ifa
+       return nil
 }
 
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces.  Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
-       var (
-               err error
-               ifi *Interface
-       )
-       if ifindex > 0 {
-               ifi, err = InterfaceByIndex(ifindex)
-               if err != nil {
-                       return nil, err
-               }
-       }
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
        ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
        ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
        return append(ifmat4, ifmat6...), nil
index f14d1fe06e9cf93505fdedfd1baf27c4ecaa607a..50d3dc6240673befa59cef07f055a14c6bbd7d40 100644 (file)
@@ -4,7 +4,53 @@
 
 package net
 
-import "testing"
+import (
+       "fmt"
+       "os/exec"
+       "testing"
+)
+
+func (ti *testInterface) setBroadcast(suffix int) {
+       ti.name = fmt.Sprintf("gotest%d", suffix)
+       xname, err := exec.LookPath("ip")
+       if err != nil {
+               xname = "ip"
+       }
+       ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ip", "link", "add", ti.name, "type", "dummy"},
+       })
+       ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ip", "link", "delete", ti.name, "type", "dummy"},
+       })
+}
+
+func (ti *testInterface) setPointToPoint(suffix int, local, remote string) {
+       ti.name = fmt.Sprintf("gotest%d", suffix)
+       ti.local = local
+       ti.remote = remote
+       xname, err := exec.LookPath("ip")
+       if err != nil {
+               xname = "ip"
+       }
+       ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ip", "tunnel", "add", ti.name, "mode", "gre", "local", local, "remote", remote},
+       })
+       ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ip", "tunnel", "del", ti.name, "mode", "gre", "local", local, "remote", remote},
+       })
+       xname, err = exec.LookPath("ifconfig")
+       if err != nil {
+               xname = "ifconfig"
+       }
+       ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+               Path: xname,
+               Args: []string{"ifconfig", ti.name, "inet", local, "dstaddr", remote},
+       })
+}
 
 const (
        numOfTestIPv4MCAddrs = 14
index 691d311d8d460c076bd4261313d669f3a2d9bd25..c9ce5a7ac15a4f1ee30092974957a111b8e9f6ef 100644 (file)
@@ -2,14 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Network interface identification for NetBSD
-
 package net
 
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces.  Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
        // TODO(mikio): Implement this like other platforms.
        return nil, nil
 }
index 3188871031bd8ad46a4f180fc52a4626c722661d..c9ce5a7ac15a4f1ee30092974957a111b8e9f6ef 100644 (file)
@@ -2,14 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Network interface identification for OpenBSD
-
 package net
 
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces.  Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
        // TODO(mikio): Implement this like other platforms.
        return nil, nil
 }
index d4d7ce9c7f88e1d2f374cd72c7f31f1dbe632bf2..a4eb731da441ed606cd9cab8ec34ca8b8a9c83ff 100644 (file)
@@ -4,8 +4,6 @@
 
 // +build plan9
 
-// Network interface identification
-
 package net
 
 // If the ifindex is zero, interfaceTable returns mappings of all
@@ -15,16 +13,15 @@ func interfaceTable(ifindex int) ([]Interface, error) {
        return nil, nil
 }
 
-// If the ifindex is zero, interfaceAddrTable returns addresses
-// for all network interfaces.  Otherwise it returns addresses
-// for a specific interface.
-func interfaceAddrTable(ifindex int) ([]Addr, error) {
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces.  Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
        return nil, nil
 }
 
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces.  Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
        return nil, nil
 }
diff --git a/src/pkg/net/interface_unix_test.go b/src/pkg/net/interface_unix_test.go
new file mode 100644 (file)
index 0000000..2040d16
--- /dev/null
@@ -0,0 +1,141 @@
+// Copyright 2013 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.
+
+// +build darwin freebsd linux netbsd openbsd
+
+package net
+
+import (
+       "os"
+       "os/exec"
+       "runtime"
+       "testing"
+       "time"
+)
+
+type testInterface struct {
+       name         string
+       local        string
+       remote       string
+       setupCmds    []*exec.Cmd
+       teardownCmds []*exec.Cmd
+}
+
+func (ti *testInterface) setup() error {
+       for _, cmd := range ti.setupCmds {
+               if err := cmd.Run(); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+func (ti *testInterface) teardown() error {
+       for _, cmd := range ti.teardownCmds {
+               if err := cmd.Run(); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+func TestPointToPointInterface(t *testing.T) {
+       switch runtime.GOOS {
+       case "darwin":
+               t.Skipf("skipping read test on %q", runtime.GOOS)
+       }
+       if os.Getuid() != 0 {
+               t.Skip("skipping test; must be root")
+       }
+
+       local, remote := "169.254.0.1", "169.254.0.254"
+       ip := ParseIP(remote)
+       for i := 0; i < 3; i++ {
+               ti := &testInterface{}
+               ti.setPointToPoint(5963+i, local, remote)
+               if err := ti.setup(); err != nil {
+                       t.Fatalf("testInterface.setup failed: %v", err)
+               } else {
+                       time.Sleep(3 * time.Millisecond)
+               }
+               ift, err := Interfaces()
+               if err != nil {
+                       ti.teardown()
+                       t.Fatalf("Interfaces failed: %v", err)
+               }
+               for _, ifi := range ift {
+                       if ti.name == ifi.Name {
+                               ifat, err := ifi.Addrs()
+                               if err != nil {
+                                       ti.teardown()
+                                       t.Fatalf("Interface.Addrs failed: %v", err)
+                               }
+                               for _, ifa := range ifat {
+                                       if ip.Equal(ifa.(*IPNet).IP) {
+                                               ti.teardown()
+                                               t.Fatalf("got %v; want %v", ip, local)
+                                       }
+                               }
+                       }
+               }
+               if err := ti.teardown(); err != nil {
+                       t.Fatalf("testInterface.teardown failed: %v", err)
+               } else {
+                       time.Sleep(3 * time.Millisecond)
+               }
+       }
+}
+
+func TestInterfaceArrivalAndDeparture(t *testing.T) {
+       if os.Getuid() != 0 {
+               t.Skip("skipping test; must be root")
+       }
+
+       for i := 0; i < 3; i++ {
+               ift1, err := Interfaces()
+               if err != nil {
+                       t.Fatalf("Interfaces failed: %v", err)
+               }
+               ti := &testInterface{}
+               ti.setBroadcast(5682 + i)
+               if err := ti.setup(); err != nil {
+                       t.Fatalf("testInterface.setup failed: %v", err)
+               } else {
+                       time.Sleep(3 * time.Millisecond)
+               }
+               ift2, err := Interfaces()
+               if err != nil {
+                       ti.teardown()
+                       t.Fatalf("Interfaces failed: %v", err)
+               }
+               if len(ift2) <= len(ift1) {
+                       for _, ifi := range ift1 {
+                               t.Logf("before: %v", ifi)
+                       }
+                       for _, ifi := range ift2 {
+                               t.Logf("after: %v", ifi)
+                       }
+                       ti.teardown()
+                       t.Fatalf("got %v; want gt %v", len(ift2), len(ift1))
+               }
+               if err := ti.teardown(); err != nil {
+                       t.Fatalf("testInterface.teardown failed: %v", err)
+               } else {
+                       time.Sleep(3 * time.Millisecond)
+               }
+               ift3, err := Interfaces()
+               if err != nil {
+                       t.Fatalf("Interfaces failed: %v", err)
+               }
+               if len(ift3) >= len(ift2) {
+                       for _, ifi := range ift2 {
+                               t.Logf("before: %v", ifi)
+                       }
+                       for _, ifi := range ift3 {
+                               t.Logf("after: %v", ifi)
+                       }
+                       t.Fatalf("got %v; want lt %v", len(ift3), len(ift2))
+               }
+       }
+}
index aa57fab695822e8f90682a8223da34ce8213bd76..0759dc255d4346f2fdb0d6565b2d2327a12288dd 100644 (file)
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Network interface identification for Windows
-
 package net
 
 import (
@@ -129,10 +127,10 @@ func interfaceTable(ifindex int) ([]Interface, error) {
        return ift, nil
 }
 
-// If the ifindex is zero, interfaceAddrTable returns addresses
-// for all network interfaces.  Otherwise it returns addresses
-// for a specific interface.
-func interfaceAddrTable(ifindex int) ([]Addr, error) {
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces.  Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
        ai, err := getAdapterList()
        if err != nil {
                return nil, err
@@ -141,11 +139,10 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) {
        var ifat []Addr
        for ; ai != nil; ai = ai.Next {
                index := ai.Index
-               if ifindex == 0 || ifindex == int(index) {
+               if ifi == nil || ifi.Index == int(index) {
                        ipl := &ai.IpAddressList
                        for ; ipl != nil; ipl = ipl.Next {
-                               ifa := IPAddr{}
-                               ifa.IP = parseIPv4(bytePtrToString(&ipl.IpAddress.String[0]))
+                               ifa := IPAddr{IP: parseIPv4(bytePtrToString(&ipl.IpAddress.String[0]))}
                                ifat = append(ifat, ifa.toAddr())
                        }
                }
@@ -153,10 +150,9 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) {
        return ifat, nil
 }
 
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces.  Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
        // TODO(mikio): Implement this like other platforms.
        return nil, nil
 }