Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 1 | // Package netstate provides routines to obtain the available set of |
| 2 | // of network addresess, for determining changes to those addresses and for |
| 3 | // selecting from amongst them according to some set of policies that are |
| 4 | // implemented by applying simple predicates (functions with names of the form |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 5 | // Is<condition>) to filter or find the first matching address from a list |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 6 | // of addresses. The intent is to make it easy to create policies that do |
| 7 | // things like 'find the first IPv4 unicast address that is globally routable, |
| 8 | // failing that use a private IPv4 address, and failing that, an IPv6 address'. |
| 9 | // |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 10 | // A simple usage would be: |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 11 | // |
| 12 | // state, _ := netstate.GetAccessibleIPs() |
Bogdan Caprita | 06829fd | 2014-11-06 15:56:28 -0800 | [diff] [blame] | 13 | // ipv4 := state.Filter(netstate.IsPublicUnicastIPv4) |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 14 | // // ipv4 will contain all of the public IPv4 addresses, if any. |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 15 | // |
| 16 | // The example policy described above would be implemented using a |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 17 | // series of calls to Filter with appropriate predicates. |
| 18 | // |
| 19 | // In some cases, it may be necessary to take IP routing information |
| 20 | // into account and hence interface hosting the address. The interface |
| 21 | // hosting each address is provided in the AddrIfc structure used to represent |
| 22 | // addresses and the IP routing information is provided by the GetAccessibleIPs |
| 23 | // function which will typically be used to obtain the available IP addresses. |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 24 | // |
| 25 | // Although most commercial networking hardware supports IPv6, some consumer |
| 26 | // devices and more importantly many ISPs do not, so routines are provided |
| 27 | // to allow developers to easily distinguish between the two and to use |
| 28 | // whichever is appropriate for their product/situation. |
| 29 | // |
| 30 | // The term 'accessible' is used to refer to any non-loopback IP address. |
| 31 | // The term 'public' is used to refer to any globally routable IP address. |
| 32 | // |
| 33 | // All IPv6 addresses are intended to be 'public', but any starting with |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 34 | // fc00::/7 (RFC4193) are reserved for private use, but the go |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 35 | // net libraries do not appear to recognise this. Similarly fe80::/10 |
| 36 | // (RFC 4291) are reserved for 'site-local' usage, but again this is not |
| 37 | // implemented in the go libraries. Any developer who needs to distinguish |
| 38 | // these cases will need to write their own routines to test for them. |
| 39 | // |
| 40 | // When using the go net package it is important to remember that IPv6 |
| 41 | // addresses subsume IPv4 and hence in many cases the same internal |
| 42 | // representation is used for both, thus testing for the length of the IP |
| 43 | // address is unreliable. The reliable test is to use the net.To4() which |
| 44 | // will return a non-nil result if can be used as an IPv4 one. Any address |
| 45 | // can be used as an IPv6 and hence the only reliable way to test for an IPv6 |
| 46 | // address that is not an IPv4 one also is for the To4 call to return nil for |
| 47 | // it. |
| 48 | package netstate |
| 49 | |
| 50 | import ( |
| 51 | "fmt" |
| 52 | "net" |
| 53 | "strings" |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 54 | |
| 55 | "veyron.io/veyron/veyron2/ipc" |
| 56 | |
| 57 | "veyron.io/veyron/veyron/lib/netconfig" |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 58 | ) |
| 59 | |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 60 | // AddrIfc represents a network address and the network interface that |
| 61 | // hosts it. |
| 62 | type AddrIfc struct { |
| 63 | // Network address |
| 64 | Addr net.Addr |
| 65 | |
| 66 | // The name of the network interface this address is hosted on, empty |
| 67 | // if this information is not available. |
| 68 | Name string |
| 69 | |
| 70 | // The IPRoutes of the network interface this address is hosted on, |
| 71 | // nil if this information is not available. |
| 72 | IPRoutes []*netconfig.IPRoute |
| 73 | } |
| 74 | |
| 75 | func (a *AddrIfc) String() string { |
| 76 | if a.IPRoutes != nil { |
| 77 | r := fmt.Sprintf("%s: %s[", a.Addr, a.Name) |
| 78 | for _, rt := range a.IPRoutes { |
| 79 | src := "" |
| 80 | if rt.PreferredSource != nil { |
| 81 | src = ", src: " + rt.PreferredSource.String() |
| 82 | } |
| 83 | r += fmt.Sprintf("{%d: net: %s, gw: %s%s}, ", rt.IfcIndex, rt.Net, rt.Gateway, src) |
| 84 | } |
| 85 | r = strings.TrimSuffix(r, ", ") |
| 86 | r += "]" |
| 87 | return r |
| 88 | } |
| 89 | return a.Addr.String() |
| 90 | } |
| 91 | |
| 92 | func (a *AddrIfc) Address() net.Addr { |
| 93 | return a.Addr |
| 94 | } |
| 95 | |
| 96 | func (a *AddrIfc) InterfaceIndex() int { |
| 97 | if len(a.IPRoutes) == 0 { |
| 98 | return -1 |
| 99 | } |
| 100 | return a.IPRoutes[0].IfcIndex |
| 101 | } |
| 102 | |
| 103 | func (a *AddrIfc) InterfaceName() string { |
| 104 | return a.Name |
| 105 | } |
| 106 | |
| 107 | func (a *AddrIfc) Networks() []net.Addr { |
| 108 | nets := []net.Addr{} |
| 109 | for _, r := range a.IPRoutes { |
| 110 | nets = append(nets, &r.Net) |
| 111 | } |
| 112 | return nets |
| 113 | } |
| 114 | |
| 115 | type AddrList []ipc.Address |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 116 | |
| 117 | func (al AddrList) String() string { |
| 118 | r := "" |
| 119 | for _, v := range al { |
| 120 | r += fmt.Sprintf("(%s) ", v) |
| 121 | } |
| 122 | return strings.TrimRight(r, " ") |
| 123 | } |
| 124 | |
| 125 | // GetAll gets all of the available addresses on the device, including |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 126 | // loopback addresses, non-IP protocols etc. |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 127 | func GetAll() (AddrList, error) { |
| 128 | interfaces, err := net.Interfaces() |
| 129 | if err != nil { |
| 130 | return nil, err |
| 131 | } |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 132 | routes := netconfig.GetIPRoutes(false) |
| 133 | routeTable := make(map[int][]*netconfig.IPRoute) |
| 134 | for _, r := range routes { |
| 135 | routeTable[r.IfcIndex] = append(routeTable[r.IfcIndex], r) |
| 136 | } |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 137 | var all AddrList |
| 138 | for _, ifc := range interfaces { |
| 139 | addrs, err := ifc.Addrs() |
| 140 | if err != nil { |
| 141 | continue |
| 142 | } |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 143 | for _, a := range addrs { |
| 144 | all = append(all, &AddrIfc{a, ifc.Name, routeTable[ifc.Index]}) |
| 145 | } |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 146 | } |
| 147 | return all, nil |
| 148 | } |
| 149 | |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 150 | // GetAccessibleIPs returns all of the accessible IP addresses on the device |
| 151 | // - i.e. excluding loopback and unspecified addresses. |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 152 | // The IP addresses returned will be host addresses. |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 153 | func GetAccessibleIPs() (AddrList, error) { |
| 154 | all, err := GetAll() |
| 155 | if err != nil { |
| 156 | return nil, err |
| 157 | } |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 158 | return all.Map(accessibleIPHost), nil |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 159 | } |
| 160 | |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 161 | func accessibleIPHost(a ipc.Address) ipc.Address { |
| 162 | if !IsAccessibleIP(a) { |
| 163 | return nil |
| 164 | } |
| 165 | aifc, ok := a.(*AddrIfc) |
| 166 | if !ok { |
| 167 | return nil |
| 168 | } |
| 169 | ip := AsIPAddr(aifc.Addr) |
| 170 | if ip == nil { |
| 171 | return aifc |
| 172 | } |
| 173 | aifc.Addr = ip |
| 174 | return aifc |
| 175 | } |
| 176 | |
| 177 | // AddressPredicate defines the function signature for predicate functions |
| 178 | // to be used with AddrList |
| 179 | type AddressPredicate func(a ipc.Address) bool |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 180 | |
| 181 | // Filter returns all of the addresses for which the predicate |
| 182 | // function is true. |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 183 | func (al AddrList) Filter(predicate AddressPredicate) AddrList { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 184 | r := AddrList{} |
| 185 | for _, a := range al { |
| 186 | if predicate(a) { |
| 187 | r = append(r, a) |
| 188 | } |
| 189 | } |
| 190 | return r |
| 191 | } |
| 192 | |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 193 | type Mapper func(a ipc.Address) ipc.Address |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 194 | |
| 195 | // Map will apply the Mapper function to all of the items in its receiver |
| 196 | // and return a new AddrList containing all of the non-nil results from |
| 197 | // said calls. |
| 198 | func (al AddrList) Map(mapper Mapper) AddrList { |
| 199 | var ral AddrList |
| 200 | for _, a := range al { |
| 201 | if na := mapper(a); na != nil { |
| 202 | ral = append(ral, na) |
| 203 | } |
| 204 | } |
| 205 | return ral |
| 206 | } |
| 207 | |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 208 | // Convert the network address component of an ipc.Address into an instance |
| 209 | // with a net.Addr that contains an IP host address (as opposed to a |
| 210 | // network CIDR for example). |
| 211 | func ConvertToIPHost(a ipc.Address) ipc.Address { |
| 212 | aifc, ok := a.(*AddrIfc) |
| 213 | if !ok { |
| 214 | return nil |
| 215 | } |
| 216 | aifc.Addr = AsIPAddr(aifc.Addr) |
| 217 | return aifc |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 218 | } |
| 219 | |
| 220 | // IsIPProtocol returns true if its parameter is one of the allowed |
| 221 | // network/protocol values for IP. |
| 222 | func IsIPProtocol(n string) bool { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 223 | switch n { |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 224 | case "ip+net", "ip", "tcp", "tcp4", "tcp6", "udp": |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 225 | return true |
| 226 | default: |
| 227 | return false |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 228 | } |
| 229 | } |
| 230 | |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 231 | // AsIPAddr returns its argument as a net.IPAddr if that's possible. |
| 232 | func AsIPAddr(a net.Addr) *net.IPAddr { |
| 233 | if v, ok := a.(*net.IPAddr); ok { |
| 234 | return v |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 235 | } |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 236 | if ipn, ok := a.(*net.IPNet); ok { |
| 237 | return &net.IPAddr{IP: ipn.IP} |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 238 | } |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 239 | if IsIPProtocol(a.Network()) { |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 240 | if r := net.ParseIP(a.String()); r != nil { |
| 241 | return &net.IPAddr{IP: r} |
| 242 | } |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 243 | } |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 244 | return nil |
| 245 | } |
| 246 | |
| 247 | // AsIP returns its argument as a net.IP if that's possible. |
| 248 | func AsIP(a net.Addr) net.IP { |
| 249 | ipAddr := AsIPAddr(a) |
| 250 | if ipAddr == nil { |
| 251 | return nil |
| 252 | } |
| 253 | return ipAddr.IP |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | // IsUnspecified returns true if its argument is an unspecified IP address |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 257 | func IsUnspecifiedIP(a ipc.Address) bool { |
| 258 | if ip := AsIP(a.Address()); ip != nil { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 259 | return ip.IsUnspecified() |
| 260 | } |
| 261 | return false |
| 262 | } |
| 263 | |
| 264 | // IsLoopback returns true if its argument is a loopback IP address |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 265 | func IsLoopbackIP(a ipc.Address) bool { |
| 266 | if ip := AsIP(a.Address()); ip != nil && !ip.IsUnspecified() { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 267 | return ip.IsLoopback() |
| 268 | } |
| 269 | return false |
| 270 | } |
| 271 | |
| 272 | // IsAccessible returns true if its argument is an accessible (non-loopback) |
| 273 | // IP address. |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 274 | func IsAccessibleIP(a ipc.Address) bool { |
| 275 | if ip := AsIP(a.Address()); ip != nil && !ip.IsUnspecified() { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 276 | return !ip.IsLoopback() |
| 277 | } |
| 278 | return false |
| 279 | } |
| 280 | |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 281 | // IsUnicastIP returns true if its argument is a unicast IP address. |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 282 | func IsUnicastIP(a ipc.Address) bool { |
| 283 | if ip := AsIP(a.Address()); ip != nil && !ip.IsUnspecified() { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 284 | // ipv4 or v6 |
| 285 | return !(ip.IsMulticast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast()) |
| 286 | } |
| 287 | return false |
| 288 | } |
| 289 | |
| 290 | // IsUnicastIPv4 returns true if its argument is a unicast IP4 address |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 291 | func IsUnicastIPv4(a ipc.Address) bool { |
| 292 | if ip := AsIP(a.Address()); ip != nil && ip.To4() != nil { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 293 | return !ip.IsUnspecified() && !ip.IsMulticast() |
| 294 | } |
| 295 | return false |
| 296 | } |
| 297 | |
| 298 | // IsPublicUnicastIPv4 returns true if its argument is a globally routable, |
| 299 | // public IPv4 unicast address. |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 300 | func IsPublicUnicastIPv4(a ipc.Address) bool { |
| 301 | if ip := AsIP(a.Address()); ip != nil && !ip.IsUnspecified() { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 302 | if t := ip.To4(); t != nil && IsGloballyRoutableIP(t) { |
| 303 | return !ip.IsMulticast() |
| 304 | } |
| 305 | } |
| 306 | return false |
| 307 | } |
| 308 | |
| 309 | // IsUnicastIPv6 returns true if its argument is a unicast IPv6 address |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 310 | func IsUnicastIPv6(a ipc.Address) bool { |
| 311 | if ip := AsIP(a.Address()); ip != nil && ip.To4() == nil { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 312 | return !ip.IsUnspecified() && !(ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast()) |
| 313 | } |
| 314 | return false |
| 315 | } |
| 316 | |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 317 | // IsUnicastIPv6 returns true if its argument is a globally routable IP6 |
| 318 | // address |
| 319 | func IsPublicUnicastIPv6(a ipc.Address) bool { |
| 320 | if ip := AsIP(a.Address()); ip != nil && ip.To4() == nil { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 321 | if t := ip.To16(); t != nil && IsGloballyRoutableIP(t) { |
| 322 | return true |
| 323 | } |
| 324 | } |
| 325 | return false |
| 326 | } |
| 327 | |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 328 | // IsPublicUnicastIP returns true if its argument is a global routable IPv4 |
| 329 | // or 6 address. |
| 330 | func IsPublicUnicastIP(a ipc.Address) bool { |
| 331 | if ip := AsIP(a.Address()); ip != nil { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 332 | if t := ip.To4(); t != nil && IsGloballyRoutableIP(t) { |
| 333 | return true |
| 334 | } |
| 335 | if t := ip.To16(); t != nil && IsGloballyRoutableIP(t) { |
| 336 | return true |
| 337 | } |
| 338 | } |
| 339 | return false |
| 340 | } |
| 341 | |
| 342 | func diffAB(a, b AddrList) AddrList { |
| 343 | diff := AddrList{} |
| 344 | for _, av := range a { |
| 345 | found := false |
| 346 | for _, bv := range b { |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 347 | if av.Address().Network() == bv.Address().Network() && |
| 348 | av.Address().String() == bv.Address().String() { |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 349 | found = true |
| 350 | break |
| 351 | } |
| 352 | } |
| 353 | if !found { |
| 354 | diff = append(diff, av) |
| 355 | } |
| 356 | } |
| 357 | return diff |
| 358 | } |
| 359 | |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 360 | // FindAdded returns the set addresses that are present in b, but not |
| 361 | // in a - i.e. have been added. |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 362 | func FindAdded(a, b AddrList) AddrList { |
| 363 | return diffAB(b, a) |
| 364 | } |
| 365 | |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 366 | // FindRemoved returns the set of addresses that are present in a, but not |
| 367 | // in b - i.e. have been removed. |
Cosmos Nicolaou | f7a11d9 | 2014-08-29 09:56:07 -0700 | [diff] [blame] | 368 | func FindRemoved(a, b AddrList) AddrList { |
| 369 | return diffAB(a, b) |
| 370 | } |
Robert Kroeger | 053c068 | 2014-09-29 15:02:49 -0700 | [diff] [blame] | 371 | |
| 372 | // SameMachine returns true if the provided addr is on the node |
| 373 | // executing this function. |
| 374 | func SameMachine(addr net.Addr) (bool, error) { |
| 375 | // The available interfaces may change between calls. |
| 376 | addrs, err := GetAll() |
| 377 | if err != nil { |
| 378 | return false, err |
| 379 | } |
Robert Kroeger | 053c068 | 2014-09-29 15:02:49 -0700 | [diff] [blame] | 380 | ips := make(map[string]struct{}) |
| 381 | for _, a := range addrs { |
| 382 | ip, _, err := net.ParseCIDR(a.Address().String()) |
| 383 | if err != nil { |
| 384 | return false, err |
| 385 | } |
| 386 | ips[ip.String()] = struct{}{} |
| 387 | } |
| 388 | |
| 389 | client, _, err := net.SplitHostPort(addr.String()) |
| 390 | if err != nil { |
| 391 | return false, err |
| 392 | } |
Robert Kroeger | 053c068 | 2014-09-29 15:02:49 -0700 | [diff] [blame] | 393 | _, islocal := ips[client] |
| 394 | return islocal, nil |
| 395 | } |