net: fix slow network interface manipulations
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
diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go
index ee23570..0713e9c 100644
--- a/src/pkg/net/interface.go
+++ b/src/pkg/net/interface.go
@@ -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 @@
if ifi == nil {
return nil, errInvalidInterface
}
- return interfaceAddrTable(ifi.Index)
+ return interfaceAddrTable(ifi)
}
// MulticastAddrs returns multicast, joined group addresses for
@@ -75,7 +73,7 @@
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 @@
// 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 @@
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
}
diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go
index 9e74845..f58065a 100644
--- a/src/pkg/net/interface_bsd.go
+++ b/src/pkg/net/interface_bsd.go
@@ -4,8 +4,6 @@
// +build darwin freebsd netbsd openbsd
-// Network interface identification for BSD variants
-
package net
import (
@@ -26,7 +24,12 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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
index 0000000..c6e1bf7
--- /dev/null
+++ b/src/pkg/net/interface_bsd_test.go
@@ -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"},
+ })
+}
diff --git a/src/pkg/net/interface_darwin.go b/src/pkg/net/interface_darwin.go
index edf4d74..83e483b 100644
--- a/src/pkg/net/interface_darwin.go
+++ b/src/pkg/net/interface_darwin.go
@@ -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 @@
"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 @@
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 @@
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 @@
// 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())
diff --git a/src/pkg/net/interface_freebsd.go b/src/pkg/net/interface_freebsd.go
index af3be4d..1bf5ae7 100644
--- a/src/pkg/net/interface_freebsd.go
+++ b/src/pkg/net/interface_freebsd.go
@@ -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 @@
"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 @@
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 @@
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 @@
// 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())
diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go
index 5c7590b..e66daef 100644
--- a/src/pkg/net/interface_linux.go
+++ b/src/pkg/net/interface_linux.go
@@ -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 @@
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 @@
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 @@
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 @@
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
diff --git a/src/pkg/net/interface_linux_test.go b/src/pkg/net/interface_linux_test.go
index f14d1fe..50d3dc6 100644
--- a/src/pkg/net/interface_linux_test.go
+++ b/src/pkg/net/interface_linux_test.go
@@ -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
diff --git a/src/pkg/net/interface_netbsd.go b/src/pkg/net/interface_netbsd.go
index 691d311..c9ce5a7 100644
--- a/src/pkg/net/interface_netbsd.go
+++ b/src/pkg/net/interface_netbsd.go
@@ -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
}
diff --git a/src/pkg/net/interface_openbsd.go b/src/pkg/net/interface_openbsd.go
index 3188871..c9ce5a7 100644
--- a/src/pkg/net/interface_openbsd.go
+++ b/src/pkg/net/interface_openbsd.go
@@ -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
}
diff --git a/src/pkg/net/interface_stub.go b/src/pkg/net/interface_stub.go
index d4d7ce9..a4eb731 100644
--- a/src/pkg/net/interface_stub.go
+++ b/src/pkg/net/interface_stub.go
@@ -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 @@
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
index 0000000..2040d16
--- /dev/null
+++ b/src/pkg/net/interface_unix_test.go
@@ -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))
+ }
+ }
+}
diff --git a/src/pkg/net/interface_windows.go b/src/pkg/net/interface_windows.go
index aa57fab..0759dc2 100644
--- a/src/pkg/net/interface_windows.go
+++ b/src/pkg/net/interface_windows.go
@@ -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 @@
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 @@
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 @@
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
}