Brad Fitzpatrick | 5194744 | 2016-03-01 22:57:46 +0000 | [diff] [blame] | 1 | // Copyright 2011 The Go Authors. All rights reserved. |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 5 | package net |
| 6 | |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 7 | import ( |
| 8 | "errors" |
| 9 | "sync" |
| 10 | "time" |
| 11 | ) |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 12 | |
Chris McGee | 6ca48f7 | 2016-09-28 21:29:08 -0400 | [diff] [blame] | 13 | // BUG(mikio): On NaCl, methods and functions related to |
Mikio Hara | 2f184c6 | 2016-09-27 19:54:05 +0900 | [diff] [blame] | 14 | // Interface are not implemented. |
Mikio Hara | a7ed9ff | 2016-08-22 05:52:15 +0900 | [diff] [blame] | 15 | |
Mikio Hara | 8fc9c50 | 2016-10-12 19:41:29 +0900 | [diff] [blame] | 16 | // BUG(mikio): On DragonFly BSD, NetBSD, OpenBSD, Plan 9 and Solaris, |
| 17 | // the MulticastAddrs method of Interface is not implemented. |
Mikio Hara | a7ed9ff | 2016-08-22 05:52:15 +0900 | [diff] [blame] | 18 | |
Mikio Hara | cbdbdc4 | 2012-01-11 09:53:32 +0900 | [diff] [blame] | 19 | var ( |
Mikio Hara | 3f5288c | 2014-04-08 06:14:19 +0900 | [diff] [blame] | 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") |
Mikio Hara | cbdbdc4 | 2012-01-11 09:53:32 +0900 | [diff] [blame] | 25 | ) |
| 26 | |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 27 | // Interface represents a mapping between network interface name |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 28 | // and index. It also represents network interface facility |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 29 | // information. |
| 30 | type 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 |
Mikio Hara | c4dfc55 | 2011-06-14 13:32:52 -0400 | [diff] [blame] | 35 | Flags Flags // e.g., FlagUp, FlagLoopback, FlagMulticast |
| 36 | } |
| 37 | |
| 38 | type Flags uint |
| 39 | |
| 40 | const ( |
| 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 | |
| 48 | var flagNames = []string{ |
| 49 | "up", |
| 50 | "broadcast", |
| 51 | "loopback", |
| 52 | "pointtopoint", |
| 53 | "multicast", |
| 54 | } |
| 55 | |
| 56 | func (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 |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 70 | } |
| 71 | |
Mikio Hara | 79c0362 | 2016-10-18 19:43:04 +0900 | [diff] [blame] | 72 | // Addrs returns a list of unicast interface addresses for a specific |
| 73 | // interface. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 74 | func (ifi *Interface) Addrs() ([]Addr, error) { |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 75 | if ifi == nil { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 76 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface} |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 77 | } |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 78 | ifat, err := interfaceAddrTable(ifi) |
| 79 | if err != nil { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 80 | err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 81 | } |
| 82 | return ifat, err |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 83 | } |
| 84 | |
Mikio Hara | 79c0362 | 2016-10-18 19:43:04 +0900 | [diff] [blame] | 85 | // MulticastAddrs returns a list of multicast, joined group addresses |
| 86 | // for a specific interface. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 87 | func (ifi *Interface) MulticastAddrs() ([]Addr, error) { |
Mikio Hara | 946cb0e | 2011-08-04 00:22:52 -0400 | [diff] [blame] | 88 | if ifi == nil { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 89 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface} |
Mikio Hara | 946cb0e | 2011-08-04 00:22:52 -0400 | [diff] [blame] | 90 | } |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 91 | ifat, err := interfaceMulticastAddrTable(ifi) |
| 92 | if err != nil { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 93 | err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 94 | } |
| 95 | return ifat, err |
Mikio Hara | 946cb0e | 2011-08-04 00:22:52 -0400 | [diff] [blame] | 96 | } |
| 97 | |
Mikio Hara | 26fa1c8 | 2012-03-09 11:50:38 +0900 | [diff] [blame] | 98 | // Interfaces returns a list of the system's network interfaces. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 99 | func Interfaces() ([]Interface, error) { |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 100 | ift, err := interfaceTable(0) |
| 101 | if err != nil { |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 102 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 103 | } |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 104 | if len(ift) != 0 { |
| 105 | zoneCache.update(ift) |
| 106 | } |
| 107 | return ift, nil |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 108 | } |
| 109 | |
Mikio Hara | 79c0362 | 2016-10-18 19:43:04 +0900 | [diff] [blame] | 110 | // InterfaceAddrs returns a list of the system's unicast interface |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 111 | // addresses. |
Mikio Hara | 79c0362 | 2016-10-18 19:43:04 +0900 | [diff] [blame] | 112 | // |
| 113 | // The returned list does not identify the associated interface; use |
| 114 | // Interfaces and Interface.Addrs for more detail. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 115 | func InterfaceAddrs() ([]Addr, error) { |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 116 | ifat, err := interfaceAddrTable(nil) |
| 117 | if err != nil { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 118 | err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 119 | } |
| 120 | return ifat, err |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | // InterfaceByIndex returns the interface specified by index. |
Mikio Hara | 2f184c6 | 2016-09-27 19:54:05 +0900 | [diff] [blame] | 124 | // |
| 125 | // On Solaris, it returns one of the logical network interfaces |
| 126 | // sharing the logical data link; for more precision use |
| 127 | // InterfaceByName. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 128 | func InterfaceByIndex(index int) (*Interface, error) { |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 129 | if index <= 0 { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 130 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceIndex} |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 131 | } |
| 132 | ift, err := interfaceTable(index) |
| 133 | if err != nil { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 134 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 135 | } |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 136 | ifi, err := interfaceByIndex(ift, index) |
| 137 | if err != nil { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 138 | err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} |
Mikio Hara | 456cf0f | 2015-04-19 22:17:08 +0900 | [diff] [blame] | 139 | } |
| 140 | return ifi, err |
Mikio Hara | 322214c | 2013-02-28 14:58:41 +0900 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | func interfaceByIndex(ift []Interface, index int) (*Interface, error) { |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 144 | for _, ifi := range ift { |
Mikio Hara | 322214c | 2013-02-28 14:58:41 +0900 | [diff] [blame] | 145 | if index == ifi.Index { |
| 146 | return &ifi, nil |
| 147 | } |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 148 | } |
Mikio Hara | cbdbdc4 | 2012-01-11 09:53:32 +0900 | [diff] [blame] | 149 | return nil, errNoSuchInterface |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | // InterfaceByName returns the interface specified by name. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 153 | func InterfaceByName(name string) (*Interface, error) { |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 154 | if name == "" { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 155 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceName} |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 156 | } |
| 157 | ift, err := interfaceTable(0) |
| 158 | if err != nil { |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 159 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 160 | } |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 161 | if len(ift) != 0 { |
| 162 | zoneCache.update(ift) |
| 163 | } |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 164 | for _, ifi := range ift { |
| 165 | if name == ifi.Name { |
| 166 | return &ifi, nil |
| 167 | } |
| 168 | } |
Mikio Hara | afd2d2b | 2015-04-21 22:53:47 +0900 | [diff] [blame] | 169 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errNoSuchInterface} |
Mikio Hara | 518331d | 2011-06-03 14:35:42 -0400 | [diff] [blame] | 170 | } |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 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. |
Mikio Hara | 86f2c5f | 2016-12-20 17:39:54 +0900 | [diff] [blame] | 175 | // |
| 176 | // Multiple names sharing the index are managed by first-come |
| 177 | // first-served basis for consistency. |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 178 | type 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 | |
| 185 | var zoneCache = ipv6ZoneCache{ |
| 186 | toIndex: make(map[string]int), |
| 187 | toName: make(map[int]string), |
| 188 | } |
| 189 | |
| 190 | func (zc *ipv6ZoneCache) update(ift []Interface) { |
| 191 | zc.Lock() |
| 192 | defer zc.Unlock() |
| 193 | now := time.Now() |
| 194 | if zc.lastFetched.After(now.Add(-60 * time.Second)) { |
| 195 | return |
| 196 | } |
| 197 | zc.lastFetched = now |
| 198 | if len(ift) == 0 { |
| 199 | var err error |
| 200 | if ift, err = interfaceTable(0); err != nil { |
| 201 | return |
| 202 | } |
| 203 | } |
| 204 | zc.toIndex = make(map[string]int, len(ift)) |
| 205 | zc.toName = make(map[int]string, len(ift)) |
| 206 | for _, ifi := range ift { |
| 207 | zc.toIndex[ifi.Name] = ifi.Index |
Mikio Hara | 86f2c5f | 2016-12-20 17:39:54 +0900 | [diff] [blame] | 208 | if _, ok := zc.toName[ifi.Index]; !ok { |
| 209 | zc.toName[ifi.Index] = ifi.Name |
| 210 | } |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 211 | } |
| 212 | } |
| 213 | |
Mikio Hara | bf4cd98 | 2017-04-27 19:00:09 +0900 | [diff] [blame] | 214 | func (zc *ipv6ZoneCache) name(index int) string { |
| 215 | if index == 0 { |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 216 | return "" |
| 217 | } |
| 218 | zoneCache.update(nil) |
| 219 | zoneCache.RLock() |
| 220 | defer zoneCache.RUnlock() |
Mikio Hara | bf4cd98 | 2017-04-27 19:00:09 +0900 | [diff] [blame] | 221 | name, ok := zoneCache.toName[index] |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 222 | if !ok { |
Mikio Hara | bf4cd98 | 2017-04-27 19:00:09 +0900 | [diff] [blame] | 223 | name = uitoa(uint(index)) |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 224 | } |
| 225 | return name |
| 226 | } |
| 227 | |
Mikio Hara | bf4cd98 | 2017-04-27 19:00:09 +0900 | [diff] [blame] | 228 | func (zc *ipv6ZoneCache) index(name string) int { |
| 229 | if name == "" { |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 230 | return 0 |
| 231 | } |
| 232 | zoneCache.update(nil) |
| 233 | zoneCache.RLock() |
| 234 | defer zoneCache.RUnlock() |
Mikio Hara | bf4cd98 | 2017-04-27 19:00:09 +0900 | [diff] [blame] | 235 | index, ok := zoneCache.toIndex[name] |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 236 | if !ok { |
Mikio Hara | bf4cd98 | 2017-04-27 19:00:09 +0900 | [diff] [blame] | 237 | index, _, _ = dtoi(name) |
Mikio Hara | 1d214f7 | 2016-04-13 06:19:53 +0900 | [diff] [blame] | 238 | } |
| 239 | return index |
| 240 | } |