Source file src/net/interface.go

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

View as plain text