github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/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  	"fmt"
     9  	"net"
    10  	"sort"
    11  
    12  	"github.com/juju/collections/set"
    13  	"github.com/juju/errors"
    14  )
    15  
    16  // Private and special use network ranges for IPv4 and IPv6.
    17  // See: http://tools.ietf.org/html/rfc1918
    18  // Also: http://tools.ietf.org/html/rfc4193
    19  // And: https://tools.ietf.org/html/rfc6890
    20  var (
    21  	classAPrivate   = mustParseCIDR("10.0.0.0/8")
    22  	classBPrivate   = mustParseCIDR("172.16.0.0/12")
    23  	classCPrivate   = mustParseCIDR("192.168.0.0/16")
    24  	ipv6UniqueLocal = mustParseCIDR("fc00::/7")
    25  	classEReserved  = mustParseCIDR("240.0.0.0/4")
    26  )
    27  
    28  func mustParseCIDR(s string) *net.IPNet {
    29  	_, ipNet, err := net.ParseCIDR(s)
    30  	if err != nil {
    31  		panic(err)
    32  	}
    33  	return ipNet
    34  }
    35  
    36  // AddressConfigType defines valid network link configuration types.
    37  // See interfaces(5) for details.
    38  type AddressConfigType string
    39  
    40  const (
    41  	ConfigUnknown  AddressConfigType = ""
    42  	ConfigDHCP     AddressConfigType = "dhcp"
    43  	ConfigStatic   AddressConfigType = "static"
    44  	ConfigManual   AddressConfigType = "manual"
    45  	ConfigLoopback AddressConfigType = "loopback"
    46  )
    47  
    48  // IsValidAddressConfigType returns whether the given value is a valid
    49  // method to configure a link-layer network device's IP address.
    50  // TODO (manadart 2021-05-04): There is an issue with the usage of this
    51  // method in state where we have denormalised the config method so it is
    52  // against device addresses. This is because "manual" indicates a device that
    53  // has no configuration by default. This could never apply to an address.
    54  func IsValidAddressConfigType(value string) bool {
    55  	switch AddressConfigType(value) {
    56  	case ConfigLoopback, ConfigStatic, ConfigDHCP, ConfigManual:
    57  		return true
    58  	}
    59  	return false
    60  }
    61  
    62  // AddressType represents the possible ways of specifying a machine location by
    63  // either a hostname resolvable by dns lookup, or IPv4 or IPv6 address.
    64  type AddressType string
    65  
    66  const (
    67  	HostName    AddressType = "hostname"
    68  	IPv4Address AddressType = "ipv4"
    69  	IPv6Address AddressType = "ipv6"
    70  )
    71  
    72  // Scope denotes the context a location may apply to. If a name or address can
    73  // be reached from the wider internet, it is considered public.
    74  // A private network address is either specific to the cloud or cloud subnet a
    75  // machine belongs to, or to the machine itself for containers.
    76  type Scope string
    77  
    78  const (
    79  	ScopeUnknown      Scope = ""
    80  	ScopePublic       Scope = "public"
    81  	ScopeCloudLocal   Scope = "local-cloud"
    82  	ScopeFanLocal     Scope = "local-fan"
    83  	ScopeMachineLocal Scope = "local-machine"
    84  	ScopeLinkLocal    Scope = "link-local"
    85  )
    86  
    87  // ScopeMatch is a numeric designation of how well the requirement
    88  // for satisfying a scope is met.
    89  type ScopeMatch int
    90  
    91  const (
    92  	invalidScope ScopeMatch = iota
    93  	exactScopeIPv4
    94  	exactScope
    95  	firstFallbackScopeIPv4
    96  	firstFallbackScope
    97  	secondFallbackScopeIPv4
    98  	secondFallbackScope
    99  )
   100  
   101  // Address describes methods for returning details
   102  // about an IP address or host name.
   103  type Address interface {
   104  	// Host returns the value for the host-name/IP address.
   105  	Host() string
   106  
   107  	// AddressType returns the type of the address.
   108  	AddressType() AddressType
   109  
   110  	// AddressScope returns the scope of the address.
   111  	AddressScope() Scope
   112  
   113  	// AddressCIDR returns the subnet CIDR of the address.
   114  	AddressCIDR() string
   115  
   116  	// AddressConfigType returns the configuration method of the address.
   117  	AddressConfigType() AddressConfigType
   118  
   119  	// AddressIsSecondary returns whether this address is not the
   120  	// primary address associated with the network device.
   121  	AddressIsSecondary() bool
   122  }
   123  
   124  // ScopeMatchFunc is an alias for a function that accepts an Address,
   125  // and returns what kind of scope match is determined by the body.
   126  type ScopeMatchFunc = func(addr Address) ScopeMatch
   127  
   128  // ExactScopeMatch checks if an address exactly
   129  // matches any of the specified scopes.
   130  func ExactScopeMatch(addr Address, addrScopes ...Scope) bool {
   131  	for _, scope := range addrScopes {
   132  		if addr.AddressScope() == scope {
   133  			return true
   134  		}
   135  	}
   136  	return false
   137  }
   138  
   139  // SortOrderMostPublic calculates the "weight" of the address to use when
   140  // sorting such that the most accessible addresses will appear first:
   141  // - public IPs first;
   142  // - hostnames after that, but "localhost" will be last if present;
   143  // - cloud-local next;
   144  // - fan-local next;
   145  // - machine-local next;
   146  // - link-local next;
   147  // - non-hostnames with unknown scope last.
   148  // Secondary addresses with otherwise equal weight will be sorted to come after
   149  // primary addresses, including host names *except* localhost.
   150  func SortOrderMostPublic(a Address) int {
   151  	order := 100
   152  
   153  	switch a.AddressScope() {
   154  	case ScopePublic:
   155  		order = 0
   156  		// Special case to ensure that these follow non-localhost host names.
   157  		if a.AddressIsSecondary() {
   158  			order = 10
   159  		}
   160  	case ScopeCloudLocal:
   161  		order = 30
   162  	case ScopeFanLocal:
   163  		order = 50
   164  	case ScopeMachineLocal:
   165  		order = 70
   166  	case ScopeLinkLocal:
   167  		order = 90
   168  	}
   169  
   170  	switch a.AddressType() {
   171  	case HostName:
   172  		order = 10
   173  		if a.Host() == "localhost" {
   174  			order = 20
   175  		}
   176  	case IPv6Address:
   177  		order++
   178  	case IPv4Address:
   179  	}
   180  
   181  	if a.AddressIsSecondary() {
   182  		order += 2
   183  	}
   184  
   185  	return order
   186  }
   187  
   188  // MachineAddress represents an address without associated space or provider
   189  // information. Addresses of this form will be supplied by an agent running
   190  // directly on a machine or container, or returned for requests where space
   191  // information is irrelevant to usage.
   192  type MachineAddress struct {
   193  	// Value is an IP address or hostname.
   194  	Value string
   195  
   196  	// Type indicates the form of the address value;
   197  	// IPv4, IPv6 or host-name.
   198  	Type AddressType
   199  
   200  	// Scope indicates the visibility of this address.
   201  	Scope Scope
   202  
   203  	// CIDR is used for IP addresses to indicate
   204  	// the subnet that they are part of.
   205  	CIDR string
   206  
   207  	// ConfigType denotes how this address was configured.
   208  	ConfigType AddressConfigType
   209  
   210  	// IsSecondary if true, indicates that this address is not the primary
   211  	// address associated with the network device.
   212  	IsSecondary bool
   213  }
   214  
   215  // Host returns the value for the host-name/IP address.
   216  func (a MachineAddress) Host() string {
   217  	return a.Value
   218  }
   219  
   220  // AddressType returns the type of the address.
   221  func (a MachineAddress) AddressType() AddressType {
   222  	return a.Type
   223  }
   224  
   225  // AddressScope returns the scope of the address.
   226  func (a MachineAddress) AddressScope() Scope {
   227  	return a.Scope
   228  }
   229  
   230  // AddressCIDR returns the subnet CIDR of the address.
   231  func (a MachineAddress) AddressCIDR() string {
   232  	return a.CIDR
   233  }
   234  
   235  // AddressConfigType returns the configuration method of the address.
   236  func (a MachineAddress) AddressConfigType() AddressConfigType {
   237  	return a.ConfigType
   238  }
   239  
   240  // AddressIsSecondary returns whether this address is not the
   241  // primary address associated with the network device.
   242  func (a MachineAddress) AddressIsSecondary() bool {
   243  	return a.IsSecondary
   244  }
   245  
   246  // GoString implements fmt.GoStringer.
   247  func (a MachineAddress) GoString() string {
   248  	return a.String()
   249  }
   250  
   251  // String returns the address value, prefixed with the scope if known.
   252  func (a MachineAddress) String() string {
   253  	var prefix string
   254  	if a.Scope != ScopeUnknown {
   255  		prefix = string(a.Scope) + ":"
   256  	}
   257  	return prefix + a.Value
   258  }
   259  
   260  // IP returns the net.IP representation of this address.
   261  func (a MachineAddress) IP() net.IP {
   262  	return net.ParseIP(a.Value)
   263  }
   264  
   265  // ValueWithMask returns the value of the address combined
   266  // with the subnet mask indicated by its CIDR.
   267  func (a MachineAddress) ValueWithMask() (string, error) {
   268  	// Returning a NotFound error preserves prior behaviour from when
   269  	// CIDRAddress was a method on InterfaceInfo.
   270  	// TODO (manadart 2021-03-16): Rethink this as we clean up InterfaceInfos
   271  	// and its corresponding wire type.
   272  	if a.Value == "" || a.CIDR == "" {
   273  		return "", errors.NotFoundf("address and CIDR pair (%q, %q)", a.Value, a.CIDR)
   274  	}
   275  
   276  	_, ipNet, err := net.ParseCIDR(a.CIDR)
   277  	if err != nil {
   278  		return "", errors.Trace(err)
   279  	}
   280  
   281  	ip := a.IP()
   282  	if ip == nil {
   283  		return "", errors.Errorf("cannot parse IP address %q", a.Value)
   284  	}
   285  
   286  	ipNet.IP = ip
   287  	return ipNet.String(), nil
   288  }
   289  
   290  // AsProviderAddress is used to construct a ProviderAddress
   291  // from a MachineAddress
   292  func (a MachineAddress) AsProviderAddress(options ...func(mutator ProviderAddressMutator)) ProviderAddress {
   293  	addr := ProviderAddress{MachineAddress: a}
   294  
   295  	for _, option := range options {
   296  		option(&addr)
   297  	}
   298  
   299  	return addr
   300  }
   301  
   302  // NewMachineAddress creates a new MachineAddress,
   303  // applying any supplied options to the result.
   304  func NewMachineAddress(value string, options ...func(AddressMutator)) MachineAddress {
   305  	addr := MachineAddress{
   306  		Value: value,
   307  		Type:  DeriveAddressType(value),
   308  		Scope: ScopeUnknown,
   309  	}
   310  
   311  	for _, option := range options {
   312  		option(&addr)
   313  	}
   314  
   315  	if addr.Scope == ScopeUnknown {
   316  		addr.Scope = deriveScope(addr)
   317  	}
   318  
   319  	return addr
   320  }
   321  
   322  // MachineAddresses is a slice of MachineAddress
   323  type MachineAddresses []MachineAddress
   324  
   325  // NewMachineAddresses is a convenience function to create addresses
   326  // from a variable number of string arguments, applying any supplied
   327  // options to each address
   328  func NewMachineAddresses(values []string, options ...func(AddressMutator)) MachineAddresses {
   329  	if len(values) == 0 {
   330  		return nil
   331  	}
   332  
   333  	addrs := make(MachineAddresses, len(values))
   334  	for i, value := range values {
   335  		addrs[i] = NewMachineAddress(value, options...)
   336  	}
   337  	return addrs
   338  }
   339  
   340  // AsProviderAddresses is used to construct ProviderAddresses
   341  // element-wise from MachineAddresses
   342  func (as MachineAddresses) AsProviderAddresses(options ...func(mutator ProviderAddressMutator)) ProviderAddresses {
   343  	if len(as) == 0 {
   344  		return nil
   345  	}
   346  
   347  	addrs := make(ProviderAddresses, len(as))
   348  	for i, addr := range as {
   349  		addrs[i] = addr.AsProviderAddress(options...)
   350  	}
   351  	return addrs
   352  }
   353  
   354  // AllMatchingScope returns the addresses that satisfy
   355  // the input scope matching function.
   356  func (as MachineAddresses) AllMatchingScope(getMatcher ScopeMatchFunc) MachineAddresses {
   357  	return allMatchingScope(as, getMatcher)
   358  }
   359  
   360  // Values transforms the MachineAddresses to a string slice
   361  // containing their raw IP values.
   362  func (as MachineAddresses) Values() []string {
   363  	return toStrings(as)
   364  }
   365  
   366  // deriveScope attempts to derive the network scope from an address'
   367  // type and value, returning the original network scope if no
   368  // deduction can be made.
   369  func deriveScope(addr MachineAddress) Scope {
   370  	if addr.Type == HostName {
   371  		return addr.Scope
   372  	}
   373  	ip := net.ParseIP(addr.Value)
   374  	if ip == nil {
   375  		return addr.Scope
   376  	}
   377  	if ip.IsLoopback() {
   378  		return ScopeMachineLocal
   379  	}
   380  	if isIPv4PrivateNetworkAddress(addr.Type, ip) ||
   381  		isIPv6UniqueLocalAddress(addr.Type, ip) {
   382  		return ScopeCloudLocal
   383  	}
   384  	if isIPv4ReservedEAddress(addr.Type, ip) {
   385  		return ScopeFanLocal
   386  	}
   387  
   388  	if ip.IsLinkLocalMulticast() ||
   389  		ip.IsLinkLocalUnicast() ||
   390  		ip.IsInterfaceLocalMulticast() {
   391  		return ScopeLinkLocal
   392  	}
   393  	if ip.IsGlobalUnicast() {
   394  		return ScopePublic
   395  	}
   396  	return addr.Scope
   397  }
   398  
   399  func isIPv4PrivateNetworkAddress(addrType AddressType, ip net.IP) bool {
   400  	if addrType != IPv4Address {
   401  		return false
   402  	}
   403  	return classAPrivate.Contains(ip) ||
   404  		classBPrivate.Contains(ip) ||
   405  		classCPrivate.Contains(ip)
   406  }
   407  
   408  func isIPv4ReservedEAddress(addrType AddressType, ip net.IP) bool {
   409  	if addrType != IPv4Address {
   410  		return false
   411  	}
   412  	return classEReserved.Contains(ip)
   413  }
   414  
   415  func isIPv6UniqueLocalAddress(addrType AddressType, ip net.IP) bool {
   416  	if addrType != IPv6Address {
   417  		return false
   418  	}
   419  	return ipv6UniqueLocal.Contains(ip)
   420  }
   421  
   422  // InterfaceAddrs is patched for tests.
   423  var InterfaceAddrs = func() ([]net.Addr, error) {
   424  	return net.InterfaceAddrs()
   425  }
   426  
   427  // IsLocalAddress returns true if the provided IP address equals to one of the
   428  // local IP addresses.
   429  func IsLocalAddress(ip net.IP) (bool, error) {
   430  	addrs, err := InterfaceAddrs()
   431  	if err != nil {
   432  		return false, errors.Trace(err)
   433  	}
   434  
   435  	for _, addr := range addrs {
   436  		localIP, _, err := net.ParseCIDR(addr.String())
   437  		if err != nil {
   438  			continue
   439  		}
   440  		if localIP.To4() != nil || localIP.To16() != nil {
   441  			if ip.Equal(localIP) {
   442  				return true, nil
   443  			}
   444  		}
   445  	}
   446  	return false, nil
   447  }
   448  
   449  // ProviderAddress represents an address supplied by provider logic.
   450  // It can include the provider's knowledge of the space in which the
   451  // address resides.
   452  type ProviderAddress struct {
   453  	MachineAddress
   454  
   455  	// SpaceName is the space in which this address resides
   456  	SpaceName SpaceName
   457  
   458  	// ProviderSpaceID is the provider's ID for the space this address is in
   459  	ProviderSpaceID Id
   460  
   461  	// ProviderID is the ID of this address's provider
   462  	ProviderID Id
   463  
   464  	// ProviderSubnetID is the provider's ID for the subnet this address is in
   465  	ProviderSubnetID Id
   466  
   467  	// ProviderVLANID is the provider's ID for the VLAN this address is in
   468  	ProviderVLANID Id
   469  
   470  	// VLANTag is the tag associated with this address's VLAN
   471  	VLANTag int
   472  }
   473  
   474  // GoString implements fmt.GoStringer.
   475  func (a ProviderAddress) GoString() string {
   476  	return a.String()
   477  }
   478  
   479  // String returns a string representation of the address, in the form:
   480  // `<scope>:<address-value>@<space-name>(id:<space-provider-id)`; for example:
   481  //
   482  //	public:c2-54-226-162-124.compute-1.amazonaws.com@public-api(id:42)
   483  //
   484  // If the SpaceName is blank, the "@<space-name>" suffix will be omitted.
   485  // Finally, if the ProviderSpaceID is empty the suffix
   486  // "(id:<space-provider-id>)" part will be omitted as well.
   487  func (a ProviderAddress) String() string {
   488  	var buf bytes.Buffer
   489  	buf.WriteString(a.MachineAddress.String())
   490  
   491  	var spaceFound bool
   492  	if a.SpaceName != "" {
   493  		spaceFound = true
   494  		buf.WriteByte('@')
   495  		buf.WriteString(string(a.SpaceName))
   496  	}
   497  	if a.ProviderSpaceID != "" {
   498  		if !spaceFound {
   499  			buf.WriteByte('@')
   500  		}
   501  		buf.WriteString(fmt.Sprintf("(id:%v)", string(a.ProviderSpaceID)))
   502  	}
   503  
   504  	return buf.String()
   505  }
   506  
   507  // ProviderAddresses is a slice of ProviderAddress
   508  // supporting conversion to SpaceAddresses.
   509  type ProviderAddresses []ProviderAddress
   510  
   511  // Values transforms the ProviderAddresses to a string slice containing
   512  // their raw IP values.
   513  func (pas ProviderAddresses) Values() []string {
   514  	return toStrings(pas)
   515  }
   516  
   517  // ToSpaceAddresses transforms the ProviderAddresses to SpaceAddresses by using
   518  // the input lookup to get a space ID from the name or the CIDR.
   519  func (pas ProviderAddresses) ToSpaceAddresses(lookup SpaceLookup) (SpaceAddresses, error) {
   520  	if pas == nil {
   521  		return nil, nil
   522  	}
   523  
   524  	var spaceInfos SpaceInfos
   525  	if len(pas) > 0 {
   526  		var err error
   527  		if spaceInfos, err = lookup.AllSpaceInfos(); err != nil {
   528  			return nil, errors.Trace(err)
   529  		}
   530  	}
   531  
   532  	sas := make(SpaceAddresses, len(pas))
   533  	for i, pa := range pas {
   534  		sas[i] = SpaceAddress{MachineAddress: pa.MachineAddress}
   535  
   536  		// If the provider explicitly sets the space, i.e. MAAS, prefer the name.
   537  		if pa.SpaceName != "" {
   538  			info := spaceInfos.GetByName(string(pa.SpaceName))
   539  			if info == nil {
   540  				return nil, errors.NotFoundf("space with name %q", pa.SpaceName)
   541  			}
   542  			sas[i].SpaceID = info.ID
   543  			continue
   544  		}
   545  
   546  		// Otherwise attempt to look up the CIDR.
   547  		sInfo, err := spaceInfos.InferSpaceFromCIDRAndSubnetID(pa.CIDR, string(pa.ProviderSubnetID))
   548  		if err != nil {
   549  			logger.Debugf("no matching subnet for CIDR %q and provider ID %q", pa.CIDR, pa.ProviderSubnetID)
   550  			continue
   551  		}
   552  		sas[i].SpaceID = sInfo.ID
   553  	}
   554  	return sas, nil
   555  }
   556  
   557  // OneMatchingScope returns the address that best satisfies the input scope
   558  // matching function. The boolean return indicates if a match was found.
   559  func (pas ProviderAddresses) OneMatchingScope(getMatcher ScopeMatchFunc) (ProviderAddress, bool) {
   560  	indexes := indexesForScope(pas, getMatcher)
   561  	if len(indexes) == 0 {
   562  		return ProviderAddress{}, false
   563  	}
   564  	addr := pas[indexes[0]]
   565  	logger.Debugf("selected %q as address, using scope %q", addr.Value, addr.Scope)
   566  	return addr, true
   567  }
   568  
   569  // SpaceAddress represents the location of a machine, including metadata
   570  // about what kind of location the address describes.
   571  // This is a server-side type that may include a space reference.
   572  // It is used in logic for filtering addresses by space.
   573  type SpaceAddress struct {
   574  	MachineAddress
   575  	SpaceID string
   576  }
   577  
   578  // GoString implements fmt.GoStringer.
   579  func (a SpaceAddress) GoString() string {
   580  	return a.String()
   581  }
   582  
   583  // String returns a string representation of the address, in the form:
   584  // `<scope>:<address-value>@space:<space-id>`; for example:
   585  //
   586  //	public:c2-54-226-162-124.compute-1.amazonaws.com@space:1
   587  //
   588  // If the Space ID is empty, the @space:<space-id> suffix will be omitted.
   589  func (a SpaceAddress) String() string {
   590  	var buf bytes.Buffer
   591  	buf.WriteString(a.MachineAddress.String())
   592  
   593  	if a.SpaceID != "" {
   594  		buf.WriteString("@space:")
   595  		buf.WriteString(a.SpaceID)
   596  	}
   597  
   598  	return buf.String()
   599  }
   600  
   601  // NewSpaceAddress creates a new SpaceAddress,
   602  // applying any supplied options to the result.
   603  func NewSpaceAddress(value string, options ...func(mutator AddressMutator)) SpaceAddress {
   604  	return SpaceAddress{MachineAddress: NewMachineAddress(value, options...)}
   605  }
   606  
   607  // SpaceAddresses is a slice of SpaceAddress
   608  // supporting conversion to ProviderAddresses.
   609  type SpaceAddresses []SpaceAddress
   610  
   611  // NewSpaceAddresses is a convenience function to create addresses
   612  // from a variable number of string arguments.
   613  func NewSpaceAddresses(inAddresses ...string) (outAddresses SpaceAddresses) {
   614  	outAddresses = make(SpaceAddresses, len(inAddresses))
   615  	for i, address := range inAddresses {
   616  		outAddresses[i] = NewSpaceAddress(address)
   617  	}
   618  	return outAddresses
   619  }
   620  
   621  // Values returns a slice of strings containing the IP/host-name of each of
   622  // the receiver addresses.
   623  func (sas SpaceAddresses) Values() []string {
   624  	return toStrings(sas)
   625  }
   626  
   627  // ToProviderAddresses transforms the SpaceAddresses to ProviderAddresses by using
   628  // the input lookup for conversion of space ID to space info.
   629  func (sas SpaceAddresses) ToProviderAddresses(lookup SpaceLookup) (ProviderAddresses, error) {
   630  	if sas == nil {
   631  		return nil, nil
   632  	}
   633  
   634  	var spaces SpaceInfos
   635  	if len(sas) > 0 {
   636  		var err error
   637  		if spaces, err = lookup.AllSpaceInfos(); err != nil {
   638  			return nil, errors.Trace(err)
   639  		}
   640  	}
   641  
   642  	pas := make(ProviderAddresses, len(sas))
   643  	for i, sa := range sas {
   644  		pas[i] = ProviderAddress{MachineAddress: sa.MachineAddress}
   645  		if sa.SpaceID != "" {
   646  			info := spaces.GetByID(sa.SpaceID)
   647  			if info == nil {
   648  				return nil, errors.NotFoundf("space with ID %q", sa.SpaceID)
   649  			}
   650  			pas[i].SpaceName = info.Name
   651  			pas[i].ProviderSpaceID = info.ProviderId
   652  		}
   653  	}
   654  	return pas, nil
   655  }
   656  
   657  // InSpaces returns the SpaceAddresses that are in the input spaces.
   658  func (sas SpaceAddresses) InSpaces(spaces ...SpaceInfo) (SpaceAddresses, bool) {
   659  	if len(spaces) == 0 {
   660  		logger.Errorf("addresses not filtered - no spaces given.")
   661  		return sas, false
   662  	}
   663  
   664  	spaceInfos := SpaceInfos(spaces)
   665  	var selectedAddresses SpaceAddresses
   666  	for _, addr := range sas {
   667  		if space := spaceInfos.GetByID(addr.SpaceID); space != nil {
   668  			logger.Debugf("selected %q as an address in space %q", addr.Value, space.Name)
   669  			selectedAddresses = append(selectedAddresses, addr)
   670  		}
   671  	}
   672  
   673  	if len(selectedAddresses) > 0 {
   674  		return selectedAddresses, true
   675  	}
   676  
   677  	logger.Errorf("no addresses found in spaces %s", spaceInfos)
   678  	return sas, false
   679  }
   680  
   681  // OneMatchingScope returns the address that best satisfies the input scope
   682  // matching function. The boolean return indicates if a match was found.
   683  func (sas SpaceAddresses) OneMatchingScope(getMatcher ScopeMatchFunc) (SpaceAddress, bool) {
   684  	addrs := sas.AllMatchingScope(getMatcher)
   685  	if len(addrs) == 0 {
   686  		return SpaceAddress{}, false
   687  	}
   688  	return addrs[0], true
   689  }
   690  
   691  // AllMatchingScope returns the addresses that satisfy
   692  // the input scope matching function.
   693  func (sas SpaceAddresses) AllMatchingScope(getMatcher ScopeMatchFunc) SpaceAddresses {
   694  	return allMatchingScope(sas, getMatcher)
   695  }
   696  
   697  // EqualTo returns true if this set of SpaceAddresses is equal to other.
   698  func (sas SpaceAddresses) EqualTo(other SpaceAddresses) bool {
   699  	if len(sas) != len(other) {
   700  		return false
   701  	}
   702  
   703  	sort.Sort(sas)
   704  	sort.Sort(other)
   705  	for i := 0; i < len(sas); i++ {
   706  		if sas[i].String() != other[i].String() {
   707  			return false
   708  		}
   709  	}
   710  
   711  	return true
   712  }
   713  
   714  func (sas SpaceAddresses) Len() int      { return len(sas) }
   715  func (sas SpaceAddresses) Swap(i, j int) { sas[i], sas[j] = sas[j], sas[i] }
   716  func (sas SpaceAddresses) Less(i, j int) bool {
   717  	addr1 := sas[i]
   718  	addr2 := sas[j]
   719  	order1 := SortOrderMostPublic(addr1)
   720  	order2 := SortOrderMostPublic(addr2)
   721  	if order1 == order2 {
   722  		return addr1.Value < addr2.Value
   723  	}
   724  	return order1 < order2
   725  }
   726  
   727  // DeriveAddressType attempts to detect the type of address given.
   728  func DeriveAddressType(value string) AddressType {
   729  	ip := net.ParseIP(value)
   730  	switch {
   731  	case ip == nil:
   732  		// TODO(gz): Check value is a valid hostname
   733  		return HostName
   734  	case ip.To4() != nil:
   735  		return IPv4Address
   736  	case ip.To16() != nil:
   737  		return IPv6Address
   738  	default:
   739  		panic("Unknown form of IP address")
   740  	}
   741  }
   742  
   743  // ScopeMatchPublic is an address scope matching function for determining the
   744  // extent to which the input address' scope satisfies a requirement for public
   745  // accessibility.
   746  func ScopeMatchPublic(addr Address) ScopeMatch {
   747  	switch addr.AddressScope() {
   748  	case ScopePublic:
   749  		if addr.AddressType() == IPv4Address {
   750  			return exactScopeIPv4
   751  		}
   752  		return exactScope
   753  	case ScopeCloudLocal:
   754  		if addr.AddressType() == IPv4Address {
   755  			return firstFallbackScopeIPv4
   756  		}
   757  		return firstFallbackScope
   758  	case ScopeFanLocal, ScopeUnknown:
   759  		if addr.AddressType() == IPv4Address {
   760  			return secondFallbackScopeIPv4
   761  		}
   762  		return secondFallbackScope
   763  	}
   764  	return invalidScope
   765  }
   766  
   767  func ScopeMatchMachineOrCloudLocal(addr Address) ScopeMatch {
   768  	if addr.AddressScope() == ScopeMachineLocal {
   769  		if addr.AddressType() == IPv4Address {
   770  			return exactScopeIPv4
   771  		}
   772  		return exactScope
   773  	}
   774  	return ScopeMatchCloudLocal(addr)
   775  }
   776  
   777  // ScopeMatchCloudLocal is an address scope matching function for determining
   778  // the extent to which the input address' scope satisfies a requirement for
   779  // accessibility from within the local cloud.
   780  // Machine-only addresses do not satisfy this matcher.
   781  func ScopeMatchCloudLocal(addr Address) ScopeMatch {
   782  	switch addr.AddressScope() {
   783  	case ScopeCloudLocal:
   784  		if addr.AddressType() == IPv4Address {
   785  			return exactScopeIPv4
   786  		}
   787  		return exactScope
   788  	case ScopeFanLocal:
   789  		if addr.AddressType() == IPv4Address {
   790  			return firstFallbackScopeIPv4
   791  		}
   792  		return firstFallbackScope
   793  	case ScopePublic, ScopeUnknown:
   794  		if addr.AddressType() == IPv4Address {
   795  			return secondFallbackScopeIPv4
   796  		}
   797  		return secondFallbackScope
   798  	}
   799  	return invalidScope
   800  }
   801  
   802  // MergedAddresses provides a single list of addresses without duplicates
   803  // suitable for returning as an address list for a machine.
   804  // TODO (cherylj) Add explicit unit tests - tracked with bug #1544158
   805  func MergedAddresses(machineAddresses, providerAddresses []SpaceAddress) []SpaceAddress {
   806  	merged := make([]SpaceAddress, 0, len(providerAddresses)+len(machineAddresses))
   807  	providerValues := set.NewStrings()
   808  	for _, address := range providerAddresses {
   809  		// Older versions of Juju may have stored an empty address so ignore it here.
   810  		if address.Value == "" || providerValues.Contains(address.Value) {
   811  			continue
   812  		}
   813  		providerValues.Add(address.Value)
   814  		merged = append(merged, address)
   815  	}
   816  	for _, address := range machineAddresses {
   817  		if !providerValues.Contains(address.Value) {
   818  			merged = append(merged, address)
   819  		}
   820  	}
   821  	return merged
   822  }
   823  
   824  // CIDRAddressType returns back an AddressType to indicate whether the supplied
   825  // CIDR corresponds to an IPV4 or IPV6 range. An error will be returned if a
   826  // non-valid CIDR is provided.
   827  //
   828  // Caveat: if the provided CIDR corresponds to an IPV6 range with a 4in6
   829  // prefix, the function will classify it as an IPV4 address. This is a known
   830  // limitation of the go stdlib IP parsing code but it's not something that we
   831  // are likely to encounter in the wild so there is no need to add extra logic
   832  // to work around it.
   833  func CIDRAddressType(cidr string) (AddressType, error) {
   834  	_, netIP, err := net.ParseCIDR(cidr)
   835  	if err != nil {
   836  		return "", err
   837  	}
   838  
   839  	if netIP.IP.To4() != nil {
   840  		return IPv4Address, nil
   841  	}
   842  
   843  	return IPv6Address, nil
   844  }
   845  
   846  // NetworkCIDRFromIPAndMask constructs a CIDR for a network by applying the
   847  // provided netmask to the specified address (can be either a host or network
   848  // address) and formatting the result as a CIDR.
   849  //
   850  // For example, passing 10.0.0.4 and a /24 mask yields 10.0.0.0/24.
   851  func NetworkCIDRFromIPAndMask(ip net.IP, netmask net.IPMask) string {
   852  	if ip == nil || netmask == nil {
   853  		return ""
   854  	}
   855  
   856  	hostBits, _ := netmask.Size()
   857  	return fmt.Sprintf("%s/%d", ip.Mask(netmask), hostBits)
   858  }
   859  
   860  // SpaceAddressCandidate describes property methods required
   861  // for conversion to sortable space addresses.
   862  type SpaceAddressCandidate interface {
   863  	Value() string
   864  	ConfigMethod() AddressConfigType
   865  	SubnetCIDR() string
   866  	IsSecondary() bool
   867  }
   868  
   869  // ConvertToSpaceAddress returns a SpaceAddress representing the
   870  // input candidate address, by using the input subnet lookup to
   871  // associate the address with a space..
   872  func ConvertToSpaceAddress(addr SpaceAddressCandidate, lookup SubnetLookup) (SpaceAddress, error) {
   873  	subnets, err := lookup.AllSubnetInfos()
   874  	if err != nil {
   875  		return SpaceAddress{}, errors.Trace(err)
   876  	}
   877  
   878  	cidr := addr.SubnetCIDR()
   879  
   880  	spaceAddr := SpaceAddress{
   881  		MachineAddress: NewMachineAddress(
   882  			addr.Value(),
   883  			WithCIDR(cidr),
   884  			WithConfigType(addr.ConfigMethod()),
   885  			WithSecondary(addr.IsSecondary()),
   886  		),
   887  	}
   888  
   889  	// Attempt to set the space ID based on the subnet.
   890  	if cidr != "" {
   891  		allMatching, err := subnets.GetByCIDR(cidr)
   892  		if err != nil {
   893  			return SpaceAddress{}, errors.Trace(err)
   894  		}
   895  
   896  		// This only holds true while CIDRs uniquely identify subnets.
   897  		if len(allMatching) != 0 {
   898  			spaceAddr.SpaceID = allMatching[0].SpaceID
   899  		}
   900  	}
   901  
   902  	return spaceAddr, nil
   903  }
   904  
   905  // noAddress represents an error when an address is requested but not available.
   906  type noAddress struct {
   907  	errors.Err
   908  }
   909  
   910  // NoAddressError returns an error which satisfies IsNoAddressError(). The given
   911  // addressKind specifies what kind of address(es) is(are) missing, usually
   912  // "private" or "public".
   913  func NoAddressError(addressKind string) error {
   914  	newErr := errors.NewErr("no %s address(es)", addressKind)
   915  	newErr.SetLocation(1)
   916  	return &noAddress{newErr}
   917  }
   918  
   919  // IsNoAddressError reports whether err was created with NoAddressError().
   920  func IsNoAddressError(err error) bool {
   921  	err = errors.Cause(err)
   922  	_, ok := err.(*noAddress)
   923  	return ok
   924  }
   925  
   926  // toStrings returns the IP addresses in string form for input
   927  // that is a slice of types implementing the Address interface.
   928  func toStrings[T Address](addrs []T) []string {
   929  	if addrs == nil {
   930  		return nil
   931  	}
   932  
   933  	ips := make([]string, len(addrs))
   934  	for i, addr := range addrs {
   935  		ips[i] = addr.Host()
   936  	}
   937  	return ips
   938  }
   939  
   940  func allMatchingScope[T Address](addrs []T, getMatcher ScopeMatchFunc) []T {
   941  	indexes := indexesForScope(addrs, getMatcher)
   942  	if len(indexes) == 0 {
   943  		return nil
   944  	}
   945  	out := make([]T, len(indexes))
   946  	for i, index := range indexes {
   947  		out[i] = addrs[index]
   948  	}
   949  	return out
   950  }
   951  
   952  // indexesForScope returns the indexes of the addresses with the best
   953  // matching scope and type (according to the matchFunc).
   954  // An empty slice is returned if there were no suitable addresses.
   955  func indexesForScope[T Address](addrs []T, matchFunc ScopeMatchFunc) []int {
   956  	matches := filterAndCollateAddressIndexes(addrs, matchFunc)
   957  
   958  	for _, matchType := range scopeMatchHierarchy() {
   959  		indexes, ok := matches[matchType]
   960  		if ok && len(indexes) > 0 {
   961  			return indexes
   962  		}
   963  	}
   964  	return nil
   965  }
   966  
   967  // indexesByScopeMatch filters address indexes by matching scope,
   968  // then returns them in descending order of best match.
   969  func indexesByScopeMatch[T Address](addrs []T, matchFunc ScopeMatchFunc) []int {
   970  	matches := filterAndCollateAddressIndexes(addrs, matchFunc)
   971  
   972  	var prioritized []int
   973  	for _, matchType := range scopeMatchHierarchy() {
   974  		indexes, ok := matches[matchType]
   975  		if ok && len(indexes) > 0 {
   976  			prioritized = append(prioritized, indexes...)
   977  		}
   978  	}
   979  	return prioritized
   980  }
   981  
   982  // filterAndCollateAddressIndexes filters address indexes using the input scope
   983  // matching function, then returns the results grouped by scope match quality.
   984  // Invalid results are omitted.
   985  func filterAndCollateAddressIndexes[T Address](addrs []T, matchFunc ScopeMatchFunc) map[ScopeMatch][]int {
   986  	matches := make(map[ScopeMatch][]int)
   987  	for i, addr := range addrs {
   988  		matchType := matchFunc(addr)
   989  		if matchType != invalidScope {
   990  			matches[matchType] = append(matches[matchType], i)
   991  		}
   992  	}
   993  	return matches
   994  }
   995  
   996  func scopeMatchHierarchy() []ScopeMatch {
   997  	return []ScopeMatch{
   998  		exactScopeIPv4, exactScope,
   999  		firstFallbackScopeIPv4, firstFallbackScope,
  1000  		secondFallbackScopeIPv4, secondFallbackScope,
  1001  	}
  1002  }