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