Przeglądaj źródła

Merge pull request #25279 from aboch/auto

Only consider netlink "device" interfaces in address autodetection on Linux
Sebastiaan van Stijn 9 lat temu
rodzic
commit
acbac04c4b

+ 9 - 96
daemon/cluster/listen_addr.go

@@ -124,13 +124,13 @@ func resolveInterfaceAddr(specifiedInterface string) (net.IP, error) {
 			if ipAddr.IP.To4() != nil {
 				// IPv4
 				if interfaceAddr4 != nil {
-					return nil, fmt.Errorf("interface %s has more than one IPv4 address", specifiedInterface)
+					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", specifiedInterface)
+					return nil, fmt.Errorf("interface %s has more than one IPv6 address (%s and %s)", specifiedInterface, interfaceAddr6, ipAddr.IP)
 				}
 				interfaceAddr6 = ipAddr.IP
 			}
@@ -149,100 +149,6 @@ func resolveInterfaceAddr(specifiedInterface string) (net.IP, error) {
 	return interfaceAddr6, nil
 }
 
-func (c *Cluster) resolveSystemAddr() (net.IP, error) {
-	// Use the system's only IP address, or fail if there are
-	// multiple addresses to choose from.
-	interfaces, err := net.Interfaces()
-	if err != nil {
-		return nil, err
-	}
-
-	var systemAddr net.IP
-	var systemInterface net.Interface
-
-	// List Docker-managed subnets
-	v4Subnets := c.config.NetworkSubnetsProvider.V4Subnets()
-	v6Subnets := c.config.NetworkSubnetsProvider.V6Subnets()
-
-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, fmt.Errorf("could not choose an IP address to advertise since this system has multiple addresses on interface %s (%s and %s)", 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, fmt.Errorf("could not choose an IP address to advertise since this system has multiple addresses on interface %s (%s and %s)", 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, 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)", systemAddr, systemInterface.Name, interfaceAddr4, intf.Name)
-			}
-			systemAddr = interfaceAddr4
-			systemInterface = intf
-		} else if interfaceAddr6 != nil {
-			if systemAddr != nil {
-				return nil, 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)", systemAddr, systemInterface.Name, interfaceAddr6, intf.Name)
-			}
-			systemAddr = interfaceAddr6
-			systemInterface = intf
-		}
-	}
-
-	if systemAddr == nil {
-		return nil, errNoIP
-	}
-
-	return systemAddr, nil
-}
-
 func listSystemIPs() []net.IP {
 	interfaces, err := net.Interfaces()
 	if err != nil {
@@ -268,3 +174,10 @@ func listSystemIPs() []net.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)
+}

+ 78 - 0
daemon/cluster/listen_addr_linux.go

@@ -0,0 +1,78 @@
+// +build linux
+
+package cluster
+
+import (
+	"net"
+
+	"github.com/vishvananda/netlink"
+)
+
+func (c *Cluster) resolveSystemAddr() (net.IP, error) {
+	// Use the system's only device IP address, or fail if there are
+	// multiple addresses to choose from.
+	interfaces, err := netlink.LinkList()
+	if err != nil {
+		return nil, err
+	}
+
+	var systemAddr net.IP
+	var systemInterface string
+
+	for _, intf := range interfaces {
+		// Skip non device or inactive interfaces
+		if intf.Type() != "device" || intf.Attrs().Flags&net.FlagUp == 0 {
+			continue
+		}
+
+		addrs, err := netlink.AddrList(intf, netlink.FAMILY_ALL)
+		if err != nil {
+			continue
+		}
+
+		var interfaceAddr4, interfaceAddr6 net.IP
+
+		for _, addr := range addrs {
+			ipAddr := addr.IPNet.IP
+
+			// Skip loopback and link-local addresses
+			if !ipAddr.IsGlobalUnicast() {
+				continue
+			}
+
+			if ipAddr.To4() != nil {
+				if interfaceAddr4 != nil {
+					return nil, errMultipleIPs(intf.Attrs().Name, intf.Attrs().Name, interfaceAddr4, ipAddr)
+				}
+				interfaceAddr4 = ipAddr
+			} else {
+				if interfaceAddr6 != nil {
+					return nil, errMultipleIPs(intf.Attrs().Name, intf.Attrs().Name, interfaceAddr6, ipAddr)
+				}
+				interfaceAddr6 = ipAddr
+			}
+		}
+
+		// 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.Attrs().Name, systemAddr, interfaceAddr4)
+			}
+			systemAddr = interfaceAddr4
+			systemInterface = intf.Attrs().Name
+		} else if interfaceAddr6 != nil {
+			if systemAddr != nil {
+				return nil, errMultipleIPs(systemInterface, intf.Attrs().Name, systemAddr, interfaceAddr6)
+			}
+			systemAddr = interfaceAddr6
+			systemInterface = intf.Attrs().Name
+		}
+	}
+
+	if systemAddr == nil {
+		return nil, errNoIP
+	}
+
+	return systemAddr, nil
+}

+ 99 - 0
daemon/cluster/listen_addr_others.go

@@ -0,0 +1,99 @@
+// +build !linux
+
+package cluster
+
+import "net"
+
+func (c *Cluster) resolveSystemAddr() (net.IP, error) {
+	// Use the system's only IP address, or fail if there are
+	// multiple addresses to choose from.
+	interfaces, err := net.Interfaces()
+	if err != nil {
+		return nil, err
+	}
+
+	var systemAddr net.IP
+	var systemInterface string
+
+	// List Docker-managed subnets
+	v4Subnets := c.config.NetworkSubnetsProvider.V4Subnets()
+	v6Subnets := c.config.NetworkSubnetsProvider.V6Subnets()
+
+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
+}