interface.mx raw

   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         []byte       // e.g., "en0", "lo0", "eth0.100"; may be the empty string
  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 = [][]byte{
  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 []byte) (*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][]byte // interface index to its name
 197  }
 198  
 199  var zoneCache = ipv6ZoneCache{
 200  	toIndex: map[string]int{},
 201  	toName:  map[int][]byte{},
 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 = map[string]int{}
 222  	zc.toName = map[int][]byte{}
 223  	for _, ifi := range ift {
 224  		if ifi.Name != "" {
 225  			zc.toIndex[ifi.Name] = ifi.Index
 226  			if _, ok := zc.toName[ifi.Index]; !ok {
 227  				zc.toName[ifi.Index] = ifi.Name
 228  			}
 229  		}
 230  	}
 231  	return true
 232  }
 233  
 234  func (zc *ipv6ZoneCache) name(index int) []byte {
 235  	if index == 0 {
 236  		return ""
 237  	}
 238  	updated := zoneCache.update(nil, false)
 239  	zoneCache.RLock()
 240  	name, ok := zoneCache.toName[index]
 241  	zoneCache.RUnlock()
 242  	if !ok && !updated {
 243  		zoneCache.update(nil, true)
 244  		zoneCache.RLock()
 245  		name, ok = zoneCache.toName[index]
 246  		zoneCache.RUnlock()
 247  	}
 248  	if !ok { // last resort
 249  		name = itoa.Uitoa(uint(index))
 250  	}
 251  	return name
 252  }
 253  
 254  func (zc *ipv6ZoneCache) index(name []byte) int {
 255  	if name == "" {
 256  		return 0
 257  	}
 258  	updated := zoneCache.update(nil, false)
 259  	zoneCache.RLock()
 260  	index, ok := zoneCache.toIndex[name]
 261  	zoneCache.RUnlock()
 262  	if !ok && !updated {
 263  		zoneCache.update(nil, true)
 264  		zoneCache.RLock()
 265  		index, ok = zoneCache.toIndex[name]
 266  		zoneCache.RUnlock()
 267  	}
 268  	if !ok { // last resort
 269  		index, _, _ = dtoi(name)
 270  	}
 271  	return index
 272  }
 273