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

View as plain text