github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/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") 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 an IP address or network interface (with optional port number)") 15 errBadDefaultAdvertiseAddr = errors.New("default advertise address must be an 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 net.ParseIP(advertiseHost) == nil { 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 net.ParseIP(c.config.DefaultAdvertiseAddr) == nil { 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 listSystemIPs() []net.IP { 153 interfaces, err := net.Interfaces() 154 if err != nil { 155 return nil 156 } 157 158 var systemAddrs []net.IP 159 160 for _, intf := range interfaces { 161 addrs, err := intf.Addrs() 162 if err != nil { 163 continue 164 } 165 166 for _, addr := range addrs { 167 ipAddr, ok := addr.(*net.IPNet) 168 169 if ok { 170 systemAddrs = append(systemAddrs, ipAddr.IP) 171 } 172 } 173 } 174 175 return systemAddrs 176 } 177 178 func errMultipleIPs(interfaceA, interfaceB string, addrA, addrB net.IP) error { 179 if interfaceA == interfaceB { 180 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) 181 } 182 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) 183 }