/daemon/cluster/listen_addr.go
Go | 183 lines | 129 code | 29 blank | 25 comment | 56 complexity | 382c8a4d810982210a816ce1c3dc9478 MD5 | raw file
- package cluster
- import (
- "errors"
- "fmt"
- "net"
- )
- var (
- errNoSuchInterface = errors.New("no such interface")
- errNoIP = errors.New("could not find the system's IP address")
- errMustSpecifyListenAddr = errors.New("must specify a listening address because the address to advertise is not recognized as a system address")
- errBadListenAddr = errors.New("listen address must be an IP address or network interface (with optional port number)")
- errBadAdvertiseAddr = errors.New("advertise address must be an IP address or network interface (with optional port number)")
- errBadDefaultAdvertiseAddr = errors.New("default advertise address must be an IP address or network interface (without a port number)")
- )
- func resolveListenAddr(specifiedAddr string) (string, string, error) {
- specifiedHost, specifiedPort, err := net.SplitHostPort(specifiedAddr)
- if err != nil {
- return "", "", fmt.Errorf("could not parse listen address %s", specifiedAddr)
- }
- // Does the host component match any of the interface names on the
- // system? If so, use the address from that interface.
- interfaceAddr, err := resolveInterfaceAddr(specifiedHost)
- if err == nil {
- return interfaceAddr.String(), specifiedPort, nil
- }
- if err != errNoSuchInterface {
- return "", "", err
- }
- // If it's not an interface, it must be an IP (for now)
- if net.ParseIP(specifiedHost) == nil {
- return "", "", errBadListenAddr
- }
- return specifiedHost, specifiedPort, nil
- }
- func (c *Cluster) resolveAdvertiseAddr(advertiseAddr, listenAddrPort string) (string, string, error) {
- // Approach:
- // - If an advertise address is specified, use that. Resolve the
- // interface's address if an interface was specified in
- // advertiseAddr. Fill in the port from listenAddrPort if necessary.
- // - If DefaultAdvertiseAddr is not empty, use that with the port from
- // listenAddrPort. Resolve the interface's address from
- // if an interface name was specified in DefaultAdvertiseAddr.
- // - Otherwise, try to autodetect the system's address. Use the port in
- // listenAddrPort with this address if autodetection succeeds.
- if advertiseAddr != "" {
- advertiseHost, advertisePort, err := net.SplitHostPort(advertiseAddr)
- if err != nil {
- // Not a host:port specification
- advertiseHost = advertiseAddr
- advertisePort = listenAddrPort
- }
- // Does the host component match any of the interface names on the
- // system? If so, use the address from that interface.
- interfaceAddr, err := resolveInterfaceAddr(advertiseHost)
- if err == nil {
- return interfaceAddr.String(), advertisePort, nil
- }
- if err != errNoSuchInterface {
- return "", "", err
- }
- // If it's not an interface, it must be an IP (for now)
- if net.ParseIP(advertiseHost) == nil {
- return "", "", errBadAdvertiseAddr
- }
- return advertiseHost, advertisePort, nil
- }
- if c.config.DefaultAdvertiseAddr != "" {
- // Does the default advertise address component match any of the
- // interface names on the system? If so, use the address from
- // that interface.
- interfaceAddr, err := resolveInterfaceAddr(c.config.DefaultAdvertiseAddr)
- if err == nil {
- return interfaceAddr.String(), listenAddrPort, nil
- }
- if err != errNoSuchInterface {
- return "", "", err
- }
- // If it's not an interface, it must be an IP (for now)
- if net.ParseIP(c.config.DefaultAdvertiseAddr) == nil {
- return "", "", errBadDefaultAdvertiseAddr
- }
- return c.config.DefaultAdvertiseAddr, listenAddrPort, nil
- }
- systemAddr, err := c.resolveSystemAddr()
- if err != nil {
- return "", "", err
- }
- return systemAddr.String(), listenAddrPort, nil
- }
- func resolveInterfaceAddr(specifiedInterface string) (net.IP, error) {
- // Use a specific interface's IP address.
- intf, err := net.InterfaceByName(specifiedInterface)
- if err != nil {
- return nil, errNoSuchInterface
- }
- addrs, err := intf.Addrs()
- if err != nil {
- return nil, err
- }
- var interfaceAddr4, interfaceAddr6 net.IP
- for _, addr := range addrs {
- ipAddr, ok := addr.(*net.IPNet)
- if ok {
- if ipAddr.IP.To4() != nil {
- // IPv4
- if interfaceAddr4 != nil {
- return nil, fmt.Errorf("interface %s has more than one IPv4 address (%s and %s)", specifiedInterface, interfaceAddr4, ipAddr.IP)
- }
- interfaceAddr4 = ipAddr.IP
- } else {
- // IPv6
- if interfaceAddr6 != nil {
- return nil, fmt.Errorf("interface %s has more than one IPv6 address (%s and %s)", specifiedInterface, interfaceAddr6, ipAddr.IP)
- }
- interfaceAddr6 = ipAddr.IP
- }
- }
- }
- if interfaceAddr4 == nil && interfaceAddr6 == nil {
- return nil, fmt.Errorf("interface %s has no usable IPv4 or IPv6 address", specifiedInterface)
- }
- // In the case that there's exactly one IPv4 address
- // and exactly one IPv6 address, favor IPv4 over IPv6.
- if interfaceAddr4 != nil {
- return interfaceAddr4, nil
- }
- return interfaceAddr6, nil
- }
- func listSystemIPs() []net.IP {
- interfaces, err := net.Interfaces()
- if err != nil {
- return nil
- }
- var systemAddrs []net.IP
- for _, intf := range interfaces {
- addrs, err := intf.Addrs()
- if err != nil {
- continue
- }
- for _, addr := range addrs {
- ipAddr, ok := addr.(*net.IPNet)
- if ok {
- systemAddrs = append(systemAddrs, ipAddr.IP)
- }
- }
- }
- return systemAddrs
- }
- func errMultipleIPs(interfaceA, interfaceB string, addrA, addrB net.IP) error {
- if interfaceA == interfaceB {
- 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)
- }
- 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)
- }