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  }