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