github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/network/address.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package network 5 6 import ( 7 "bytes" 8 "encoding/binary" 9 "net" 10 "sort" 11 12 "github.com/juju/errors" 13 ) 14 15 // Private network ranges for IPv4 and IPv6. 16 // See: http://tools.ietf.org/html/rfc1918 17 // Also: http://tools.ietf.org/html/rfc4193 18 var ( 19 classAPrivate = mustParseCIDR("10.0.0.0/8") 20 classBPrivate = mustParseCIDR("172.16.0.0/12") 21 classCPrivate = mustParseCIDR("192.168.0.0/16") 22 ipv6UniqueLocal = mustParseCIDR("fc00::/7") 23 ) 24 25 // globalPreferIPv6 determines whether IPv6 addresses will be 26 // preferred when selecting a public or internal addresses, using the 27 // Select*() methods below. InitializeFromConfig() needs to be called 28 // to set this flag globally at the earliest time possible (e.g. at 29 // bootstrap, agent startup, before any CLI command). 30 var globalPreferIPv6 bool = false 31 32 // ResetGlobalPreferIPv6 resets the global variable back to the default, 33 // and is called only from the isolation test suite to make sure we have 34 // a clean environment. 35 func ResetGlobalPreferIPv6() { 36 globalPreferIPv6 = false 37 } 38 39 func mustParseCIDR(s string) *net.IPNet { 40 _, net, err := net.ParseCIDR(s) 41 if err != nil { 42 panic(err) 43 } 44 return net 45 } 46 47 // AddressType represents the possible ways of specifying a machine location by 48 // either a hostname resolvable by dns lookup, or IPv4 or IPv6 address. 49 type AddressType string 50 51 const ( 52 HostName AddressType = "hostname" 53 IPv4Address AddressType = "ipv4" 54 IPv6Address AddressType = "ipv6" 55 ) 56 57 // Scope denotes the context a location may apply to. If a name or 58 // address can be reached from the wider internet, it is considered 59 // public. A private network address is either specific to the cloud 60 // or cloud subnet a machine belongs to, or to the machine itself for 61 // containers. 62 type Scope string 63 64 const ( 65 ScopeUnknown Scope = "" 66 ScopePublic Scope = "public" 67 ScopeCloudLocal Scope = "local-cloud" 68 ScopeMachineLocal Scope = "local-machine" 69 ScopeLinkLocal Scope = "link-local" 70 ) 71 72 // Address represents the location of a machine, including metadata 73 // about what kind of location the address describes. 74 type Address struct { 75 Value string 76 Type AddressType 77 NetworkName string 78 Scope 79 } 80 81 // String returns a string representation of the address, 82 // in the form: scope:address(network name); 83 // for example: 84 // 85 // public:c2-54-226-162-124.compute-1.amazonaws.com(ec2network) 86 // 87 // If the scope is NetworkUnknown, the initial scope: prefix will 88 // be omitted. If the NetworkName is blank, the (network name) suffix 89 // will be omitted. 90 func (a Address) String() string { 91 var buf bytes.Buffer 92 if a.Scope != ScopeUnknown { 93 buf.WriteString(string(a.Scope)) 94 buf.WriteByte(':') 95 } 96 buf.WriteString(a.Value) 97 if a.NetworkName != "" { 98 buf.WriteByte('(') 99 buf.WriteString(a.NetworkName) 100 buf.WriteByte(')') 101 } 102 return buf.String() 103 } 104 105 // GoString implements fmt.GoStringer. 106 func (a Address) GoString() string { 107 return a.String() 108 } 109 110 // NewAddress creates a new Address, deriving its type from the value 111 // and using ScopeUnknown as scope. It's a shortcut to calling 112 // NewScopedAddress(value, ScopeUnknown). 113 func NewAddress(value string) Address { 114 return NewScopedAddress(value, ScopeUnknown) 115 } 116 117 // NewScopedAddress creates a new Address, deriving its type from the 118 // value. 119 // 120 // If the specified scope is ScopeUnknown, then NewScopedAddress will 121 // attempt derive the scope based on reserved IP address ranges. 122 // Because passing ScopeUnknown is fairly common, NewAddress() above 123 // does exactly that. 124 func NewScopedAddress(value string, scope Scope) Address { 125 addr := Address{ 126 Value: value, 127 Type: DeriveAddressType(value), 128 Scope: scope, 129 } 130 if scope == ScopeUnknown { 131 addr.Scope = deriveScope(addr) 132 } 133 return addr 134 } 135 136 // NewAddresses is a convenience function to create addresses from a 137 // string slice. 138 func NewAddresses(inAddresses ...string) (outAddresses []Address) { 139 for _, address := range inAddresses { 140 outAddresses = append(outAddresses, NewAddress(address)) 141 } 142 return outAddresses 143 } 144 145 // DeriveAddressType attempts to detect the type of address given. 146 func DeriveAddressType(value string) AddressType { 147 ip := net.ParseIP(value) 148 switch { 149 case ip == nil: 150 // TODO(gz): Check value is a valid hostname 151 return HostName 152 case ip.To4() != nil: 153 return IPv4Address 154 case ip.To16() != nil: 155 return IPv6Address 156 default: 157 panic("Unknown form of IP address") 158 } 159 } 160 161 func isIPv4PrivateNetworkAddress(addrType AddressType, ip net.IP) bool { 162 if addrType != IPv4Address { 163 return false 164 } 165 return classAPrivate.Contains(ip) || 166 classBPrivate.Contains(ip) || 167 classCPrivate.Contains(ip) 168 } 169 170 func isIPv6UniqueLocalAddress(addrType AddressType, ip net.IP) bool { 171 if addrType != IPv6Address { 172 return false 173 } 174 return ipv6UniqueLocal.Contains(ip) 175 } 176 177 // deriveScope attempts to derive the network scope from an address's 178 // type and value, returning the original network scope if no 179 // deduction can be made. 180 func deriveScope(addr Address) Scope { 181 if addr.Type == HostName { 182 return addr.Scope 183 } 184 ip := net.ParseIP(addr.Value) 185 if ip == nil { 186 return addr.Scope 187 } 188 if ip.IsLoopback() { 189 return ScopeMachineLocal 190 } 191 if isIPv4PrivateNetworkAddress(addr.Type, ip) || 192 isIPv6UniqueLocalAddress(addr.Type, ip) { 193 return ScopeCloudLocal 194 } 195 if ip.IsLinkLocalMulticast() || 196 ip.IsLinkLocalUnicast() || 197 ip.IsInterfaceLocalMulticast() { 198 return ScopeLinkLocal 199 } 200 if ip.IsGlobalUnicast() { 201 return ScopePublic 202 } 203 return addr.Scope 204 } 205 206 // ExactScopeMatch checks if an address exactly matches any of the specified 207 // scopes. An address will not match if globalPreferIPv6 is set and it isn't an 208 // IPv6 address. 209 func ExactScopeMatch(addr Address, addrScopes ...Scope) bool { 210 if globalPreferIPv6 && addr.Type != IPv6Address { 211 return false 212 } 213 for _, scope := range addrScopes { 214 if addr.Scope == scope { 215 return true 216 } 217 } 218 return false 219 } 220 221 // SelectPublicAddress picks one address from a slice that would be 222 // appropriate to display as a publicly accessible endpoint. If there 223 // are no suitable addresses, then ok is false (and an empty address is 224 // returned). If a suitable address is then ok is true. 225 func SelectPublicAddress(addresses []Address) (Address, bool) { 226 index := bestAddressIndex(len(addresses), globalPreferIPv6, func(i int) Address { 227 return addresses[i] 228 }, publicMatch) 229 if index < 0 { 230 return Address{}, false 231 } 232 return addresses[index], true 233 } 234 235 // SelectPublicHostPort picks one HostPort from a slice that would be 236 // appropriate to display as a publicly accessible endpoint. If there 237 // are no suitable candidates, the empty string is returned. 238 func SelectPublicHostPort(hps []HostPort) string { 239 index := bestAddressIndex(len(hps), globalPreferIPv6, func(i int) Address { 240 return hps[i].Address 241 }, publicMatch) 242 if index < 0 { 243 return "" 244 } 245 return hps[index].NetAddr() 246 } 247 248 // SelectInternalAddress picks one address from a slice that can be 249 // used as an endpoint for juju internal communication. If there are 250 // are no suitable addresses, then ok is false (and an empty address is 251 // returned). If a suitable address is then ok is true. 252 func SelectInternalAddress(addresses []Address, machineLocal bool) (Address, bool) { 253 index := bestAddressIndex(len(addresses), globalPreferIPv6, func(i int) Address { 254 return addresses[i] 255 }, internalAddressMatcher(machineLocal)) 256 if index < 0 { 257 return Address{}, false 258 } 259 return addresses[index], true 260 } 261 262 // SelectInternalHostPort picks one HostPort from a slice that can be 263 // used as an endpoint for juju internal communication and returns it 264 // in its NetAddr form. If there are no suitable addresses, the empty 265 // string is returned. 266 func SelectInternalHostPort(hps []HostPort, machineLocal bool) string { 267 index := bestAddressIndex(len(hps), globalPreferIPv6, func(i int) Address { 268 return hps[i].Address 269 }, internalAddressMatcher(machineLocal)) 270 if index < 0 { 271 return "" 272 } 273 return hps[index].NetAddr() 274 } 275 276 func publicMatch(addr Address, preferIPv6 bool) scopeMatch { 277 switch addr.Scope { 278 case ScopePublic: 279 return mayPreferIPv6(addr, exactScope, preferIPv6) 280 case ScopeCloudLocal, ScopeUnknown: 281 return mayPreferIPv6(addr, fallbackScope, preferIPv6) 282 } 283 return invalidScope 284 } 285 286 // mayPreferIPv6 returns mismatchedTypeExactScope or 287 // mismatchedTypeFallbackScope (depending on originalScope) if addr's 288 // type is IPv4, and preferIPv6 is true. When preferIPv6 is false, or 289 // addr's type is IPv6 and preferIPv6 is true, returns the 290 // originalScope unchanged. 291 func mayPreferIPv6(addr Address, originalScope scopeMatch, preferIPv6 bool) scopeMatch { 292 if preferIPv6 && addr.Type != IPv6Address { 293 switch originalScope { 294 case exactScope: 295 return mismatchedTypeExactScope 296 case fallbackScope: 297 return mismatchedTypeFallbackScope 298 } 299 return invalidScope 300 } 301 return originalScope 302 } 303 304 func internalAddressMatcher(machineLocal bool) func(Address, bool) scopeMatch { 305 if machineLocal { 306 return cloudOrMachineLocalMatch 307 } 308 return cloudLocalMatch 309 } 310 311 func cloudLocalMatch(addr Address, preferIPv6 bool) scopeMatch { 312 switch addr.Scope { 313 case ScopeCloudLocal: 314 return mayPreferIPv6(addr, exactScope, preferIPv6) 315 case ScopePublic, ScopeUnknown: 316 return mayPreferIPv6(addr, fallbackScope, preferIPv6) 317 } 318 return invalidScope 319 } 320 321 func cloudOrMachineLocalMatch(addr Address, preferIPv6 bool) scopeMatch { 322 if addr.Scope == ScopeMachineLocal { 323 return mayPreferIPv6(addr, exactScope, preferIPv6) 324 } 325 return cloudLocalMatch(addr, preferIPv6) 326 } 327 328 type scopeMatch int 329 330 const ( 331 invalidScope scopeMatch = iota 332 exactScope 333 fallbackScope 334 mismatchedTypeExactScope 335 mismatchedTypeFallbackScope 336 ) 337 338 // bestAddressIndex returns the index of the first address 339 // with an exactly matching scope, or the first address with 340 // a matching fallback scope if there are no exact matches, or 341 // a matching scope but mismatched type when preferIPv6 is true. 342 // If there are no suitable addresses, -1 is returned. 343 func bestAddressIndex(numAddr int, preferIPv6 bool, getAddr func(i int) Address, match func(addr Address, preferIPv6 bool) scopeMatch) int { 344 fallbackAddressIndex := -1 345 mismatchedTypeFallbackIndex := -1 346 mismatchedTypeExactIndex := -1 347 for i := 0; i < numAddr; i++ { 348 addr := getAddr(i) 349 switch match(addr, preferIPv6) { 350 case exactScope: 351 logger.Tracef("exactScope match: index=%d,fallback=%d,mismatchedExact=%d,mismatchedFallback=%d,preferIPv6=%v", i, fallbackAddressIndex, mismatchedTypeExactIndex, mismatchedTypeFallbackIndex, preferIPv6) 352 return i 353 case fallbackScope: 354 logger.Tracef("fallbackScope match: index=%d,fallback=%d,mismatchedExact=%d,mismatchedFallback=%d,preferIPv6=%v", i, fallbackAddressIndex, mismatchedTypeExactIndex, mismatchedTypeFallbackIndex, preferIPv6) 355 // Use the first fallback address if there are no exact matches. 356 if fallbackAddressIndex == -1 { 357 fallbackAddressIndex = i 358 } 359 case mismatchedTypeExactScope: 360 logger.Tracef("mismatchedTypeExactScope match: index=%d,fallback=%d,mismatchedExact=%d,mismatchedFallback=%d,preferIPv6=%v", i, fallbackAddressIndex, mismatchedTypeExactIndex, mismatchedTypeFallbackIndex, preferIPv6) 361 // We have an exact scope match, but the type does not 362 // match, so save the first index as this is the best 363 // match so far. 364 if mismatchedTypeExactIndex == -1 { 365 mismatchedTypeExactIndex = i 366 } 367 case mismatchedTypeFallbackScope: 368 logger.Tracef("mismatchedTypeFallbackScope match: index=%d,fallback=%d,mismatchedExact=%d,mismatchedFallback=%d,preferIPv6=%v", i, fallbackAddressIndex, mismatchedTypeExactIndex, mismatchedTypeFallbackIndex, preferIPv6) 369 // We have a fallback scope match, but the type does not 370 // match, so we save the first index in case this is the 371 // best match so far. 372 if mismatchedTypeFallbackIndex == -1 { 373 mismatchedTypeFallbackIndex = i 374 } 375 } 376 } 377 if preferIPv6 { 378 if fallbackAddressIndex != -1 { 379 // Prefer an IPv6 fallback to a IPv4 mismatch. 380 logger.Tracef("fallbackScope return: index=%d,fallback=%d,mismatchedExact=%d,mismatchedFallback=%d,preferIPv6=%v", fallbackAddressIndex, fallbackAddressIndex, mismatchedTypeExactIndex, mismatchedTypeFallbackIndex, preferIPv6) 381 return fallbackAddressIndex 382 } 383 if mismatchedTypeExactIndex != -1 { 384 // Prefer an exact IPv4 match to a fallback. 385 logger.Tracef("mismatchedTypeExactScope return: index=%d,fallback=%d,mismatchedExact=%d,mismatchedFallback=%d,preferIPv6=%v", mismatchedTypeExactIndex, fallbackAddressIndex, mismatchedTypeExactIndex, mismatchedTypeFallbackIndex, preferIPv6) 386 return mismatchedTypeExactIndex 387 } 388 logger.Tracef("mismatchedTypeFallbackScope return: index=%d,fallback=%d,mismatchedExact=%d,mismatchedFallback=%d,preferIPv6=%v", mismatchedTypeFallbackIndex, fallbackAddressIndex, mismatchedTypeExactIndex, mismatchedTypeFallbackIndex, preferIPv6) 389 return mismatchedTypeFallbackIndex 390 } 391 logger.Tracef("fallbackScope return: index=%d,fallback=%d,mismatchedExact=%d,mismatchedFallback=%d,preferIPv6=%v", fallbackAddressIndex, fallbackAddressIndex, mismatchedTypeExactIndex, mismatchedTypeFallbackIndex, preferIPv6) 392 return fallbackAddressIndex 393 } 394 395 // sortOrder calculates the "weight" of the address when sorting, 396 // taking into account the preferIPv6 flag: 397 // - public IPs first; 398 // - hostnames after that, but "localhost" will be last if present; 399 // - cloud-local next; 400 // - machine-local next; 401 // - link-local next; 402 // - non-hostnames with unknown scope last. 403 // 404 // When preferIPv6 flag and the address type do not match, the order 405 // is incremented to put non-preferred addresses after preferred. 406 func (a Address) sortOrder(preferIPv6 bool) int { 407 order := 0xFF 408 switch a.Scope { 409 case ScopePublic: 410 order = 0x00 411 case ScopeCloudLocal: 412 order = 0x20 413 case ScopeMachineLocal: 414 order = 0x40 415 case ScopeLinkLocal: 416 order = 0x80 417 } 418 switch a.Type { 419 case HostName: 420 order = 0x10 421 if a.Value == "localhost" { 422 order++ 423 } 424 case IPv4Address: 425 if preferIPv6 { 426 order++ 427 } 428 case IPv6Address: 429 if !preferIPv6 { 430 order++ 431 } 432 } 433 return order 434 } 435 436 type addressesPreferringIPv4Slice []Address 437 438 func (a addressesPreferringIPv4Slice) Len() int { return len(a) } 439 func (a addressesPreferringIPv4Slice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 440 func (a addressesPreferringIPv4Slice) Less(i, j int) bool { 441 addr1 := a[i] 442 addr2 := a[j] 443 order1 := addr1.sortOrder(false) 444 order2 := addr2.sortOrder(false) 445 if order1 == order2 { 446 return addr1.Value < addr2.Value 447 } 448 return order1 < order2 449 } 450 451 type addressesPreferringIPv6Slice struct { 452 addressesPreferringIPv4Slice 453 } 454 455 func (a addressesPreferringIPv6Slice) Less(i, j int) bool { 456 addr1 := a.addressesPreferringIPv4Slice[i] 457 addr2 := a.addressesPreferringIPv4Slice[j] 458 order1 := addr1.sortOrder(true) 459 order2 := addr2.sortOrder(true) 460 if order1 == order2 { 461 return addr1.Value < addr2.Value 462 } 463 return order1 < order2 464 } 465 466 // SortAddresses sorts the given Address slice according to the 467 // sortOrder of each address and the preferIpv6 flag. See 468 // Address.sortOrder() for more info. 469 func SortAddresses(addrs []Address, preferIPv6 bool) { 470 if preferIPv6 { 471 sort.Sort(addressesPreferringIPv6Slice{addressesPreferringIPv4Slice(addrs)}) 472 } else { 473 sort.Sort(addressesPreferringIPv4Slice(addrs)) 474 } 475 } 476 477 // DecimalToIPv4 converts a decimal to the dotted quad IP address format. 478 func DecimalToIPv4(addr uint32) net.IP { 479 bytes := make([]byte, 4) 480 binary.BigEndian.PutUint32(bytes, addr) 481 return net.IP(bytes) 482 } 483 484 // IPv4ToDecimal converts a dotted quad IP address to its decimal equivalent. 485 func IPv4ToDecimal(ipv4Addr net.IP) (uint32, error) { 486 ip := ipv4Addr.To4() 487 if ip == nil { 488 return 0, errors.Errorf("%q is not a valid IPv4 address", ipv4Addr.String()) 489 } 490 return binary.BigEndian.Uint32([]byte(ip)), nil 491 }