Home | History | Annotate | Download | only in net
      1 // Copyright 2011 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package net
      6 
      7 import (
      8 	"errors"
      9 	"sync"
     10 	"time"
     11 )
     12 
     13 // BUG(mikio): On NaCl, methods and functions related to
     14 // Interface are not implemented.
     15 
     16 // BUG(mikio): On DragonFly BSD, NetBSD, OpenBSD, Plan 9 and Solaris,
     17 // the MulticastAddrs method of Interface is not implemented.
     18 
     19 var (
     20 	errInvalidInterface         = errors.New("invalid network interface")
     21 	errInvalidInterfaceIndex    = errors.New("invalid network interface index")
     22 	errInvalidInterfaceName     = errors.New("invalid network interface name")
     23 	errNoSuchInterface          = errors.New("no such network interface")
     24 	errNoSuchMulticastInterface = errors.New("no such multicast network interface")
     25 )
     26 
     27 // Interface represents a mapping between network interface name
     28 // and index. It also represents network interface facility
     29 // information.
     30 type Interface struct {
     31 	Index        int          // positive integer that starts at one, zero is never used
     32 	MTU          int          // maximum transmission unit
     33 	Name         string       // e.g., "en0", "lo0", "eth0.100"
     34 	HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form
     35 	Flags        Flags        // e.g., FlagUp, FlagLoopback, FlagMulticast
     36 }
     37 
     38 type Flags uint
     39 
     40 const (
     41 	FlagUp           Flags = 1 << iota // interface is up
     42 	FlagBroadcast                      // interface supports broadcast access capability
     43 	FlagLoopback                       // interface is a loopback interface
     44 	FlagPointToPoint                   // interface belongs to a point-to-point link
     45 	FlagMulticast                      // interface supports multicast access capability
     46 )
     47 
     48 var flagNames = []string{
     49 	"up",
     50 	"broadcast",
     51 	"loopback",
     52 	"pointtopoint",
     53 	"multicast",
     54 }
     55 
     56 func (f Flags) String() string {
     57 	s := ""
     58 	for i, name := range flagNames {
     59 		if f&(1<<uint(i)) != 0 {
     60 			if s != "" {
     61 				s += "|"
     62 			}
     63 			s += name
     64 		}
     65 	}
     66 	if s == "" {
     67 		s = "0"
     68 	}
     69 	return s
     70 }
     71 
     72 // Addrs returns a list of unicast interface addresses for a specific
     73 // interface.
     74 func (ifi *Interface) Addrs() ([]Addr, error) {
     75 	if ifi == nil {
     76 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface}
     77 	}
     78 	ifat, err := interfaceAddrTable(ifi)
     79 	if err != nil {
     80 		err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
     81 	}
     82 	return ifat, err
     83 }
     84 
     85 // MulticastAddrs returns a list of multicast, joined group addresses
     86 // for a specific interface.
     87 func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
     88 	if ifi == nil {
     89 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface}
     90 	}
     91 	ifat, err := interfaceMulticastAddrTable(ifi)
     92 	if err != nil {
     93 		err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
     94 	}
     95 	return ifat, err
     96 }
     97 
     98 // Interfaces returns a list of the system's network interfaces.
     99 func Interfaces() ([]Interface, error) {
    100 	ift, err := interfaceTable(0)
    101 	if err != nil {
    102 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
    103 	}
    104 	if len(ift) != 0 {
    105 		zoneCache.update(ift)
    106 	}
    107 	return ift, nil
    108 }
    109 
    110 // InterfaceAddrs returns a list of the system's unicast interface
    111 // addresses.
    112 //
    113 // The returned list does not identify the associated interface; use
    114 // Interfaces and Interface.Addrs for more detail.
    115 func InterfaceAddrs() ([]Addr, error) {
    116 	ifat, err := interfaceAddrTable(nil)
    117 	if err != nil {
    118 		err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
    119 	}
    120 	return ifat, err
    121 }
    122 
    123 // InterfaceByIndex returns the interface specified by index.
    124 //
    125 // On Solaris, it returns one of the logical network interfaces
    126 // sharing the logical data link; for more precision use
    127 // InterfaceByName.
    128 func InterfaceByIndex(index int) (*Interface, error) {
    129 	if index <= 0 {
    130 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceIndex}
    131 	}
    132 	ift, err := interfaceTable(index)
    133 	if err != nil {
    134 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
    135 	}
    136 	ifi, err := interfaceByIndex(ift, index)
    137 	if err != nil {
    138 		err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
    139 	}
    140 	return ifi, err
    141 }
    142 
    143 func interfaceByIndex(ift []Interface, index int) (*Interface, error) {
    144 	for _, ifi := range ift {
    145 		if index == ifi.Index {
    146 			return &ifi, nil
    147 		}
    148 	}
    149 	return nil, errNoSuchInterface
    150 }
    151 
    152 // InterfaceByName returns the interface specified by name.
    153 func InterfaceByName(name string) (*Interface, error) {
    154 	if name == "" {
    155 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceName}
    156 	}
    157 	ift, err := interfaceTable(0)
    158 	if err != nil {
    159 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
    160 	}
    161 	if len(ift) != 0 {
    162 		zoneCache.update(ift)
    163 	}
    164 	for _, ifi := range ift {
    165 		if name == ifi.Name {
    166 			return &ifi, nil
    167 		}
    168 	}
    169 	return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errNoSuchInterface}
    170 }
    171 
    172 // An ipv6ZoneCache represents a cache holding partial network
    173 // interface information. It is used for reducing the cost of IPv6
    174 // addressing scope zone resolution.
    175 //
    176 // Multiple names sharing the index are managed by first-come
    177 // first-served basis for consistency.
    178 type ipv6ZoneCache struct {
    179 	sync.RWMutex                // guard the following
    180 	lastFetched  time.Time      // last time routing information was fetched
    181 	toIndex      map[string]int // interface name to its index
    182 	toName       map[int]string // interface index to its name
    183 }
    184 
    185 var zoneCache = ipv6ZoneCache{
    186 	toIndex: make(map[string]int),
    187 	toName:  make(map[int]string),
    188 }
    189 
    190 func (zc *ipv6ZoneCache) update(ift []Interface) {
    191 	zc.Lock()
    192 	defer zc.Unlock()
    193 	now := time.Now()
    194 	if zc.lastFetched.After(now.Add(-60 * time.Second)) {
    195 		return
    196 	}
    197 	zc.lastFetched = now
    198 	if len(ift) == 0 {
    199 		var err error
    200 		if ift, err = interfaceTable(0); err != nil {
    201 			return
    202 		}
    203 	}
    204 	zc.toIndex = make(map[string]int, len(ift))
    205 	zc.toName = make(map[int]string, len(ift))
    206 	for _, ifi := range ift {
    207 		zc.toIndex[ifi.Name] = ifi.Index
    208 		if _, ok := zc.toName[ifi.Index]; !ok {
    209 			zc.toName[ifi.Index] = ifi.Name
    210 		}
    211 	}
    212 }
    213 
    214 func zoneToString(zone int) string {
    215 	if zone == 0 {
    216 		return ""
    217 	}
    218 	zoneCache.update(nil)
    219 	zoneCache.RLock()
    220 	defer zoneCache.RUnlock()
    221 	name, ok := zoneCache.toName[zone]
    222 	if !ok {
    223 		name = uitoa(uint(zone))
    224 	}
    225 	return name
    226 }
    227 
    228 func zoneToInt(zone string) int {
    229 	if zone == "" {
    230 		return 0
    231 	}
    232 	zoneCache.update(nil)
    233 	zoneCache.RLock()
    234 	defer zoneCache.RUnlock()
    235 	index, ok := zoneCache.toIndex[zone]
    236 	if !ok {
    237 		index, _, _ = dtoi(zone)
    238 	}
    239 	return index
    240 }
    241