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