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 5package net 6 7import ( 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 31var ( 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. 42type 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 50type Flags uint 51 52const ( 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 61var flagNames = []string{ 62 "up", 63 "broadcast", 64 "loopback", 65 "pointtopoint", 66 "multicast", 67 "running", 68} 69 70func (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. 88func (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. 101func (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. 113func 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. 129func 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]. 142func 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 157func 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. 167func 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. 192type 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 199var 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. 207func (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 232func (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 252func (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