|
4 | 4 |
|
5 | 5 | package net
|
6 | 6 |
|
7 |
| -import "errors" |
| 7 | +import ( |
| 8 | + "errors" |
| 9 | + "sync" |
| 10 | + "time" |
| 11 | +) |
8 | 12 |
|
9 | 13 | var (
|
10 | 14 | errInvalidInterface = errors.New("invalid network interface")
|
@@ -88,9 +92,12 @@ func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
|
88 | 92 | func Interfaces() ([]Interface, error) {
|
89 | 93 | ift, err := interfaceTable(0)
|
90 | 94 | if err != nil {
|
91 |
| - err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} |
| 95 | + return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err} |
92 | 96 | }
|
93 |
| - return ift, err |
| 97 | + if len(ift) != 0 { |
| 98 | + zoneCache.update(ift) |
| 99 | + } |
| 100 | + return ift, nil |
94 | 101 | }
|
95 | 102 |
|
96 | 103 | // InterfaceAddrs returns a list of the system's network interface
|
@@ -137,10 +144,78 @@ func InterfaceByName(name string) (*Interface, error) {
|
137 | 144 | if err != nil {
|
138 | 145 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
139 | 146 | }
|
| 147 | + if len(ift) != 0 { |
| 148 | + zoneCache.update(ift) |
| 149 | + } |
140 | 150 | for _, ifi := range ift {
|
141 | 151 | if name == ifi.Name {
|
142 | 152 | return &ifi, nil
|
143 | 153 | }
|
144 | 154 | }
|
145 | 155 | return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errNoSuchInterface}
|
146 | 156 | }
|
| 157 | + |
| 158 | +// An ipv6ZoneCache represents a cache holding partial network |
| 159 | +// interface information. It is used for reducing the cost of IPv6 |
| 160 | +// addressing scope zone resolution. |
| 161 | +type ipv6ZoneCache struct { |
| 162 | + sync.RWMutex // guard the following |
| 163 | + lastFetched time.Time // last time routing information was fetched |
| 164 | + toIndex map[string]int // interface name to its index |
| 165 | + toName map[int]string // interface index to its name |
| 166 | +} |
| 167 | + |
| 168 | +var zoneCache = ipv6ZoneCache{ |
| 169 | + toIndex: make(map[string]int), |
| 170 | + toName: make(map[int]string), |
| 171 | +} |
| 172 | + |
| 173 | +func (zc *ipv6ZoneCache) update(ift []Interface) { |
| 174 | + zc.Lock() |
| 175 | + defer zc.Unlock() |
| 176 | + now := time.Now() |
| 177 | + if zc.lastFetched.After(now.Add(-60 * time.Second)) { |
| 178 | + return |
| 179 | + } |
| 180 | + zc.lastFetched = now |
| 181 | + if len(ift) == 0 { |
| 182 | + var err error |
| 183 | + if ift, err = interfaceTable(0); err != nil { |
| 184 | + return |
| 185 | + } |
| 186 | + } |
| 187 | + zc.toIndex = make(map[string]int, len(ift)) |
| 188 | + zc.toName = make(map[int]string, len(ift)) |
| 189 | + for _, ifi := range ift { |
| 190 | + zc.toIndex[ifi.Name] = ifi.Index |
| 191 | + zc.toName[ifi.Index] = ifi.Name |
| 192 | + } |
| 193 | +} |
| 194 | + |
| 195 | +func zoneToString(zone int) string { |
| 196 | + if zone == 0 { |
| 197 | + return "" |
| 198 | + } |
| 199 | + zoneCache.update(nil) |
| 200 | + zoneCache.RLock() |
| 201 | + defer zoneCache.RUnlock() |
| 202 | + name, ok := zoneCache.toName[zone] |
| 203 | + if !ok { |
| 204 | + name = uitoa(uint(zone)) |
| 205 | + } |
| 206 | + return name |
| 207 | +} |
| 208 | + |
| 209 | +func zoneToInt(zone string) int { |
| 210 | + if zone == "" { |
| 211 | + return 0 |
| 212 | + } |
| 213 | + zoneCache.update(nil) |
| 214 | + zoneCache.RLock() |
| 215 | + defer zoneCache.RUnlock() |
| 216 | + index, ok := zoneCache.toIndex[zone] |
| 217 | + if !ok { |
| 218 | + index, _, _ = dtoi(zone, 0) |
| 219 | + } |
| 220 | + return index |
| 221 | +} |
0 commit comments