/
listen_addr.go
302 lines (256 loc) · 9 KB
/
listen_addr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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, and a system's IP address to use could not be uniquely identified")
errBadNetworkIdentifier = errors.New("must specify a valid IP address or interface name")
errBadListenAddr = errors.New("listen address must be an IP address or network interface (with optional port number)")
errBadAdvertiseAddr = errors.New("advertise address must be a non-zero IP address or network interface (with optional port number)")
errBadDataPathAddr = errors.New("data path address must be a non-zero IP address or network interface (without a port number)")
errBadDefaultAdvertiseAddr = errors.New("default advertise address must be a non-zero 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.
specifiedIP, err := resolveInputIPAddr(specifiedHost, true)
if err != nil {
if err == errBadNetworkIdentifier {
err = errBadListenAddr
}
return "", "", err
}
return specifiedIP.String(), 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.
advertiseIP, err := resolveInputIPAddr(advertiseHost, false)
if err != nil {
if err == errBadNetworkIdentifier {
err = errBadAdvertiseAddr
}
return "", "", err
}
return advertiseIP.String(), 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.
defaultAdvertiseIP, err := resolveInputIPAddr(c.config.DefaultAdvertiseAddr, false)
if err != nil {
if err == errBadNetworkIdentifier {
err = errBadDefaultAdvertiseAddr
}
return "", "", err
}
return defaultAdvertiseIP.String(), listenAddrPort, nil
}
systemAddr, err := c.resolveSystemAddr()
if err != nil {
return "", "", err
}
return systemAddr.String(), listenAddrPort, nil
}
func resolveDataPathAddr(dataPathAddr string) (string, error) {
if dataPathAddr == "" {
// dataPathAddr is not defined
return "", nil
}
// If a data path flag is specified try to resolve the IP address.
dataPathIP, err := resolveInputIPAddr(dataPathAddr, false)
if err != nil {
if err == errBadNetworkIdentifier {
err = errBadDataPathAddr
}
return "", err
}
return dataPathIP.String(), 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
}
// resolveInputIPAddr tries to resolve the IP address from the string passed as input
// - tries to match the string as an interface name, if so returns the IP address associated with it
// - on failure of previous step tries to parse the string as an IP address itself
// if succeeds returns the IP address
func resolveInputIPAddr(input string, isUnspecifiedValid bool) (net.IP, error) {
// Try to see if it is an interface name
interfaceAddr, err := resolveInterfaceAddr(input)
if err == nil {
return interfaceAddr, nil
}
// String matched interface but there is a potential ambiguity to be resolved
if err != errNoSuchInterface {
return nil, err
}
// String is not an interface check if it is a valid IP
if ip := net.ParseIP(input); ip != nil && (isUnspecifiedValid || !ip.IsUnspecified()) {
return ip, nil
}
// Not valid IP found
return nil, errBadNetworkIdentifier
}
func (c *Cluster) resolveSystemAddrViaSubnetCheck() (net.IP, error) {
// Use the system's only IP address, or fail if there are
// multiple addresses to choose from. Skip interfaces which
// are managed by docker via subnet check.
interfaces, err := net.Interfaces()
if err != nil {
return nil, err
}
var systemAddr net.IP
var systemInterface string
// List Docker-managed subnets
v4Subnets, v6Subnets := c.config.NetworkSubnetsProvider.Subnets()
ifaceLoop:
for _, intf := range interfaces {
// Skip inactive interfaces and loopback interfaces
if (intf.Flags&net.FlagUp == 0) || (intf.Flags&net.FlagLoopback) != 0 {
continue
}
addrs, err := intf.Addrs()
if err != nil {
continue
}
var interfaceAddr4, interfaceAddr6 net.IP
for _, addr := range addrs {
ipAddr, ok := addr.(*net.IPNet)
// Skip loopback and link-local addresses
if !ok || !ipAddr.IP.IsGlobalUnicast() {
continue
}
if ipAddr.IP.To4() != nil {
// IPv4
// Ignore addresses in subnets that are managed by Docker.
for _, subnet := range v4Subnets {
if subnet.Contains(ipAddr.IP) {
continue ifaceLoop
}
}
if interfaceAddr4 != nil {
return nil, errMultipleIPs(intf.Name, intf.Name, interfaceAddr4, ipAddr.IP)
}
interfaceAddr4 = ipAddr.IP
} else {
// IPv6
// Ignore addresses in subnets that are managed by Docker.
for _, subnet := range v6Subnets {
if subnet.Contains(ipAddr.IP) {
continue ifaceLoop
}
}
if interfaceAddr6 != nil {
return nil, errMultipleIPs(intf.Name, intf.Name, interfaceAddr6, ipAddr.IP)
}
interfaceAddr6 = ipAddr.IP
}
}
// In the case that this interface has exactly one IPv4 address
// and exactly one IPv6 address, favor IPv4 over IPv6.
if interfaceAddr4 != nil {
if systemAddr != nil {
return nil, errMultipleIPs(systemInterface, intf.Name, systemAddr, interfaceAddr4)
}
systemAddr = interfaceAddr4
systemInterface = intf.Name
} else if interfaceAddr6 != nil {
if systemAddr != nil {
return nil, errMultipleIPs(systemInterface, intf.Name, systemAddr, interfaceAddr6)
}
systemAddr = interfaceAddr6
systemInterface = intf.Name
}
}
if systemAddr == nil {
return nil, errNoIP
}
return systemAddr, 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)
}