github.com/gdevillele/moby@v1.13.0/daemon/cluster/listen_addr.go (about)

     1  package cluster
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  )
     8  
     9  var (
    10  	errNoSuchInterface         = errors.New("no such interface")
    11  	errNoIP                    = errors.New("could not find the system's IP address")
    12  	errMustSpecifyListenAddr   = errors.New("must specify a listening address because the address to advertise is not recognized as a system address, and a system's IP address to use could not be uniquely identified")
    13  	errBadListenAddr           = errors.New("listen address must be an IP address or network interface (with optional port number)")
    14  	errBadAdvertiseAddr        = errors.New("advertise address must be a non-zero IP address or network interface (with optional port number)")
    15  	errBadDefaultAdvertiseAddr = errors.New("default advertise address must be a non-zero IP address or network interface (without a port number)")
    16  )
    17  
    18  func resolveListenAddr(specifiedAddr string) (string, string, error) {
    19  	specifiedHost, specifiedPort, err := net.SplitHostPort(specifiedAddr)
    20  	if err != nil {
    21  		return "", "", fmt.Errorf("could not parse listen address %s", specifiedAddr)
    22  	}
    23  
    24  	// Does the host component match any of the interface names on the
    25  	// system? If so, use the address from that interface.
    26  	interfaceAddr, err := resolveInterfaceAddr(specifiedHost)
    27  	if err == nil {
    28  		return interfaceAddr.String(), specifiedPort, nil
    29  	}
    30  	if err != errNoSuchInterface {
    31  		return "", "", err
    32  	}
    33  
    34  	// If it's not an interface, it must be an IP (for now)
    35  	if net.ParseIP(specifiedHost) == nil {
    36  		return "", "", errBadListenAddr
    37  	}
    38  
    39  	return specifiedHost, specifiedPort, nil
    40  }
    41  
    42  func (c *Cluster) resolveAdvertiseAddr(advertiseAddr, listenAddrPort string) (string, string, error) {
    43  	// Approach:
    44  	// - If an advertise address is specified, use that. Resolve the
    45  	//   interface's address if an interface was specified in
    46  	//   advertiseAddr. Fill in the port from listenAddrPort if necessary.
    47  	// - If DefaultAdvertiseAddr is not empty, use that with the port from
    48  	//   listenAddrPort. Resolve the interface's address from
    49  	//   if an interface name was specified in DefaultAdvertiseAddr.
    50  	// - Otherwise, try to autodetect the system's address. Use the port in
    51  	//   listenAddrPort with this address if autodetection succeeds.
    52  
    53  	if advertiseAddr != "" {
    54  		advertiseHost, advertisePort, err := net.SplitHostPort(advertiseAddr)
    55  		if err != nil {
    56  			// Not a host:port specification
    57  			advertiseHost = advertiseAddr
    58  			advertisePort = listenAddrPort
    59  		}
    60  
    61  		// Does the host component match any of the interface names on the
    62  		// system? If so, use the address from that interface.
    63  		interfaceAddr, err := resolveInterfaceAddr(advertiseHost)
    64  		if err == nil {
    65  			return interfaceAddr.String(), advertisePort, nil
    66  		}
    67  		if err != errNoSuchInterface {
    68  			return "", "", err
    69  		}
    70  
    71  		// If it's not an interface, it must be an IP (for now)
    72  		if ip := net.ParseIP(advertiseHost); ip == nil || ip.IsUnspecified() {
    73  			return "", "", errBadAdvertiseAddr
    74  		}
    75  
    76  		return advertiseHost, advertisePort, nil
    77  	}
    78  
    79  	if c.config.DefaultAdvertiseAddr != "" {
    80  		// Does the default advertise address component match any of the
    81  		// interface names on the system? If so, use the address from
    82  		// that interface.
    83  		interfaceAddr, err := resolveInterfaceAddr(c.config.DefaultAdvertiseAddr)
    84  		if err == nil {
    85  			return interfaceAddr.String(), listenAddrPort, nil
    86  		}
    87  		if err != errNoSuchInterface {
    88  			return "", "", err
    89  		}
    90  
    91  		// If it's not an interface, it must be an IP (for now)
    92  		if ip := net.ParseIP(c.config.DefaultAdvertiseAddr); ip == nil || ip.IsUnspecified() {
    93  			return "", "", errBadDefaultAdvertiseAddr
    94  		}
    95  
    96  		return c.config.DefaultAdvertiseAddr, listenAddrPort, nil
    97  	}
    98  
    99  	systemAddr, err := c.resolveSystemAddr()
   100  	if err != nil {
   101  		return "", "", err
   102  	}
   103  	return systemAddr.String(), listenAddrPort, nil
   104  }
   105  
   106  func resolveInterfaceAddr(specifiedInterface string) (net.IP, error) {
   107  	// Use a specific interface's IP address.
   108  	intf, err := net.InterfaceByName(specifiedInterface)
   109  	if err != nil {
   110  		return nil, errNoSuchInterface
   111  	}
   112  
   113  	addrs, err := intf.Addrs()
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	var interfaceAddr4, interfaceAddr6 net.IP
   119  
   120  	for _, addr := range addrs {
   121  		ipAddr, ok := addr.(*net.IPNet)
   122  
   123  		if ok {
   124  			if ipAddr.IP.To4() != nil {
   125  				// IPv4
   126  				if interfaceAddr4 != nil {
   127  					return nil, fmt.Errorf("interface %s has more than one IPv4 address (%s and %s)", specifiedInterface, interfaceAddr4, ipAddr.IP)
   128  				}
   129  				interfaceAddr4 = ipAddr.IP
   130  			} else {
   131  				// IPv6
   132  				if interfaceAddr6 != nil {
   133  					return nil, fmt.Errorf("interface %s has more than one IPv6 address (%s and %s)", specifiedInterface, interfaceAddr6, ipAddr.IP)
   134  				}
   135  				interfaceAddr6 = ipAddr.IP
   136  			}
   137  		}
   138  	}
   139  
   140  	if interfaceAddr4 == nil && interfaceAddr6 == nil {
   141  		return nil, fmt.Errorf("interface %s has no usable IPv4 or IPv6 address", specifiedInterface)
   142  	}
   143  
   144  	// In the case that there's exactly one IPv4 address
   145  	// and exactly one IPv6 address, favor IPv4 over IPv6.
   146  	if interfaceAddr4 != nil {
   147  		return interfaceAddr4, nil
   148  	}
   149  	return interfaceAddr6, nil
   150  }
   151  
   152  func (c *Cluster) resolveSystemAddrViaSubnetCheck() (net.IP, error) {
   153  	// Use the system's only IP address, or fail if there are
   154  	// multiple addresses to choose from. Skip interfaces which
   155  	// are managed by docker via subnet check.
   156  	interfaces, err := net.Interfaces()
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	var systemAddr net.IP
   162  	var systemInterface string
   163  
   164  	// List Docker-managed subnets
   165  	v4Subnets := c.config.NetworkSubnetsProvider.V4Subnets()
   166  	v6Subnets := c.config.NetworkSubnetsProvider.V6Subnets()
   167  
   168  ifaceLoop:
   169  	for _, intf := range interfaces {
   170  		// Skip inactive interfaces and loopback interfaces
   171  		if (intf.Flags&net.FlagUp == 0) || (intf.Flags&net.FlagLoopback) != 0 {
   172  			continue
   173  		}
   174  
   175  		addrs, err := intf.Addrs()
   176  		if err != nil {
   177  			continue
   178  		}
   179  
   180  		var interfaceAddr4, interfaceAddr6 net.IP
   181  
   182  		for _, addr := range addrs {
   183  			ipAddr, ok := addr.(*net.IPNet)
   184  
   185  			// Skip loopback and link-local addresses
   186  			if !ok || !ipAddr.IP.IsGlobalUnicast() {
   187  				continue
   188  			}
   189  
   190  			if ipAddr.IP.To4() != nil {
   191  				// IPv4
   192  
   193  				// Ignore addresses in subnets that are managed by Docker.
   194  				for _, subnet := range v4Subnets {
   195  					if subnet.Contains(ipAddr.IP) {
   196  						continue ifaceLoop
   197  					}
   198  				}
   199  
   200  				if interfaceAddr4 != nil {
   201  					return nil, errMultipleIPs(intf.Name, intf.Name, interfaceAddr4, ipAddr.IP)
   202  				}
   203  
   204  				interfaceAddr4 = ipAddr.IP
   205  			} else {
   206  				// IPv6
   207  
   208  				// Ignore addresses in subnets that are managed by Docker.
   209  				for _, subnet := range v6Subnets {
   210  					if subnet.Contains(ipAddr.IP) {
   211  						continue ifaceLoop
   212  					}
   213  				}
   214  
   215  				if interfaceAddr6 != nil {
   216  					return nil, errMultipleIPs(intf.Name, intf.Name, interfaceAddr6, ipAddr.IP)
   217  				}
   218  
   219  				interfaceAddr6 = ipAddr.IP
   220  			}
   221  		}
   222  
   223  		// In the case that this interface has exactly one IPv4 address
   224  		// and exactly one IPv6 address, favor IPv4 over IPv6.
   225  		if interfaceAddr4 != nil {
   226  			if systemAddr != nil {
   227  				return nil, errMultipleIPs(systemInterface, intf.Name, systemAddr, interfaceAddr4)
   228  			}
   229  			systemAddr = interfaceAddr4
   230  			systemInterface = intf.Name
   231  		} else if interfaceAddr6 != nil {
   232  			if systemAddr != nil {
   233  				return nil, errMultipleIPs(systemInterface, intf.Name, systemAddr, interfaceAddr6)
   234  			}
   235  			systemAddr = interfaceAddr6
   236  			systemInterface = intf.Name
   237  		}
   238  	}
   239  
   240  	if systemAddr == nil {
   241  		return nil, errNoIP
   242  	}
   243  
   244  	return systemAddr, nil
   245  }
   246  
   247  func listSystemIPs() []net.IP {
   248  	interfaces, err := net.Interfaces()
   249  	if err != nil {
   250  		return nil
   251  	}
   252  
   253  	var systemAddrs []net.IP
   254  
   255  	for _, intf := range interfaces {
   256  		addrs, err := intf.Addrs()
   257  		if err != nil {
   258  			continue
   259  		}
   260  
   261  		for _, addr := range addrs {
   262  			ipAddr, ok := addr.(*net.IPNet)
   263  
   264  			if ok {
   265  				systemAddrs = append(systemAddrs, ipAddr.IP)
   266  			}
   267  		}
   268  	}
   269  
   270  	return systemAddrs
   271  }
   272  
   273  func errMultipleIPs(interfaceA, interfaceB string, addrA, addrB net.IP) error {
   274  	if interfaceA == interfaceB {
   275  		return fmt.Errorf("could not choose an IP address to advertise since this system has multiple addresses on interface %s (%s and %s)", interfaceA, addrA, addrB)
   276  	}
   277  	return fmt.Errorf("could not choose an IP address to advertise since this system has multiple addresses on different interfaces (%s on %s and %s on %s)", addrA, interfaceA, addrB, interfaceB)
   278  }