From 322214cf5434a4bd568a994eeced4f11268e330e Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Thu, 28 Feb 2013 14:58:41 +0900 Subject: [PATCH] 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 --- src/pkg/net/interface.go | 16 ++-- src/pkg/net/interface_bsd.go | 56 +++++++---- src/pkg/net/interface_bsd_test.go | 50 ++++++++++ src/pkg/net/interface_darwin.go | 19 ++-- src/pkg/net/interface_freebsd.go | 19 ++-- src/pkg/net/interface_linux.go | 81 ++++++++-------- src/pkg/net/interface_linux_test.go | 48 +++++++++- src/pkg/net/interface_netbsd.go | 9 +- src/pkg/net/interface_openbsd.go | 9 +- src/pkg/net/interface_stub.go | 17 ++-- src/pkg/net/interface_unix_test.go | 141 ++++++++++++++++++++++++++++ src/pkg/net/interface_windows.go | 22 ++--- 12 files changed, 366 insertions(+), 121 deletions(-) create mode 100644 src/pkg/net/interface_bsd_test.go create mode 100644 src/pkg/net/interface_unix_test.go diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go index ee23570a96..0713e9cd6a 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 @@ 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 } diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go index 9e74845762..f58065a85b 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 @@ 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 index 0000000000..c6e1bf731a --- /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 edf4d74dfa..83e483ba20 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 @@ 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()) diff --git a/src/pkg/net/interface_freebsd.go b/src/pkg/net/interface_freebsd.go index af3be4d3c2..1bf5ae72b8 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 @@ 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()) diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go index 5c7590b3c2..e66daef06a 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 @@ 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 diff --git a/src/pkg/net/interface_linux_test.go b/src/pkg/net/interface_linux_test.go index f14d1fe06e..50d3dc6240 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 691d311d8d..c9ce5a7ac1 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 3188871031..c9ce5a7ac1 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 d4d7ce9c7f..a4eb731da4 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 @@ 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 index 0000000000..2040d163e4 --- /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 aa57fab695..0759dc255d 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 @@ 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 } -- 2.48.1