diff options
Diffstat (limited to 'src/net/interface.go')
-rw-r--r-- | src/net/interface.go | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/src/net/interface.go b/src/net/interface.go new file mode 100644 index 0000000..0e5d320 --- /dev/null +++ b/src/net/interface.go @@ -0,0 +1,257 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "errors" + "internal/itoa" + "sync" + "time" +) + +// BUG(mikio): On JS, methods and functions related to +// Interface are not implemented. + +// BUG(mikio): On AIX, DragonFly BSD, NetBSD, OpenBSD, Plan 9 and +// Solaris, the MulticastAddrs method of Interface is not implemented. + +var ( + errInvalidInterface = errors.New("invalid network interface") + errInvalidInterfaceIndex = errors.New("invalid network interface index") + errInvalidInterfaceName = errors.New("invalid network interface name") + errNoSuchInterface = errors.New("no such network interface") + errNoSuchMulticastInterface = errors.New("no such multicast network interface") +) + +// Interface represents a mapping between network interface name +// and index. It also represents network interface facility +// information. +type Interface struct { + Index int // positive integer that starts at one, zero is never used + MTU int // maximum transmission unit + Name string // e.g., "en0", "lo0", "eth0.100" + HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form + Flags Flags // e.g., FlagUp, FlagLoopback, FlagMulticast +} + +type Flags uint + +const ( + FlagUp Flags = 1 << iota // interface is up + FlagBroadcast // interface supports broadcast access capability + FlagLoopback // interface is a loopback interface + FlagPointToPoint // interface belongs to a point-to-point link + FlagMulticast // interface supports multicast access capability +) + +var flagNames = []string{ + "up", + "broadcast", + "loopback", + "pointtopoint", + "multicast", +} + +func (f Flags) String() string { + s := "" + for i, name := range flagNames { + if f&(1<<uint(i)) != 0 { + if s != "" { + s += "|" + } + s += name + } + } + if s == "" { + s = "0" + } + return s +} + +// Addrs returns a list of unicast interface addresses for a specific +// interface. +func (ifi *Interface) Addrs() ([]Addr, error) { + if ifi == nil { + return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface} + } + ifat, err := interfaceAddrTable(ifi) + if err != nil { + err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} + } + return ifat, err +} + +// MulticastAddrs returns a list of multicast, joined group addresses +// for a specific interface. +func (ifi *Interface) MulticastAddrs() ([]Addr, error) { + if ifi == nil { + return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface} + } + ifat, err := interfaceMulticastAddrTable(ifi) + if err != nil { + err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} + } + return ifat, err +} + +// Interfaces returns a list of the system's network interfaces. +func Interfaces() ([]Interface, error) { + ift, err := interfaceTable(0) + if err != nil { + return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} + } + if len(ift) != 0 { + zoneCache.update(ift, false) + } + return ift, nil +} + +// InterfaceAddrs returns a list of the system's unicast interface +// addresses. +// +// The returned list does not identify the associated interface; use +// Interfaces and Interface.Addrs for more detail. +func InterfaceAddrs() ([]Addr, error) { + ifat, err := interfaceAddrTable(nil) + if err != nil { + err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} + } + return ifat, err +} + +// InterfaceByIndex returns the interface specified by index. +// +// On Solaris, it returns one of the logical network interfaces +// sharing the logical data link; for more precision use +// InterfaceByName. +func InterfaceByIndex(index int) (*Interface, error) { + if index <= 0 { + return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceIndex} + } + ift, err := interfaceTable(index) + if err != nil { + return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} + } + ifi, err := interfaceByIndex(ift, index) + if err != nil { + err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} + } + return ifi, err +} + +func interfaceByIndex(ift []Interface, index int) (*Interface, error) { + for _, ifi := range ift { + if index == ifi.Index { + return &ifi, nil + } + } + return nil, errNoSuchInterface +} + +// InterfaceByName returns the interface specified by name. +func InterfaceByName(name string) (*Interface, error) { + if name == "" { + return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceName} + } + ift, err := interfaceTable(0) + if err != nil { + return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} + } + if len(ift) != 0 { + zoneCache.update(ift, false) + } + for _, ifi := range ift { + if name == ifi.Name { + return &ifi, nil + } + } + return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errNoSuchInterface} +} + +// An ipv6ZoneCache represents a cache holding partial network +// interface information. It is used for reducing the cost of IPv6 +// addressing scope zone resolution. +// +// Multiple names sharing the index are managed by first-come +// first-served basis for consistency. +type ipv6ZoneCache struct { + sync.RWMutex // guard the following + lastFetched time.Time // last time routing information was fetched + toIndex map[string]int // interface name to its index + toName map[int]string // interface index to its name +} + +var zoneCache = ipv6ZoneCache{ + toIndex: make(map[string]int), + toName: make(map[int]string), +} + +// update refreshes the network interface information if the cache was last +// updated more than 1 minute ago, or if force is set. It reports whether the +// cache was updated. +func (zc *ipv6ZoneCache) update(ift []Interface, force bool) (updated bool) { + zc.Lock() + defer zc.Unlock() + now := time.Now() + if !force && zc.lastFetched.After(now.Add(-60*time.Second)) { + return false + } + zc.lastFetched = now + if len(ift) == 0 { + var err error + if ift, err = interfaceTable(0); err != nil { + return false + } + } + zc.toIndex = make(map[string]int, len(ift)) + zc.toName = make(map[int]string, len(ift)) + for _, ifi := range ift { + zc.toIndex[ifi.Name] = ifi.Index + if _, ok := zc.toName[ifi.Index]; !ok { + zc.toName[ifi.Index] = ifi.Name + } + } + return true +} + +func (zc *ipv6ZoneCache) name(index int) string { + if index == 0 { + return "" + } + updated := zoneCache.update(nil, false) + zoneCache.RLock() + name, ok := zoneCache.toName[index] + zoneCache.RUnlock() + if !ok && !updated { + zoneCache.update(nil, true) + zoneCache.RLock() + name, ok = zoneCache.toName[index] + zoneCache.RUnlock() + } + if !ok { // last resort + name = itoa.Uitoa(uint(index)) + } + return name +} + +func (zc *ipv6ZoneCache) index(name string) int { + if name == "" { + return 0 + } + updated := zoneCache.update(nil, false) + zoneCache.RLock() + index, ok := zoneCache.toIndex[name] + zoneCache.RUnlock() + if !ok && !updated { + zoneCache.update(nil, true) + zoneCache.RLock() + index, ok = zoneCache.toIndex[name] + zoneCache.RUnlock() + } + if !ok { // last resort + index, _, _ = dtoi(name) + } + return index +} |