diff --git a/api/client/run.go b/api/client/run.go index 6f8cdb871f..10cf92459d 100644 --- a/api/client/run.go +++ b/api/client/run.go @@ -9,7 +9,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/promise" - "github.com/docker/docker/pkg/resolvconf/dns" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/runconfig" "github.com/docker/libnetwork/resolvconf/dns" diff --git a/daemon/execdriver/lxc/lxc_template.go b/daemon/execdriver/lxc/lxc_template.go index 03c975c5f7..6bb50e6ab1 100644 --- a/daemon/execdriver/lxc/lxc_template.go +++ b/daemon/execdriver/lxc/lxc_template.go @@ -15,22 +15,6 @@ import ( "github.com/docker/libcontainer/label" ) -/* {{if .Network.Interface}} -# network configuration -lxc.network.type = veth -lxc.network.link = {{.Network.Interface.Bridge}} -lxc.network.name = eth0 -lxc.network.mtu = {{.Network.Mtu}} -lxc.network.flags = up -{{else if .Network.HostNetworking}} -lxc.network.type = none -{{else}} -# network is disabled (-n=false) -lxc.network.type = empty -lxc.network.flags = up -lxc.network.mtu = {{.Network.Mtu}} -{{end}} */ - const LxcTemplate = ` lxc.network.type = none # root filesystem diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go deleted file mode 100644 index 3449f4a1ef..0000000000 --- a/daemon/networkdriver/bridge/driver.go +++ /dev/null @@ -1,811 +0,0 @@ -package bridge - -import ( - "encoding/hex" - "errors" - "fmt" - "io/ioutil" - "net" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "sync" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/daemon/network" - "github.com/docker/docker/daemon/networkdriver" - "github.com/docker/docker/daemon/networkdriver/ipallocator" - "github.com/docker/docker/daemon/networkdriver/portmapper" - "github.com/docker/docker/nat" - "github.com/docker/docker/pkg/iptables" - "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/docker/pkg/resolvconf" - "github.com/docker/libcontainer/netlink" -) - -const ( - DefaultNetworkBridge = "docker0" - MaxAllocatedPortAttempts = 10 -) - -// Network interface represents the networking stack of a container -type networkInterface struct { - IP net.IP - IPv6 net.IP - PortMappings []net.Addr // There are mappings to the host interfaces -} - -type ifaces struct { - c map[string]*networkInterface - sync.Mutex -} - -func (i *ifaces) Set(key string, n *networkInterface) { - i.Lock() - i.c[key] = n - i.Unlock() -} - -func (i *ifaces) Get(key string) *networkInterface { - i.Lock() - res := i.c[key] - i.Unlock() - return res -} - -var ( - addrs = []string{ - // Here we don't follow the convention of using the 1st IP of the range for the gateway. - // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. - // In theory this shouldn't matter - in practice there's bound to be a few scripts relying - // on the internal addressing or other things like that. - // They shouldn't, but hey, let's not break them unless we really have to. - "172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 - "10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive - "10.1.42.1/16", - "10.42.42.1/16", - "172.16.42.1/24", - "172.16.43.1/24", - "172.16.44.1/24", - "10.0.42.1/24", - "10.0.43.1/24", - "192.168.42.1/24", - "192.168.43.1/24", - "192.168.44.1/24", - } - - bridgeIface string - bridgeIPv4Network *net.IPNet - gatewayIPv4 net.IP - bridgeIPv6Addr net.IP - globalIPv6Network *net.IPNet - gatewayIPv6 net.IP - portMapper *portmapper.PortMapper - once sync.Once - hairpinMode bool - - defaultBindingIP = net.ParseIP("0.0.0.0") - currentInterfaces = ifaces{c: make(map[string]*networkInterface)} - ipAllocator = ipallocator.New() -) - -func initPortMapper() { - once.Do(func() { - portMapper = portmapper.New() - }) -} - -type Config struct { - EnableIPv6 bool - EnableIptables bool - EnableIpForward bool - EnableIpMasq bool - EnableUserlandProxy bool - DefaultIp net.IP - Iface string - IP string - FixedCIDR string - FixedCIDRv6 string - DefaultGatewayIPv4 string - DefaultGatewayIPv6 string - InterContainerCommunication bool -} - -func InitDriver(config *Config) error { - var ( - networkv4 *net.IPNet - networkv6 *net.IPNet - addrv4 net.Addr - addrsv6 []net.Addr - bridgeIPv6 = "fe80::1/64" - ) - - // try to modprobe bridge first - // see gh#12177 - if out, err := exec.Command("modprobe", "-va", "bridge", "nf_nat", "br_netfilter").Output(); err != nil { - logrus.Warnf("Running modprobe bridge nf_nat failed with message: %s, error: %v", out, err) - } - - initPortMapper() - - if config.DefaultIp != nil { - defaultBindingIP = config.DefaultIp - } - - hairpinMode = !config.EnableUserlandProxy - - bridgeIface = config.Iface - usingDefaultBridge := false - if bridgeIface == "" { - usingDefaultBridge = true - bridgeIface = DefaultNetworkBridge - } - - addrv4, addrsv6, err := networkdriver.GetIfaceAddr(bridgeIface) - - if err != nil { - // No Bridge existent, create one - // If we're not using the default bridge, fail without trying to create it - if !usingDefaultBridge { - return err - } - - logrus.Info("Bridge interface not found, trying to create it") - - // If the iface is not found, try to create it - if err := configureBridge(config.IP, bridgeIPv6, config.EnableIPv6); err != nil { - logrus.Errorf("Could not configure Bridge: %s", err) - return err - } - - addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface) - if err != nil { - return err - } - - if config.FixedCIDRv6 != "" { - // Setting route to global IPv6 subnet - logrus.Infof("Adding route to IPv6 network %q via device %q", config.FixedCIDRv6, bridgeIface) - if err := netlink.AddRoute(config.FixedCIDRv6, "", "", bridgeIface); err != nil { - logrus.Fatalf("Could not add route to IPv6 network %q via device %q", config.FixedCIDRv6, bridgeIface) - } - } - } else { - // Bridge exists already, getting info... - // Validate that the bridge ip matches the ip specified by BridgeIP - if config.IP != "" { - networkv4 = addrv4.(*net.IPNet) - bip, _, err := net.ParseCIDR(config.IP) - if err != nil { - return err - } - if !networkv4.IP.Equal(bip) { - return fmt.Errorf("Bridge ip (%s) does not match existing bridge configuration %s", networkv4.IP, bip) - } - } - - // A bridge might exist but not have any IPv6 addr associated with it yet - // (for example, an existing Docker installation that has only been used - // with IPv4 and docker0 already is set up) In that case, we can perform - // the bridge init for IPv6 here, else we will error out below if --ipv6=true - if len(addrsv6) == 0 && config.EnableIPv6 { - if err := setupIPv6Bridge(bridgeIPv6); err != nil { - return err - } - // Recheck addresses now that IPv6 is setup on the bridge - addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface) - if err != nil { - return err - } - } - - // TODO: Check if route to config.FixedCIDRv6 is set - } - - if config.EnableIPv6 { - bip6, _, err := net.ParseCIDR(bridgeIPv6) - if err != nil { - return err - } - found := false - for _, addrv6 := range addrsv6 { - networkv6 = addrv6.(*net.IPNet) - if networkv6.IP.Equal(bip6) { - found = true - break - } - } - if !found { - return fmt.Errorf("Bridge IPv6 does not match existing bridge configuration %s", bip6) - } - } - - networkv4 = addrv4.(*net.IPNet) - - if config.EnableIPv6 { - if len(addrsv6) == 0 { - return errors.New("IPv6 enabled but no IPv6 detected") - } - bridgeIPv6Addr = networkv6.IP - } - - if config.EnableIptables { - if err := iptables.FirewalldInit(); err != nil { - logrus.Debugf("Error initializing firewalld: %v", err) - } - } - - // Configure iptables for link support - if config.EnableIptables { - if err := setupIPTables(addrv4, config.InterContainerCommunication, config.EnableIpMasq); err != nil { - logrus.Errorf("Error configuring iptables: %s", err) - return err - } - // call this on Firewalld reload - iptables.OnReloaded(func() { setupIPTables(addrv4, config.InterContainerCommunication, config.EnableIpMasq) }) - } - - if config.EnableIpForward { - // Enable IPv4 forwarding - if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("Unable to enable IPv4 forwarding: %v", err) - } - - if config.FixedCIDRv6 != "" { - // Enable IPv6 forwarding - if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("Unable to enable IPv6 default forwarding: %v", err) - } - if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("Unable to enable IPv6 all forwarding: %v", err) - } - } - } - - if hairpinMode { - // Enable loopback adresses routing - sysPath := filepath.Join("/proc/sys/net/ipv4/conf", bridgeIface, "route_localnet") - if err := ioutil.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("Unable to enable local routing for hairpin mode: %v", err) - } - } - - // We can always try removing the iptables - if err := iptables.RemoveExistingChain("DOCKER", iptables.Nat); err != nil { - return err - } - - if config.EnableIptables { - _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Nat, hairpinMode) - if err != nil { - return err - } - // call this on Firewalld reload - iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Nat, hairpinMode) }) - chain, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter, hairpinMode) - if err != nil { - return err - } - // call this on Firewalld reload - iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Filter, hairpinMode) }) - - portMapper.SetIptablesChain(chain) - } - - bridgeIPv4Network = networkv4 - if config.FixedCIDR != "" { - _, subnet, err := net.ParseCIDR(config.FixedCIDR) - if err != nil { - return err - } - logrus.Debugf("Subnet: %v", subnet) - if err := ipAllocator.RegisterSubnet(bridgeIPv4Network, subnet); err != nil { - logrus.Errorf("Error registering subnet for IPv4 bridge network: %s", err) - return err - } - } - - gateway, err := requestDefaultGateway(config.DefaultGatewayIPv4, bridgeIPv4Network) - if err != nil { - return err - } - gatewayIPv4 = gateway - - if config.FixedCIDRv6 != "" { - _, subnet, err := net.ParseCIDR(config.FixedCIDRv6) - if err != nil { - return err - } - logrus.Debugf("Subnet: %v", subnet) - if err := ipAllocator.RegisterSubnet(subnet, subnet); err != nil { - logrus.Errorf("Error registering subnet for IPv6 bridge network: %s", err) - return err - } - globalIPv6Network = subnet - - gateway, err := requestDefaultGateway(config.DefaultGatewayIPv6, globalIPv6Network) - if err != nil { - return err - } - gatewayIPv6 = gateway - } - - // Block BridgeIP in IP allocator - ipAllocator.RequestIP(bridgeIPv4Network, bridgeIPv4Network.IP) - - if config.EnableIptables { - iptables.OnReloaded(portMapper.ReMapAll) // call this on Firewalld reload - } - - return nil -} - -func setupIPTables(addr net.Addr, icc, ipmasq bool) error { - // Enable NAT - - if ipmasq { - natArgs := []string{"-s", addr.String(), "!", "-o", bridgeIface, "-j", "MASQUERADE"} - - if !iptables.Exists(iptables.Nat, "POSTROUTING", natArgs...) { - if output, err := iptables.Raw(append([]string{ - "-t", string(iptables.Nat), "-I", "POSTROUTING"}, natArgs...)...); err != nil { - return fmt.Errorf("Unable to enable network bridge NAT: %s", err) - } else if len(output) != 0 { - return iptables.ChainError{Chain: "POSTROUTING", Output: output} - } - } - } - - var ( - args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"} - acceptArgs = append(args, "ACCEPT") - dropArgs = append(args, "DROP") - ) - - if !icc { - iptables.Raw(append([]string{"-D", "FORWARD"}, acceptArgs...)...) - - if !iptables.Exists(iptables.Filter, "FORWARD", dropArgs...) { - logrus.Debugf("Disable inter-container communication") - if output, err := iptables.Raw(append([]string{"-A", "FORWARD"}, dropArgs...)...); err != nil { - return fmt.Errorf("Unable to prevent intercontainer communication: %s", err) - } else if len(output) != 0 { - return fmt.Errorf("Error disabling intercontainer communication: %s", output) - } - } - } else { - iptables.Raw(append([]string{"-D", "FORWARD"}, dropArgs...)...) - - if !iptables.Exists(iptables.Filter, "FORWARD", acceptArgs...) { - logrus.Debugf("Enable inter-container communication") - if output, err := iptables.Raw(append([]string{"-A", "FORWARD"}, acceptArgs...)...); err != nil { - return fmt.Errorf("Unable to allow intercontainer communication: %s", err) - } else if len(output) != 0 { - return fmt.Errorf("Error enabling intercontainer communication: %s", output) - } - } - } - - // In hairpin mode, masquerade traffic from localhost - if hairpinMode { - masqueradeArgs := []string{"-t", "nat", "-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"} - if !iptables.Exists(iptables.Filter, "POSTROUTING", masqueradeArgs...) { - if output, err := iptables.Raw(append([]string{"-I", "POSTROUTING"}, masqueradeArgs...)...); err != nil { - return fmt.Errorf("Unable to masquerade local traffic: %s", err) - } else if len(output) != 0 { - return fmt.Errorf("Error iptables masquerade local traffic: %s", output) - } - } - } - - // Accept all non-intercontainer outgoing packets - outgoingArgs := []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"} - if !iptables.Exists(iptables.Filter, "FORWARD", outgoingArgs...) { - if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, outgoingArgs...)...); err != nil { - return fmt.Errorf("Unable to allow outgoing packets: %s", err) - } else if len(output) != 0 { - return iptables.ChainError{Chain: "FORWARD outgoing", Output: output} - } - } - - // Accept incoming packets for existing connections - existingArgs := []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"} - - if !iptables.Exists(iptables.Filter, "FORWARD", existingArgs...) { - if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, existingArgs...)...); err != nil { - return fmt.Errorf("Unable to allow incoming packets: %s", err) - } else if len(output) != 0 { - return iptables.ChainError{Chain: "FORWARD incoming", Output: output} - } - } - return nil -} - -func RequestPort(ip net.IP, proto string, port int) (int, error) { - initPortMapper() - return portMapper.Allocator.RequestPort(ip, proto, port) -} - -// configureBridge attempts to create and configure a network bridge interface named `bridgeIface` on the host -// If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges -// If the bridge `bridgeIface` already exists, it will only perform the IP address association with the existing -// bridge (fixes issue #8444) -// If an address which doesn't conflict with existing interfaces can't be found, an error is returned. -func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error { - nameservers := []string{} - resolvConf, _ := resolvconf.Get() - // We don't check for an error here, because we don't really care - // if we can't read /etc/resolv.conf. So instead we skip the append - // if resolvConf is nil. It either doesn't exist, or we can't read it - // for some reason. - if resolvConf != nil { - nameservers = append(nameservers, resolvconf.GetNameserversAsCIDR(resolvConf)...) - } - - var ifaceAddr string - if len(bridgeIP) != 0 { - _, _, err := net.ParseCIDR(bridgeIP) - if err != nil { - return err - } - ifaceAddr = bridgeIP - } else { - for _, addr := range addrs { - _, dockerNetwork, err := net.ParseCIDR(addr) - if err != nil { - return err - } - if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil { - if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil { - ifaceAddr = addr - break - } else { - logrus.Debugf("%s %s", addr, err) - } - } - } - } - - if ifaceAddr == "" { - return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", bridgeIface, bridgeIface) - } - logrus.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr) - - if err := createBridgeIface(bridgeIface); err != nil { - // The bridge may already exist, therefore we can ignore an "exists" error - if !os.IsExist(err) { - return err - } - } - - iface, err := net.InterfaceByName(bridgeIface) - if err != nil { - return err - } - - ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr) - if err != nil { - return err - } - - if err := netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil { - return fmt.Errorf("Unable to add private network: %s", err) - } - - if enableIPv6 { - if err := setupIPv6Bridge(bridgeIPv6); err != nil { - return err - } - } - - if err := netlink.NetworkLinkUp(iface); err != nil { - return fmt.Errorf("Unable to start network bridge: %s", err) - } - return nil -} - -func setupIPv6Bridge(bridgeIPv6 string) error { - - iface, err := net.InterfaceByName(bridgeIface) - if err != nil { - return err - } - // Enable IPv6 on the bridge - procFile := "/proc/sys/net/ipv6/conf/" + iface.Name + "/disable_ipv6" - if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil { - return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err) - } - - ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6) - if err != nil { - return fmt.Errorf("Unable to parse bridge IPv6 address: %q, error: %v", bridgeIPv6, err) - } - - if err := netlink.NetworkLinkAddIp(iface, ipAddr6, ipNet6); err != nil { - return fmt.Errorf("Unable to add private IPv6 network: %v", err) - } - - return nil -} - -func requestDefaultGateway(requestedGateway string, network *net.IPNet) (gateway net.IP, err error) { - if requestedGateway != "" { - gateway = net.ParseIP(requestedGateway) - - if gateway == nil { - return nil, fmt.Errorf("Bad parameter: invalid gateway ip %s", requestedGateway) - } - - if !network.Contains(gateway) { - return nil, fmt.Errorf("Gateway ip %s must be part of the network %s", requestedGateway, network.String()) - } - - ipAllocator.RequestIP(network, gateway) - } - - return gateway, nil -} - -func createBridgeIface(name string) error { - kv, err := kernel.GetKernelVersion() - // Only set the bridge's mac address if the kernel version is > 3.3 - // before that it was not supported - setBridgeMacAddr := err == nil && (kv.Kernel >= 3 && kv.Major >= 3) - logrus.Debugf("setting bridge mac address = %v", setBridgeMacAddr) - return netlink.CreateBridge(name, setBridgeMacAddr) -} - -// Generate a IEEE802 compliant MAC address from the given IP address. -// -// The generator is guaranteed to be consistent: the same IP will always yield the same -// MAC address. This is to avoid ARP cache issues. -func generateMacAddr(ip net.IP) net.HardwareAddr { - hw := make(net.HardwareAddr, 6) - - // The first byte of the MAC address has to comply with these rules: - // 1. Unicast: Set the least-significant bit to 0. - // 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1. - // 3. As "small" as possible: The veth address has to be "smaller" than the bridge address. - hw[0] = 0x02 - - // The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI). - // Since this address is locally administered, we can do whatever we want as long as - // it doesn't conflict with other addresses. - hw[1] = 0x42 - - // Insert the IP address into the last 32 bits of the MAC address. - // This is a simple way to guarantee the address will be consistent and unique. - copy(hw[2:], ip.To4()) - - return hw -} - -func linkLocalIPv6FromMac(mac string) (string, error) { - hx := strings.Replace(mac, ":", "", -1) - hw, err := hex.DecodeString(hx) - if err != nil { - return "", errors.New("Could not parse MAC address " + mac) - } - - hw[0] ^= 0x2 - - return fmt.Sprintf("fe80::%x%x:%xff:fe%x:%x%x/64", hw[0], hw[1], hw[2], hw[3], hw[4], hw[5]), nil -} - -// Allocate a network interface -func Allocate(id, requestedMac, requestedIP, requestedIPv6 string) (*network.Settings, error) { - var ( - ip net.IP - mac net.HardwareAddr - err error - globalIPv6 net.IP - defaultGWIPv4 net.IP - defaultGWIPv6 net.IP - ) - - ip, err = ipAllocator.RequestIP(bridgeIPv4Network, net.ParseIP(requestedIP)) - if err != nil { - return nil, err - } - - // If no explicit mac address was given, generate one from the IP address. - if mac, err = net.ParseMAC(requestedMac); err != nil { - mac = generateMacAddr(ip) - } - - if globalIPv6Network != nil { - // If globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address - netmaskOnes, _ := globalIPv6Network.Mask.Size() - ipv6 := net.ParseIP(requestedIPv6) - if ipv6 == nil && netmaskOnes <= 80 { - ipv6 = make(net.IP, len(globalIPv6Network.IP)) - copy(ipv6, globalIPv6Network.IP) - for i, h := range mac { - ipv6[i+10] = h - } - } - - globalIPv6, err = ipAllocator.RequestIP(globalIPv6Network, ipv6) - if err != nil { - logrus.Errorf("Allocator: RequestIP v6: %v", err) - return nil, err - } - logrus.Infof("Allocated IPv6 %s", globalIPv6) - } - - maskSize, _ := bridgeIPv4Network.Mask.Size() - - if gatewayIPv4 != nil { - defaultGWIPv4 = gatewayIPv4 - } else { - defaultGWIPv4 = bridgeIPv4Network.IP - } - - if gatewayIPv6 != nil { - defaultGWIPv6 = gatewayIPv6 - } else { - defaultGWIPv6 = bridgeIPv6Addr - } - - // If linklocal IPv6 - localIPv6Net, err := linkLocalIPv6FromMac(mac.String()) - if err != nil { - return nil, err - } - localIPv6, _, _ := net.ParseCIDR(localIPv6Net) - - networkSettings := &network.Settings{ - IPAddress: ip.String(), - Gateway: defaultGWIPv4.String(), - MacAddress: mac.String(), - Bridge: bridgeIface, - IPPrefixLen: maskSize, - LinkLocalIPv6Address: localIPv6.String(), - HairpinMode: hairpinMode, - } - - if globalIPv6Network != nil { - networkSettings.GlobalIPv6Address = globalIPv6.String() - maskV6Size, _ := globalIPv6Network.Mask.Size() - networkSettings.GlobalIPv6PrefixLen = maskV6Size - networkSettings.IPv6Gateway = defaultGWIPv6.String() - } - - currentInterfaces.Set(id, &networkInterface{ - IP: ip, - IPv6: globalIPv6, - }) - - return networkSettings, nil -} - -// Release an interface for a select ip -func Release(id string) { - var containerInterface = currentInterfaces.Get(id) - - if containerInterface == nil { - logrus.Warnf("No network information to release for %s", id) - return - } - - for _, nat := range containerInterface.PortMappings { - if err := portMapper.Unmap(nat); err != nil { - logrus.Infof("Unable to unmap port %s: %s", nat, err) - } - } - - if err := ipAllocator.ReleaseIP(bridgeIPv4Network, containerInterface.IP); err != nil { - logrus.Infof("Unable to release IPv4 %s", err) - } - if globalIPv6Network != nil { - if err := ipAllocator.ReleaseIP(globalIPv6Network, containerInterface.IPv6); err != nil { - logrus.Infof("Unable to release IPv6 %s", err) - } - } -} - -// Allocate an external port and map it to the interface -func AllocatePort(id string, port nat.Port, binding nat.PortBinding) (nat.PortBinding, error) { - var ( - ip = defaultBindingIP - proto = port.Proto() - containerPort = port.Int() - network = currentInterfaces.Get(id) - ) - - if binding.HostIp != "" { - ip = net.ParseIP(binding.HostIp) - if ip == nil { - return nat.PortBinding{}, fmt.Errorf("Bad parameter: invalid host ip %s", binding.HostIp) - } - } - - // host ip, proto, and host port - var container net.Addr - switch proto { - case "tcp": - container = &net.TCPAddr{IP: network.IP, Port: containerPort} - case "udp": - container = &net.UDPAddr{IP: network.IP, Port: containerPort} - default: - return nat.PortBinding{}, fmt.Errorf("unsupported address type %s", proto) - } - - // - // Try up to 10 times to get a port that's not already allocated. - // - // In the event of failure to bind, return the error that portmapper.Map - // yields. - // - - var ( - host net.Addr - err error - ) - hostPort, err := nat.ParsePort(binding.HostPort) - if err != nil { - return nat.PortBinding{}, err - } - for i := 0; i < MaxAllocatedPortAttempts; i++ { - if host, err = portMapper.Map(container, ip, hostPort, !hairpinMode); err == nil { - break - } - // There is no point in immediately retrying to map an explicitly - // chosen port. - if hostPort != 0 { - logrus.Warnf("Failed to allocate and map port %d: %s", hostPort, err) - break - } - logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1) - } - - if err != nil { - return nat.PortBinding{}, err - } - - network.PortMappings = append(network.PortMappings, host) - - switch netAddr := host.(type) { - case *net.TCPAddr: - return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil - case *net.UDPAddr: - return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil - default: - return nat.PortBinding{}, fmt.Errorf("unsupported address type %T", netAddr) - } -} - -//TODO: should it return something more than just an error? -func LinkContainers(action, parentIP, childIP string, ports []nat.Port, ignoreErrors bool) error { - var nfAction iptables.Action - - switch action { - case "-A": - nfAction = iptables.Append - case "-I": - nfAction = iptables.Insert - case "-D": - nfAction = iptables.Delete - default: - return fmt.Errorf("Invalid action '%s' specified", action) - } - - ip1 := net.ParseIP(parentIP) - if ip1 == nil { - return fmt.Errorf("Parent IP '%s' is invalid", parentIP) - } - ip2 := net.ParseIP(childIP) - if ip2 == nil { - return fmt.Errorf("Child IP '%s' is invalid", childIP) - } - - chain := iptables.Chain{Name: "DOCKER", Bridge: bridgeIface} - for _, port := range ports { - if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil { - return err - } - } - return nil -} diff --git a/daemon/networkdriver/bridge/driver_test.go b/daemon/networkdriver/bridge/driver_test.go deleted file mode 100644 index c82acb86a2..0000000000 --- a/daemon/networkdriver/bridge/driver_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package bridge - -import ( - "fmt" - "net" - "strconv" - "testing" - - "github.com/docker/docker/daemon/network" - "github.com/docker/docker/daemon/networkdriver/portmapper" - "github.com/docker/docker/nat" - "github.com/docker/docker/pkg/iptables" -) - -func init() { - // reset the new proxy command for mocking out the userland proxy in tests - portmapper.NewProxy = portmapper.NewMockProxyCommand -} - -func findFreePort(t *testing.T) string { - l, err := net.Listen("tcp", ":0") - if err != nil { - t.Fatal("Failed to find a free port") - } - defer l.Close() - - result, err := net.ResolveTCPAddr("tcp", l.Addr().String()) - if err != nil { - t.Fatal("Failed to resolve address to identify free port") - } - return strconv.Itoa(result.Port) -} - -func TestAllocatePortDetection(t *testing.T) { - freePort := findFreePort(t) - - if err := InitDriver(new(Config)); err != nil { - t.Fatal("Failed to initialize network driver") - } - - // Allocate interface - if _, err := Allocate("container_id", "", "", ""); err != nil { - t.Fatal("Failed to allocate network interface") - } - - port := nat.Port(freePort + "/tcp") - binding := nat.PortBinding{HostIp: "127.0.0.1", HostPort: freePort} - - // Allocate same port twice, expect failure on second call - if _, err := AllocatePort("container_id", port, binding); err != nil { - t.Fatal("Failed to find a free port to allocate") - } - if _, err := AllocatePort("container_id", port, binding); err == nil { - t.Fatal("Duplicate port allocation granted by AllocatePort") - } -} - -func TestHostnameFormatChecking(t *testing.T) { - freePort := findFreePort(t) - - if err := InitDriver(new(Config)); err != nil { - t.Fatal("Failed to initialize network driver") - } - - // Allocate interface - if _, err := Allocate("container_id", "", "", ""); err != nil { - t.Fatal("Failed to allocate network interface") - } - - port := nat.Port(freePort + "/tcp") - binding := nat.PortBinding{HostIp: "localhost", HostPort: freePort} - - if _, err := AllocatePort("container_id", port, binding); err == nil { - t.Fatal("Failed to check invalid HostIP") - } -} - -func newInterfaceAllocation(t *testing.T, globalIPv6 *net.IPNet, requestedMac, requestedIP, requestedIPv6 string, expectFail bool) *network.Settings { - // set IPv6 global if given - if globalIPv6 != nil { - globalIPv6Network = globalIPv6 - } - - networkSettings, err := Allocate("container_id", requestedMac, requestedIP, requestedIPv6) - if err == nil && expectFail { - t.Fatal("Doesn't fail to allocate network interface") - } else if err != nil && !expectFail { - t.Fatal("Failed to allocate network interface") - - } - - if globalIPv6 != nil { - // check for bug #11427 - if globalIPv6Network.IP.String() != globalIPv6.IP.String() { - t.Fatal("globalIPv6Network was modified during allocation") - } - // clean up IPv6 global - globalIPv6Network = nil - } - - return networkSettings -} - -func TestIPv6InterfaceAllocationAutoNetmaskGt80(t *testing.T) { - _, subnet, _ := net.ParseCIDR("2001:db8:1234:1234:1234::/81") - networkSettings := newInterfaceAllocation(t, subnet, "", "", "", false) - - // ensure low manually assigend global ip - ip := net.ParseIP(networkSettings.GlobalIPv6Address) - _, subnet, _ = net.ParseCIDR(fmt.Sprintf("%s/%d", subnet.IP.String(), 120)) - if !subnet.Contains(ip) { - t.Fatalf("Error ip %s not in subnet %s", ip.String(), subnet.String()) - } -} - -func TestIPv6InterfaceAllocationAutoNetmaskLe80(t *testing.T) { - _, subnet, _ := net.ParseCIDR("2001:db8:1234:1234:1234::/80") - networkSettings := newInterfaceAllocation(t, subnet, "ab:cd:ab:cd:ab:cd", "", "", false) - - // ensure global ip with mac - ip := net.ParseIP(networkSettings.GlobalIPv6Address) - expectedIP := net.ParseIP("2001:db8:1234:1234:1234:abcd:abcd:abcd") - if ip.String() != expectedIP.String() { - t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP.String()) - } - - // ensure link local format - ip = net.ParseIP(networkSettings.LinkLocalIPv6Address) - expectedIP = net.ParseIP("fe80::a9cd:abff:fecd:abcd") - if ip.String() != expectedIP.String() { - t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP.String()) - } - -} - -func TestIPv6InterfaceAllocationRequest(t *testing.T) { - _, subnet, _ := net.ParseCIDR("2001:db8:1234:1234:1234::/80") - expectedIP := "2001:db8:1234:1234:1234::1328" - - networkSettings := newInterfaceAllocation(t, subnet, "", "", expectedIP, false) - - // ensure global ip with mac - ip := net.ParseIP(networkSettings.GlobalIPv6Address) - if ip.String() != expectedIP { - t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP) - } - - // retry -> fails for duplicated address - _ = newInterfaceAllocation(t, subnet, "", "", expectedIP, true) -} - -func TestMacAddrGeneration(t *testing.T) { - ip := net.ParseIP("192.168.0.1") - mac := generateMacAddr(ip).String() - - // Should be consistent. - if generateMacAddr(ip).String() != mac { - t.Fatal("Inconsistent MAC address") - } - - // Should be unique. - ip2 := net.ParseIP("192.168.0.2") - if generateMacAddr(ip2).String() == mac { - t.Fatal("Non-unique MAC address") - } -} - -func TestLinkContainers(t *testing.T) { - // Init driver - if err := InitDriver(new(Config)); err != nil { - t.Fatal("Failed to initialize network driver") - } - - // Allocate interface - if _, err := Allocate("container_id", "", "", ""); err != nil { - t.Fatal("Failed to allocate network interface") - } - - bridgeIface = "lo" - if _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter, false); err != nil { - t.Fatal(err) - } - - if err := LinkContainers("-I", "172.17.0.1", "172.17.0.2", []nat.Port{nat.Port("1234")}, false); err != nil { - t.Fatal("LinkContainers failed") - } - - // flush rules - if _, err := iptables.Raw([]string{"-F", "DOCKER"}...); err != nil { - t.Fatal(err) - } - -} diff --git a/daemon/networkdriver/ipallocator/allocator.go b/daemon/networkdriver/ipallocator/allocator.go deleted file mode 100644 index 554dbdd5b1..0000000000 --- a/daemon/networkdriver/ipallocator/allocator.go +++ /dev/null @@ -1,167 +0,0 @@ -package ipallocator - -import ( - "errors" - "math/big" - "net" - "sync" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/daemon/networkdriver" -) - -// allocatedMap is thread-unsafe set of allocated IP -type allocatedMap struct { - p map[string]struct{} - last *big.Int - begin *big.Int - end *big.Int -} - -func newAllocatedMap(network *net.IPNet) *allocatedMap { - firstIP, lastIP := networkdriver.NetworkRange(network) - begin := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1)) - end := big.NewInt(0).Sub(ipToBigInt(lastIP), big.NewInt(1)) - - return &allocatedMap{ - p: make(map[string]struct{}), - begin: begin, - end: end, - last: big.NewInt(0).Sub(begin, big.NewInt(1)), // so first allocated will be begin - } -} - -type networkSet map[string]*allocatedMap - -var ( - ErrNoAvailableIPs = errors.New("no available ip addresses on network") - ErrIPAlreadyAllocated = errors.New("ip already allocated") - ErrIPOutOfRange = errors.New("requested ip is out of range") - ErrNetworkAlreadyRegistered = errors.New("network already registered") - ErrBadSubnet = errors.New("network does not contain specified subnet") -) - -type IPAllocator struct { - allocatedIPs networkSet - mutex sync.Mutex -} - -func New() *IPAllocator { - return &IPAllocator{networkSet{}, sync.Mutex{}} -} - -// RegisterSubnet registers network in global allocator with bounds -// defined by subnet. If you want to use network range you must call -// this method before first RequestIP, otherwise full network range will be used -func (a *IPAllocator) RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error { - a.mutex.Lock() - defer a.mutex.Unlock() - - key := network.String() - if _, ok := a.allocatedIPs[key]; ok { - return ErrNetworkAlreadyRegistered - } - n := newAllocatedMap(network) - beginIP, endIP := networkdriver.NetworkRange(subnet) - begin := big.NewInt(0).Add(ipToBigInt(beginIP), big.NewInt(1)) - end := big.NewInt(0).Sub(ipToBigInt(endIP), big.NewInt(1)) - - // Check that subnet is within network - if !(begin.Cmp(n.begin) >= 0 && end.Cmp(n.end) <= 0 && begin.Cmp(end) == -1) { - return ErrBadSubnet - } - n.begin.Set(begin) - n.end.Set(end) - n.last.Sub(begin, big.NewInt(1)) - a.allocatedIPs[key] = n - return nil -} - -// RequestIP requests an available ip from the given network. It -// will return the next available ip if the ip provided is nil. If the -// ip provided is not nil it will validate that the provided ip is available -// for use or return an error -func (a *IPAllocator) RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) { - a.mutex.Lock() - defer a.mutex.Unlock() - - key := network.String() - allocated, ok := a.allocatedIPs[key] - if !ok { - allocated = newAllocatedMap(network) - a.allocatedIPs[key] = allocated - } - - if ip == nil { - return allocated.getNextIP() - } - return allocated.checkIP(ip) -} - -// ReleaseIP adds the provided ip back into the pool of -// available ips to be returned for use. -func (a *IPAllocator) ReleaseIP(network *net.IPNet, ip net.IP) error { - a.mutex.Lock() - defer a.mutex.Unlock() - - if allocated, exists := a.allocatedIPs[network.String()]; exists { - delete(allocated.p, ip.String()) - } - return nil -} - -func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) { - if _, ok := allocated.p[ip.String()]; ok { - return nil, ErrIPAlreadyAllocated - } - - pos := ipToBigInt(ip) - // Verify that the IP address is within our network range. - if pos.Cmp(allocated.begin) == -1 || pos.Cmp(allocated.end) == 1 { - return nil, ErrIPOutOfRange - } - - // Register the IP. - allocated.p[ip.String()] = struct{}{} - - return ip, nil -} - -// return an available ip if one is currently available. If not, -// return the next available ip for the network -func (allocated *allocatedMap) getNextIP() (net.IP, error) { - pos := big.NewInt(0).Set(allocated.last) - allRange := big.NewInt(0).Sub(allocated.end, allocated.begin) - for i := big.NewInt(0); i.Cmp(allRange) <= 0; i.Add(i, big.NewInt(1)) { - pos.Add(pos, big.NewInt(1)) - if pos.Cmp(allocated.end) == 1 { - pos.Set(allocated.begin) - } - if _, ok := allocated.p[bigIntToIP(pos).String()]; ok { - continue - } - allocated.p[bigIntToIP(pos).String()] = struct{}{} - allocated.last.Set(pos) - return bigIntToIP(pos), nil - } - return nil, ErrNoAvailableIPs -} - -// Converts a 4 bytes IP into a 128 bit integer -func ipToBigInt(ip net.IP) *big.Int { - x := big.NewInt(0) - if ip4 := ip.To4(); ip4 != nil { - return x.SetBytes(ip4) - } - if ip6 := ip.To16(); ip6 != nil { - return x.SetBytes(ip6) - } - - logrus.Errorf("ipToBigInt: Wrong IP length! %s", ip) - return nil -} - -// Converts 128 bit integer into a 4 bytes IP address -func bigIntToIP(v *big.Int) net.IP { - return net.IP(v.Bytes()) -} diff --git a/daemon/networkdriver/ipallocator/allocator_test.go b/daemon/networkdriver/ipallocator/allocator_test.go deleted file mode 100644 index 6c5c0e4dbc..0000000000 --- a/daemon/networkdriver/ipallocator/allocator_test.go +++ /dev/null @@ -1,690 +0,0 @@ -package ipallocator - -import ( - "fmt" - "math/big" - "net" - "testing" -) - -func TestConversion(t *testing.T) { - ip := net.ParseIP("127.0.0.1") - i := ipToBigInt(ip) - if i.Cmp(big.NewInt(0x7f000001)) != 0 { - t.Fatal("incorrect conversion") - } - conv := bigIntToIP(i) - if !ip.Equal(conv) { - t.Error(conv.String()) - } -} - -func TestConversionIPv6(t *testing.T) { - ip := net.ParseIP("2a00:1450::1") - ip2 := net.ParseIP("2a00:1450::2") - ip3 := net.ParseIP("2a00:1450::1:1") - i := ipToBigInt(ip) - val, success := big.NewInt(0).SetString("2a001450000000000000000000000001", 16) - if !success { - t.Fatal("Hex-String to BigInt conversion failed.") - } - if i.Cmp(val) != 0 { - t.Fatal("incorrent conversion") - } - - conv := bigIntToIP(i) - conv2 := bigIntToIP(big.NewInt(0).Add(i, big.NewInt(1))) - conv3 := bigIntToIP(big.NewInt(0).Add(i, big.NewInt(0x10000))) - - if !ip.Equal(conv) { - t.Error("2a00:1450::1 should be equal to " + conv.String()) - } - if !ip2.Equal(conv2) { - t.Error("2a00:1450::2 should be equal to " + conv2.String()) - } - if !ip3.Equal(conv3) { - t.Error("2a00:1450::1:1 should be equal to " + conv3.String()) - } -} - -func TestRequestNewIps(t *testing.T) { - a := New() - - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - - var ip net.IP - var err error - - for i := 1; i < 10; i++ { - ip, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - if expected := fmt.Sprintf("192.168.0.%d", i); ip.String() != expected { - t.Fatalf("Expected ip %s got %s", expected, ip.String()) - } - } - value := bigIntToIP(big.NewInt(0).Add(ipToBigInt(ip), big.NewInt(1))).String() - if err := a.ReleaseIP(network, ip); err != nil { - t.Fatal(err) - } - ip, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - if ip.String() != value { - t.Fatalf("Expected to receive the next ip %s got %s", value, ip.String()) - } -} - -func TestRequestNewIpV6(t *testing.T) { - a := New() - - network := &net.IPNet{ - IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask - } - - var ip net.IP - var err error - for i := 1; i < 10; i++ { - ip, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - if expected := fmt.Sprintf("2a00:1450::%d", i); ip.String() != expected { - t.Fatalf("Expected ip %s got %s", expected, ip.String()) - } - } - value := bigIntToIP(big.NewInt(0).Add(ipToBigInt(ip), big.NewInt(1))).String() - if err := a.ReleaseIP(network, ip); err != nil { - t.Fatal(err) - } - ip, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - if ip.String() != value { - t.Fatalf("Expected to receive the next ip %s got %s", value, ip.String()) - } -} - -func TestReleaseIp(t *testing.T) { - a := New() - - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - - ip, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - if err := a.ReleaseIP(network, ip); err != nil { - t.Fatal(err) - } -} - -func TestReleaseIpV6(t *testing.T) { - a := New() - - network := &net.IPNet{ - IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask - } - - ip, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - if err := a.ReleaseIP(network, ip); err != nil { - t.Fatal(err) - } -} - -func TestGetReleasedIp(t *testing.T) { - a := New() - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - - ip, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - value := ip.String() - if err := a.ReleaseIP(network, ip); err != nil { - t.Fatal(err) - } - - for i := 0; i < 253; i++ { - _, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - err = a.ReleaseIP(network, ip) - if err != nil { - t.Fatal(err) - } - } - - ip, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - if ip.String() != value { - t.Fatalf("Expected to receive same ip %s got %s", value, ip.String()) - } -} - -func TestGetReleasedIpV6(t *testing.T) { - a := New() - - network := &net.IPNet{ - IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0}, - } - - ip, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - value := ip.String() - if err := a.ReleaseIP(network, ip); err != nil { - t.Fatal(err) - } - - for i := 0; i < 253; i++ { - _, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - err = a.ReleaseIP(network, ip) - if err != nil { - t.Fatal(err) - } - } - - ip, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - if ip.String() != value { - t.Fatalf("Expected to receive same ip %s got %s", value, ip.String()) - } -} - -func TestRequestSpecificIp(t *testing.T) { - a := New() - - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 224}, - } - - ip := net.ParseIP("192.168.0.5") - - // Request a "good" IP. - if _, err := a.RequestIP(network, ip); err != nil { - t.Fatal(err) - } - - // Request the same IP again. - if _, err := a.RequestIP(network, ip); err != ErrIPAlreadyAllocated { - t.Fatalf("Got the same IP twice: %#v", err) - } - - // Request an out of range IP. - if _, err := a.RequestIP(network, net.ParseIP("192.168.0.42")); err != ErrIPOutOfRange { - t.Fatalf("Got an out of range IP: %#v", err) - } -} - -func TestRequestSpecificIpV6(t *testing.T) { - a := New() - - network := &net.IPNet{ - IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask - } - - ip := net.ParseIP("2a00:1450::5") - - // Request a "good" IP. - if _, err := a.RequestIP(network, ip); err != nil { - t.Fatal(err) - } - - // Request the same IP again. - if _, err := a.RequestIP(network, ip); err != ErrIPAlreadyAllocated { - t.Fatalf("Got the same IP twice: %#v", err) - } - - // Request an out of range IP. - if _, err := a.RequestIP(network, net.ParseIP("2a00:1500::1")); err != ErrIPOutOfRange { - t.Fatalf("Got an out of range IP: %#v", err) - } -} - -func TestIPAllocator(t *testing.T) { - a := New() - - expectedIPs := []net.IP{ - 0: net.IPv4(127, 0, 0, 1), - 1: net.IPv4(127, 0, 0, 2), - 2: net.IPv4(127, 0, 0, 3), - 3: net.IPv4(127, 0, 0, 4), - 4: net.IPv4(127, 0, 0, 5), - 5: net.IPv4(127, 0, 0, 6), - } - - gwIP, n, _ := net.ParseCIDR("127.0.0.1/29") - - network := &net.IPNet{IP: gwIP, Mask: n.Mask} - // Pool after initialisation (f = free, u = used) - // 1(f) - 2(f) - 3(f) - 4(f) - 5(f) - 6(f) - // ↑ - - // Check that we get 6 IPs, from 127.0.0.1–127.0.0.6, in that - // order. - for i := 0; i < 6; i++ { - ip, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - assertIPEquals(t, expectedIPs[i], ip) - } - // Before loop begin - // 1(f) - 2(f) - 3(f) - 4(f) - 5(f) - 6(f) - // ↑ - - // After i = 0 - // 1(u) - 2(f) - 3(f) - 4(f) - 5(f) - 6(f) - // ↑ - - // After i = 1 - // 1(u) - 2(u) - 3(f) - 4(f) - 5(f) - 6(f) - // ↑ - - // After i = 2 - // 1(u) - 2(u) - 3(u) - 4(f) - 5(f) - 6(f) - // ↑ - - // After i = 3 - // 1(u) - 2(u) - 3(u) - 4(u) - 5(f) - 6(f) - // ↑ - - // After i = 4 - // 1(u) - 2(u) - 3(u) - 4(u) - 5(u) - 6(f) - // ↑ - - // After i = 5 - // 1(u) - 2(u) - 3(u) - 4(u) - 5(u) - 6(u) - // ↑ - - // Check that there are no more IPs - ip, err := a.RequestIP(network, nil) - if err == nil { - t.Fatalf("There shouldn't be any IP addresses at this point, got %s\n", ip) - } - - // Release some IPs in non-sequential order - if err := a.ReleaseIP(network, expectedIPs[3]); err != nil { - t.Fatal(err) - } - // 1(u) - 2(u) - 3(u) - 4(f) - 5(u) - 6(u) - // ↑ - - if err := a.ReleaseIP(network, expectedIPs[2]); err != nil { - t.Fatal(err) - } - // 1(u) - 2(u) - 3(f) - 4(f) - 5(u) - 6(u) - // ↑ - - if err := a.ReleaseIP(network, expectedIPs[4]); err != nil { - t.Fatal(err) - } - // 1(u) - 2(u) - 3(f) - 4(f) - 5(f) - 6(u) - // ↑ - - // Make sure that IPs are reused in sequential order, starting - // with the first released IP - newIPs := make([]net.IP, 3) - for i := 0; i < 3; i++ { - ip, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - newIPs[i] = ip - } - assertIPEquals(t, expectedIPs[2], newIPs[0]) - assertIPEquals(t, expectedIPs[3], newIPs[1]) - assertIPEquals(t, expectedIPs[4], newIPs[2]) - - _, err = a.RequestIP(network, nil) - if err == nil { - t.Fatal("There shouldn't be any IP addresses at this point") - } -} - -func TestAllocateFirstIP(t *testing.T) { - a := New() - - network := &net.IPNet{ - IP: []byte{192, 168, 0, 0}, - Mask: []byte{255, 255, 255, 0}, - } - - firstIP := network.IP.To4().Mask(network.Mask) - first := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1)) - - ip, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - allocated := ipToBigInt(ip) - - if allocated == first { - t.Fatalf("allocated ip should not equal first ip: %d == %d", first, allocated) - } -} - -func TestAllocateAllIps(t *testing.T) { - a := New() - - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - - var ( - current, first net.IP - err error - isFirst = true - ) - - for err == nil { - current, err = a.RequestIP(network, nil) - if isFirst { - first = current - isFirst = false - } - } - - if err != ErrNoAvailableIPs { - t.Fatal(err) - } - - if _, err := a.RequestIP(network, nil); err != ErrNoAvailableIPs { - t.Fatal(err) - } - - if err := a.ReleaseIP(network, first); err != nil { - t.Fatal(err) - } - - again, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - assertIPEquals(t, first, again) - - // ensure that alloc.last == alloc.begin won't result in dead loop - if _, err := a.RequestIP(network, nil); err != ErrNoAvailableIPs { - t.Fatal(err) - } - - // Test by making alloc.last the only free ip and ensure we get it back - // #1. first of the range, (alloc.last == ipToInt(first) already) - if err := a.ReleaseIP(network, first); err != nil { - t.Fatal(err) - } - - ret, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - assertIPEquals(t, first, ret) - - // #2. last of the range, note that current is the last one - last := net.IPv4(192, 168, 0, 254) - setLastTo(t, a, network, last) - - ret, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - assertIPEquals(t, last, ret) - - // #3. middle of the range - mid := net.IPv4(192, 168, 0, 7) - setLastTo(t, a, network, mid) - - ret, err = a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - assertIPEquals(t, mid, ret) -} - -// make sure the pool is full when calling setLastTo. -// we don't cheat here -func setLastTo(t *testing.T, a *IPAllocator, network *net.IPNet, ip net.IP) { - if err := a.ReleaseIP(network, ip); err != nil { - t.Fatal(err) - } - - ret, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - - assertIPEquals(t, ip, ret) - - if err := a.ReleaseIP(network, ip); err != nil { - t.Fatal(err) - } -} - -func TestAllocateDifferentSubnets(t *testing.T) { - a := New() - network1 := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - network2 := &net.IPNet{ - IP: []byte{127, 0, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - network3 := &net.IPNet{ - IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask - } - network4 := &net.IPNet{ - IP: []byte{0x2a, 0x00, 0x16, 0x32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask - } - expectedIPs := []net.IP{ - 0: net.IPv4(192, 168, 0, 1), - 1: net.IPv4(192, 168, 0, 2), - 2: net.IPv4(127, 0, 0, 1), - 3: net.IPv4(127, 0, 0, 2), - 4: net.ParseIP("2a00:1450::1"), - 5: net.ParseIP("2a00:1450::2"), - 6: net.ParseIP("2a00:1450::3"), - 7: net.ParseIP("2a00:1632::1"), - 8: net.ParseIP("2a00:1632::2"), - } - - ip11, err := a.RequestIP(network1, nil) - if err != nil { - t.Fatal(err) - } - ip12, err := a.RequestIP(network1, nil) - if err != nil { - t.Fatal(err) - } - ip21, err := a.RequestIP(network2, nil) - if err != nil { - t.Fatal(err) - } - ip22, err := a.RequestIP(network2, nil) - if err != nil { - t.Fatal(err) - } - ip31, err := a.RequestIP(network3, nil) - if err != nil { - t.Fatal(err) - } - ip32, err := a.RequestIP(network3, nil) - if err != nil { - t.Fatal(err) - } - ip33, err := a.RequestIP(network3, nil) - if err != nil { - t.Fatal(err) - } - ip41, err := a.RequestIP(network4, nil) - if err != nil { - t.Fatal(err) - } - ip42, err := a.RequestIP(network4, nil) - if err != nil { - t.Fatal(err) - } - assertIPEquals(t, expectedIPs[0], ip11) - assertIPEquals(t, expectedIPs[1], ip12) - assertIPEquals(t, expectedIPs[2], ip21) - assertIPEquals(t, expectedIPs[3], ip22) - assertIPEquals(t, expectedIPs[4], ip31) - assertIPEquals(t, expectedIPs[5], ip32) - assertIPEquals(t, expectedIPs[6], ip33) - assertIPEquals(t, expectedIPs[7], ip41) - assertIPEquals(t, expectedIPs[8], ip42) -} - -func TestRegisterBadTwice(t *testing.T) { - a := New() - network := &net.IPNet{ - IP: []byte{192, 168, 1, 1}, - Mask: []byte{255, 255, 255, 0}, - } - subnet := &net.IPNet{ - IP: []byte{192, 168, 1, 8}, - Mask: []byte{255, 255, 255, 248}, - } - - if err := a.RegisterSubnet(network, subnet); err != nil { - t.Fatal(err) - } - subnet = &net.IPNet{ - IP: []byte{192, 168, 1, 16}, - Mask: []byte{255, 255, 255, 248}, - } - if err := a.RegisterSubnet(network, subnet); err != ErrNetworkAlreadyRegistered { - t.Fatalf("Expected ErrNetworkAlreadyRegistered error, got %v", err) - } -} - -func TestRegisterBadRange(t *testing.T) { - a := New() - network := &net.IPNet{ - IP: []byte{192, 168, 1, 1}, - Mask: []byte{255, 255, 255, 0}, - } - subnet := &net.IPNet{ - IP: []byte{192, 168, 1, 1}, - Mask: []byte{255, 255, 0, 0}, - } - if err := a.RegisterSubnet(network, subnet); err != ErrBadSubnet { - t.Fatalf("Expected ErrBadSubnet error, got %v", err) - } -} - -func TestAllocateFromRange(t *testing.T) { - a := New() - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - // 192.168.1.9 - 192.168.1.14 - subnet := &net.IPNet{ - IP: []byte{192, 168, 0, 8}, - Mask: []byte{255, 255, 255, 248}, - } - - if err := a.RegisterSubnet(network, subnet); err != nil { - t.Fatal(err) - } - expectedIPs := []net.IP{ - 0: net.IPv4(192, 168, 0, 9), - 1: net.IPv4(192, 168, 0, 10), - 2: net.IPv4(192, 168, 0, 11), - 3: net.IPv4(192, 168, 0, 12), - 4: net.IPv4(192, 168, 0, 13), - 5: net.IPv4(192, 168, 0, 14), - } - for _, ip := range expectedIPs { - rip, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - assertIPEquals(t, ip, rip) - } - - if _, err := a.RequestIP(network, nil); err != ErrNoAvailableIPs { - t.Fatalf("Expected ErrNoAvailableIPs error, got %v", err) - } - for _, ip := range expectedIPs { - a.ReleaseIP(network, ip) - rip, err := a.RequestIP(network, nil) - if err != nil { - t.Fatal(err) - } - assertIPEquals(t, ip, rip) - } -} - -func assertIPEquals(t *testing.T, ip1, ip2 net.IP) { - if !ip1.Equal(ip2) { - t.Fatalf("Expected IP %s, got %s", ip1, ip2) - } -} - -func BenchmarkRequestIP(b *testing.B) { - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - b.ResetTimer() - - for i := 0; i < b.N; i++ { - a := New() - - for j := 0; j < 253; j++ { - _, err := a.RequestIP(network, nil) - if err != nil { - b.Fatal(err) - } - } - } -} diff --git a/daemon/networkdriver/network.go b/daemon/networkdriver/network.go deleted file mode 100644 index 8dda789d2f..0000000000 --- a/daemon/networkdriver/network.go +++ /dev/null @@ -1,10 +0,0 @@ -package networkdriver - -import ( - "errors" -) - -var ( - ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") - ErrNetworkOverlaps = errors.New("requested network overlaps with existing network") -) diff --git a/daemon/networkdriver/network_test.go b/daemon/networkdriver/network_test.go deleted file mode 100644 index 1a6336b5de..0000000000 --- a/daemon/networkdriver/network_test.go +++ /dev/null @@ -1,175 +0,0 @@ -package networkdriver - -import ( - "github.com/docker/libcontainer/netlink" - "net" - "testing" -) - -func TestNonOverlapingNameservers(t *testing.T) { - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - nameservers := []string{ - "127.0.0.1/32", - } - - if err := CheckNameserverOverlaps(nameservers, network); err != nil { - t.Fatal(err) - } -} - -func TestOverlapingNameservers(t *testing.T) { - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - nameservers := []string{ - "192.168.0.1/32", - } - - if err := CheckNameserverOverlaps(nameservers, network); err == nil { - t.Fatalf("Expected error %s got %s", ErrNetworkOverlapsWithNameservers, err) - } -} - -func TestCheckRouteOverlaps(t *testing.T) { - orig := networkGetRoutesFct - defer func() { - networkGetRoutesFct = orig - }() - networkGetRoutesFct = func() ([]netlink.Route, error) { - routesData := []string{"10.0.2.0/32", "10.0.3.0/24", "10.0.42.0/24", "172.16.42.0/24", "192.168.142.0/24"} - - routes := []netlink.Route{} - for _, addr := range routesData { - _, netX, _ := net.ParseCIDR(addr) - routes = append(routes, netlink.Route{IPNet: netX}) - } - return routes, nil - } - - _, netX, _ := net.ParseCIDR("172.16.0.1/24") - if err := CheckRouteOverlaps(netX); err != nil { - t.Fatal(err) - } - - _, netX, _ = net.ParseCIDR("10.0.2.0/24") - if err := CheckRouteOverlaps(netX); err == nil { - t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't") - } -} - -func TestCheckNameserverOverlaps(t *testing.T) { - nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"} - - _, netX, _ := net.ParseCIDR("10.0.2.3/32") - - if err := CheckNameserverOverlaps(nameservers, netX); err == nil { - t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX) - } - - _, netX, _ = net.ParseCIDR("192.168.102.2/32") - - if err := CheckNameserverOverlaps(nameservers, netX); err != nil { - t.Fatalf("%s should not overlap %v but it does", netX, nameservers) - } -} - -func AssertOverlap(CIDRx string, CIDRy string, t *testing.T) { - _, netX, _ := net.ParseCIDR(CIDRx) - _, netY, _ := net.ParseCIDR(CIDRy) - if !NetworkOverlaps(netX, netY) { - t.Errorf("%v and %v should overlap", netX, netY) - } -} - -func AssertNoOverlap(CIDRx string, CIDRy string, t *testing.T) { - _, netX, _ := net.ParseCIDR(CIDRx) - _, netY, _ := net.ParseCIDR(CIDRy) - if NetworkOverlaps(netX, netY) { - t.Errorf("%v and %v should not overlap", netX, netY) - } -} - -func TestNetworkOverlaps(t *testing.T) { - //netY starts at same IP and ends within netX - AssertOverlap("172.16.0.1/24", "172.16.0.1/25", t) - //netY starts within netX and ends at same IP - AssertOverlap("172.16.0.1/24", "172.16.0.128/25", t) - //netY starts and ends within netX - AssertOverlap("172.16.0.1/24", "172.16.0.64/25", t) - //netY starts at same IP and ends outside of netX - AssertOverlap("172.16.0.1/24", "172.16.0.1/23", t) - //netY starts before and ends at same IP of netX - AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t) - //netY starts before and ends outside of netX - AssertOverlap("172.16.1.1/24", "172.16.0.1/22", t) - //netY starts and ends before netX - AssertNoOverlap("172.16.1.1/25", "172.16.0.1/24", t) - //netX starts and ends before netY - AssertNoOverlap("172.16.1.1/25", "172.16.2.1/24", t) -} - -func TestNetworkRange(t *testing.T) { - // Simple class C test - _, network, _ := net.ParseCIDR("192.168.0.1/24") - first, last := NetworkRange(network) - if !first.Equal(net.ParseIP("192.168.0.0")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("192.168.0.255")) { - t.Error(last.String()) - } - - // Class A test - _, network, _ = net.ParseCIDR("10.0.0.1/8") - first, last = NetworkRange(network) - if !first.Equal(net.ParseIP("10.0.0.0")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.255.255.255")) { - t.Error(last.String()) - } - - // Class A, random IP address - _, network, _ = net.ParseCIDR("10.1.2.3/8") - first, last = NetworkRange(network) - if !first.Equal(net.ParseIP("10.0.0.0")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.255.255.255")) { - t.Error(last.String()) - } - - // 32bit mask - _, network, _ = net.ParseCIDR("10.1.2.3/32") - first, last = NetworkRange(network) - if !first.Equal(net.ParseIP("10.1.2.3")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.1.2.3")) { - t.Error(last.String()) - } - - // 31bit mask - _, network, _ = net.ParseCIDR("10.1.2.3/31") - first, last = NetworkRange(network) - if !first.Equal(net.ParseIP("10.1.2.2")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.1.2.3")) { - t.Error(last.String()) - } - - // 26bit mask - _, network, _ = net.ParseCIDR("10.1.2.3/26") - first, last = NetworkRange(network) - if !first.Equal(net.ParseIP("10.1.2.0")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.1.2.63")) { - t.Error(last.String()) - } -} diff --git a/daemon/networkdriver/portallocator/portallocator.go b/daemon/networkdriver/portallocator/portallocator.go deleted file mode 100644 index c1f414b673..0000000000 --- a/daemon/networkdriver/portallocator/portallocator.go +++ /dev/null @@ -1,188 +0,0 @@ -package portallocator - -import ( - "bufio" - "errors" - "fmt" - "net" - "os" - "sync" - - "github.com/Sirupsen/logrus" -) - -const ( - DefaultPortRangeStart = 49153 - DefaultPortRangeEnd = 65535 -) - -type ipMapping map[string]protoMap - -var ( - ErrAllPortsAllocated = errors.New("all ports are allocated") - ErrUnknownProtocol = errors.New("unknown protocol") - defaultIP = net.ParseIP("0.0.0.0") -) - -type ErrPortAlreadyAllocated struct { - ip string - port int -} - -func NewErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated { - return ErrPortAlreadyAllocated{ - ip: ip, - port: port, - } -} - -func (e ErrPortAlreadyAllocated) IP() string { - return e.ip -} - -func (e ErrPortAlreadyAllocated) Port() int { - return e.port -} - -func (e ErrPortAlreadyAllocated) IPPort() string { - return fmt.Sprintf("%s:%d", e.ip, e.port) -} - -func (e ErrPortAlreadyAllocated) Error() string { - return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port) -} - -type ( - PortAllocator struct { - mutex sync.Mutex - ipMap ipMapping - Begin int - End int - } - portMap struct { - p map[int]struct{} - begin, end int - last int - } - protoMap map[string]*portMap -) - -func New() *PortAllocator { - start, end, err := getDynamicPortRange() - if err != nil { - logrus.Warn(err) - start, end = DefaultPortRangeStart, DefaultPortRangeEnd - } - return &PortAllocator{ - ipMap: ipMapping{}, - Begin: start, - End: end, - } -} - -func getDynamicPortRange() (start int, end int, err error) { - const portRangeKernelParam = "/proc/sys/net/ipv4/ip_local_port_range" - portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd) - file, err := os.Open(portRangeKernelParam) - if err != nil { - return 0, 0, fmt.Errorf("port allocator - %s due to error: %v", portRangeFallback, err) - } - n, err := fmt.Fscanf(bufio.NewReader(file), "%d\t%d", &start, &end) - if n != 2 || err != nil { - if err == nil { - err = fmt.Errorf("unexpected count of parsed numbers (%d)", n) - } - return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range from %s - %s: %v", portRangeKernelParam, portRangeFallback, err) - } - return start, end, nil -} - -// RequestPort requests new port from global ports pool for specified ip and proto. -// If port is 0 it returns first free port. Otherwise it cheks port availability -// in pool and return that port or error if port is already busy. -func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, error) { - p.mutex.Lock() - defer p.mutex.Unlock() - - if proto != "tcp" && proto != "udp" { - return 0, ErrUnknownProtocol - } - - if ip == nil { - ip = defaultIP - } - ipstr := ip.String() - protomap, ok := p.ipMap[ipstr] - if !ok { - protomap = protoMap{ - "tcp": p.newPortMap(), - "udp": p.newPortMap(), - } - - p.ipMap[ipstr] = protomap - } - mapping := protomap[proto] - if port > 0 { - if _, ok := mapping.p[port]; !ok { - mapping.p[port] = struct{}{} - return port, nil - } - return 0, NewErrPortAlreadyAllocated(ipstr, port) - } - - port, err := mapping.findPort() - if err != nil { - return 0, err - } - return port, nil -} - -// ReleasePort releases port from global ports pool for specified ip and proto. -func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error { - p.mutex.Lock() - defer p.mutex.Unlock() - - if ip == nil { - ip = defaultIP - } - protomap, ok := p.ipMap[ip.String()] - if !ok { - return nil - } - delete(protomap[proto].p, port) - return nil -} - -func (p *PortAllocator) newPortMap() *portMap { - return &portMap{ - p: map[int]struct{}{}, - begin: p.Begin, - end: p.End, - last: p.End, - } -} - -// ReleaseAll releases all ports for all ips. -func (p *PortAllocator) ReleaseAll() error { - p.mutex.Lock() - p.ipMap = ipMapping{} - p.mutex.Unlock() - return nil -} - -func (pm *portMap) findPort() (int, error) { - port := pm.last - for i := 0; i <= pm.end-pm.begin; i++ { - port++ - if port > pm.end { - port = pm.begin - } - - if _, ok := pm.p[port]; !ok { - pm.p[port] = struct{}{} - pm.last = port - return port, nil - } - } - return 0, ErrAllPortsAllocated -} diff --git a/daemon/networkdriver/portallocator/portallocator_test.go b/daemon/networkdriver/portallocator/portallocator_test.go deleted file mode 100644 index 17201235e0..0000000000 --- a/daemon/networkdriver/portallocator/portallocator_test.go +++ /dev/null @@ -1,239 +0,0 @@ -package portallocator - -import ( - "net" - "testing" -) - -func TestRequestNewPort(t *testing.T) { - p := New() - - port, err := p.RequestPort(defaultIP, "tcp", 0) - if err != nil { - t.Fatal(err) - } - - if expected := p.Begin; port != expected { - t.Fatalf("Expected port %d got %d", expected, port) - } -} - -func TestRequestSpecificPort(t *testing.T) { - p := New() - - port, err := p.RequestPort(defaultIP, "tcp", 5000) - if err != nil { - t.Fatal(err) - } - if port != 5000 { - t.Fatalf("Expected port 5000 got %d", port) - } -} - -func TestReleasePort(t *testing.T) { - p := New() - - port, err := p.RequestPort(defaultIP, "tcp", 5000) - if err != nil { - t.Fatal(err) - } - if port != 5000 { - t.Fatalf("Expected port 5000 got %d", port) - } - - if err := p.ReleasePort(defaultIP, "tcp", 5000); err != nil { - t.Fatal(err) - } -} - -func TestReuseReleasedPort(t *testing.T) { - p := New() - - port, err := p.RequestPort(defaultIP, "tcp", 5000) - if err != nil { - t.Fatal(err) - } - if port != 5000 { - t.Fatalf("Expected port 5000 got %d", port) - } - - if err := p.ReleasePort(defaultIP, "tcp", 5000); err != nil { - t.Fatal(err) - } - - port, err = p.RequestPort(defaultIP, "tcp", 5000) - if err != nil { - t.Fatal(err) - } -} - -func TestReleaseUnreadledPort(t *testing.T) { - p := New() - - port, err := p.RequestPort(defaultIP, "tcp", 5000) - if err != nil { - t.Fatal(err) - } - if port != 5000 { - t.Fatalf("Expected port 5000 got %d", port) - } - - port, err = p.RequestPort(defaultIP, "tcp", 5000) - - switch err.(type) { - case ErrPortAlreadyAllocated: - default: - t.Fatalf("Expected port allocation error got %s", err) - } -} - -func TestUnknowProtocol(t *testing.T) { - if _, err := New().RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol { - t.Fatalf("Expected error %s got %s", ErrUnknownProtocol, err) - } -} - -func TestAllocateAllPorts(t *testing.T) { - p := New() - - for i := 0; i <= p.End-p.Begin; i++ { - port, err := p.RequestPort(defaultIP, "tcp", 0) - if err != nil { - t.Fatal(err) - } - - if expected := p.Begin + i; port != expected { - t.Fatalf("Expected port %d got %d", expected, port) - } - } - - if _, err := p.RequestPort(defaultIP, "tcp", 0); err != ErrAllPortsAllocated { - t.Fatalf("Expected error %s got %s", ErrAllPortsAllocated, err) - } - - _, err := p.RequestPort(defaultIP, "udp", 0) - if err != nil { - t.Fatal(err) - } - - // release a port in the middle and ensure we get another tcp port - port := p.Begin + 5 - if err := p.ReleasePort(defaultIP, "tcp", port); err != nil { - t.Fatal(err) - } - newPort, err := p.RequestPort(defaultIP, "tcp", 0) - if err != nil { - t.Fatal(err) - } - if newPort != port { - t.Fatalf("Expected port %d got %d", port, newPort) - } - - // now pm.last == newPort, release it so that it's the only free port of - // the range, and ensure we get it back - if err := p.ReleasePort(defaultIP, "tcp", newPort); err != nil { - t.Fatal(err) - } - port, err = p.RequestPort(defaultIP, "tcp", 0) - if err != nil { - t.Fatal(err) - } - if newPort != port { - t.Fatalf("Expected port %d got %d", newPort, port) - } -} - -func BenchmarkAllocatePorts(b *testing.B) { - p := New() - - for i := 0; i < b.N; i++ { - for i := 0; i <= p.End-p.Begin; i++ { - port, err := p.RequestPort(defaultIP, "tcp", 0) - if err != nil { - b.Fatal(err) - } - - if expected := p.Begin + i; port != expected { - b.Fatalf("Expected port %d got %d", expected, port) - } - } - p.ReleaseAll() - } -} - -func TestPortAllocation(t *testing.T) { - p := New() - - ip := net.ParseIP("192.168.0.1") - ip2 := net.ParseIP("192.168.0.2") - if port, err := p.RequestPort(ip, "tcp", 80); err != nil { - t.Fatal(err) - } else if port != 80 { - t.Fatalf("Acquire(80) should return 80, not %d", port) - } - port, err := p.RequestPort(ip, "tcp", 0) - if err != nil { - t.Fatal(err) - } - if port <= 0 { - t.Fatalf("Acquire(0) should return a non-zero port") - } - - if _, err := p.RequestPort(ip, "tcp", port); err == nil { - t.Fatalf("Acquiring a port already in use should return an error") - } - - if newPort, err := p.RequestPort(ip, "tcp", 0); err != nil { - t.Fatal(err) - } else if newPort == port { - t.Fatalf("Acquire(0) allocated the same port twice: %d", port) - } - - if _, err := p.RequestPort(ip, "tcp", 80); err == nil { - t.Fatalf("Acquiring a port already in use should return an error") - } - if _, err := p.RequestPort(ip2, "tcp", 80); err != nil { - t.Fatalf("It should be possible to allocate the same port on a different interface") - } - if _, err := p.RequestPort(ip2, "tcp", 80); err == nil { - t.Fatalf("Acquiring a port already in use should return an error") - } - if err := p.ReleasePort(ip, "tcp", 80); err != nil { - t.Fatal(err) - } - if _, err := p.RequestPort(ip, "tcp", 80); err != nil { - t.Fatal(err) - } - - port, err = p.RequestPort(ip, "tcp", 0) - if err != nil { - t.Fatal(err) - } - port2, err := p.RequestPort(ip, "tcp", port+1) - if err != nil { - t.Fatal(err) - } - port3, err := p.RequestPort(ip, "tcp", 0) - if err != nil { - t.Fatal(err) - } - if port3 == port2 { - t.Fatal("Requesting a dynamic port should never allocate a used port") - } -} - -func TestNoDuplicateBPR(t *testing.T) { - p := New() - - if port, err := p.RequestPort(defaultIP, "tcp", p.Begin); err != nil { - t.Fatal(err) - } else if port != p.Begin { - t.Fatalf("Expected port %d got %d", p.Begin, port) - } - - if port, err := p.RequestPort(defaultIP, "tcp", 0); err != nil { - t.Fatal(err) - } else if port == p.Begin { - t.Fatalf("Acquire(0) allocated the same port twice: %d", port) - } -} diff --git a/daemon/networkdriver/portmapper/mapper.go b/daemon/networkdriver/portmapper/mapper.go deleted file mode 100644 index f0c7a507df..0000000000 --- a/daemon/networkdriver/portmapper/mapper.go +++ /dev/null @@ -1,210 +0,0 @@ -package portmapper - -import ( - "errors" - "fmt" - "net" - "sync" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/daemon/networkdriver/portallocator" - "github.com/docker/docker/pkg/iptables" -) - -type mapping struct { - proto string - userlandProxy UserlandProxy - host net.Addr - container net.Addr -} - -var NewProxy = NewProxyCommand - -var ( - ErrUnknownBackendAddressType = errors.New("unknown container address type not supported") - ErrPortMappedForIP = errors.New("port is already mapped to ip") - ErrPortNotMapped = errors.New("port is not mapped") -) - -type PortMapper struct { - chain *iptables.Chain - - // udp:ip:port - currentMappings map[string]*mapping - lock sync.Mutex - - Allocator *portallocator.PortAllocator -} - -func New() *PortMapper { - return NewWithPortAllocator(portallocator.New()) -} - -func NewWithPortAllocator(allocator *portallocator.PortAllocator) *PortMapper { - return &PortMapper{ - currentMappings: make(map[string]*mapping), - Allocator: allocator, - } -} - -func (pm *PortMapper) SetIptablesChain(c *iptables.Chain) { - pm.chain = c -} - -func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) { - pm.lock.Lock() - defer pm.lock.Unlock() - - var ( - m *mapping - proto string - allocatedHostPort int - ) - - switch container.(type) { - case *net.TCPAddr: - proto = "tcp" - if allocatedHostPort, err = pm.Allocator.RequestPort(hostIP, proto, hostPort); err != nil { - return nil, err - } - - m = &mapping{ - proto: proto, - host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort}, - container: container, - } - - if useProxy { - m.userlandProxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port) - } - case *net.UDPAddr: - proto = "udp" - if allocatedHostPort, err = pm.Allocator.RequestPort(hostIP, proto, hostPort); err != nil { - return nil, err - } - - m = &mapping{ - proto: proto, - host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort}, - container: container, - } - - if useProxy { - m.userlandProxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port) - } - default: - return nil, ErrUnknownBackendAddressType - } - - // release the allocated port on any further error during return. - defer func() { - if err != nil { - pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort) - } - }() - - key := getKey(m.host) - if _, exists := pm.currentMappings[key]; exists { - return nil, ErrPortMappedForIP - } - - containerIP, containerPort := getIPAndPort(m.container) - if err := pm.forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { - return nil, err - } - - cleanup := func() error { - // need to undo the iptables rules before we return - if m.userlandProxy != nil { - m.userlandProxy.Stop() - } - pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) - if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { - return err - } - - return nil - } - - if m.userlandProxy != nil { - if err := m.userlandProxy.Start(); err != nil { - if err := cleanup(); err != nil { - return nil, fmt.Errorf("Error during port allocation cleanup: %v", err) - } - return nil, err - } - } - - pm.currentMappings[key] = m - return m.host, nil -} - -// re-apply all port mappings -func (pm *PortMapper) ReMapAll() { - logrus.Debugln("Re-applying all port mappings.") - for _, data := range pm.currentMappings { - containerIP, containerPort := getIPAndPort(data.container) - hostIP, hostPort := getIPAndPort(data.host) - if err := pm.forward(iptables.Append, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { - logrus.Errorf("Error on iptables add: %s", err) - } - } -} - -func (pm *PortMapper) Unmap(host net.Addr) error { - pm.lock.Lock() - defer pm.lock.Unlock() - - key := getKey(host) - data, exists := pm.currentMappings[key] - if !exists { - return ErrPortNotMapped - } - - if data.userlandProxy != nil { - data.userlandProxy.Stop() - } - - delete(pm.currentMappings, key) - - containerIP, containerPort := getIPAndPort(data.container) - hostIP, hostPort := getIPAndPort(data.host) - if err := pm.forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { - logrus.Errorf("Error on iptables delete: %s", err) - } - - switch a := host.(type) { - case *net.TCPAddr: - return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port) - case *net.UDPAddr: - return pm.Allocator.ReleasePort(a.IP, "udp", a.Port) - } - return nil -} - -func getKey(a net.Addr) string { - switch t := a.(type) { - case *net.TCPAddr: - return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp") - case *net.UDPAddr: - return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp") - } - return "" -} - -func getIPAndPort(a net.Addr) (net.IP, int) { - switch t := a.(type) { - case *net.TCPAddr: - return t.IP, t.Port - case *net.UDPAddr: - return t.IP, t.Port - } - return nil, 0 -} - -func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { - if pm.chain == nil { - return nil - } - return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort) -} diff --git a/daemon/networkdriver/portmapper/mapper_test.go b/daemon/networkdriver/portmapper/mapper_test.go deleted file mode 100644 index b908db2811..0000000000 --- a/daemon/networkdriver/portmapper/mapper_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package portmapper - -import ( - "net" - "testing" - - "github.com/docker/docker/pkg/iptables" -) - -func init() { - // override this func to mock out the proxy server - NewProxy = NewMockProxyCommand -} - -func TestSetIptablesChain(t *testing.T) { - pm := New() - - c := &iptables.Chain{ - Name: "TEST", - Bridge: "192.168.1.1", - } - - if pm.chain != nil { - t.Fatal("chain should be nil at init") - } - - pm.SetIptablesChain(c) - if pm.chain == nil { - t.Fatal("chain should not be nil after set") - } -} - -func TestMapPorts(t *testing.T) { - pm := New() - dstIp1 := net.ParseIP("192.168.0.1") - dstIp2 := net.ParseIP("192.168.0.2") - dstAddr1 := &net.TCPAddr{IP: dstIp1, Port: 80} - dstAddr2 := &net.TCPAddr{IP: dstIp2, Port: 80} - - srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} - srcAddr2 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")} - - addrEqual := func(addr1, addr2 net.Addr) bool { - return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String()) - } - - if host, err := pm.Map(srcAddr1, dstIp1, 80, true); err != nil { - t.Fatalf("Failed to allocate port: %s", err) - } else if !addrEqual(dstAddr1, host) { - t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s", - dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network()) - } - - if _, err := pm.Map(srcAddr1, dstIp1, 80, true); err == nil { - t.Fatalf("Port is in use - mapping should have failed") - } - - if _, err := pm.Map(srcAddr2, dstIp1, 80, true); err == nil { - t.Fatalf("Port is in use - mapping should have failed") - } - - if _, err := pm.Map(srcAddr2, dstIp2, 80, true); err != nil { - t.Fatalf("Failed to allocate port: %s", err) - } - - if pm.Unmap(dstAddr1) != nil { - t.Fatalf("Failed to release port") - } - - if pm.Unmap(dstAddr2) != nil { - t.Fatalf("Failed to release port") - } - - if pm.Unmap(dstAddr2) == nil { - t.Fatalf("Port already released, but no error reported") - } -} - -func TestGetUDPKey(t *testing.T) { - addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53} - - key := getKey(addr) - - if expected := "192.168.1.5:53/udp"; key != expected { - t.Fatalf("expected key %s got %s", expected, key) - } -} - -func TestGetTCPKey(t *testing.T) { - addr := &net.TCPAddr{IP: net.ParseIP("192.168.1.5"), Port: 80} - - key := getKey(addr) - - if expected := "192.168.1.5:80/tcp"; key != expected { - t.Fatalf("expected key %s got %s", expected, key) - } -} - -func TestGetUDPIPAndPort(t *testing.T) { - addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53} - - ip, port := getIPAndPort(addr) - if expected := "192.168.1.5"; ip.String() != expected { - t.Fatalf("expected ip %s got %s", expected, ip) - } - - if ep := 53; port != ep { - t.Fatalf("expected port %d got %d", ep, port) - } -} - -func TestMapAllPortsSingleInterface(t *testing.T) { - pm := New() - dstIp1 := net.ParseIP("0.0.0.0") - srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} - - hosts := []net.Addr{} - var host net.Addr - var err error - - defer func() { - for _, val := range hosts { - pm.Unmap(val) - } - }() - - for i := 0; i < 10; i++ { - start, end := pm.Allocator.Begin, pm.Allocator.End - for i := start; i < end; i++ { - if host, err = pm.Map(srcAddr1, dstIp1, 0, true); err != nil { - t.Fatal(err) - } - - hosts = append(hosts, host) - } - - if _, err := pm.Map(srcAddr1, dstIp1, start, true); err == nil { - t.Fatalf("Port %d should be bound but is not", start) - } - - for _, val := range hosts { - if err := pm.Unmap(val); err != nil { - t.Fatal(err) - } - } - - hosts = []net.Addr{} - } -} diff --git a/daemon/networkdriver/portmapper/mock_proxy.go b/daemon/networkdriver/portmapper/mock_proxy.go deleted file mode 100644 index 253ce83112..0000000000 --- a/daemon/networkdriver/portmapper/mock_proxy.go +++ /dev/null @@ -1,18 +0,0 @@ -package portmapper - -import "net" - -func NewMockProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int) UserlandProxy { - return &mockProxyCommand{} -} - -type mockProxyCommand struct { -} - -func (p *mockProxyCommand) Start() error { - return nil -} - -func (p *mockProxyCommand) Stop() error { - return nil -} diff --git a/daemon/networkdriver/portmapper/proxy.go b/daemon/networkdriver/portmapper/proxy.go deleted file mode 100644 index 894cfa7166..0000000000 --- a/daemon/networkdriver/portmapper/proxy.go +++ /dev/null @@ -1,161 +0,0 @@ -package portmapper - -import ( - "flag" - "fmt" - "io/ioutil" - "log" - "net" - "os" - "os/exec" - "os/signal" - "strconv" - "syscall" - "time" - - "github.com/docker/docker/pkg/proxy" - "github.com/docker/docker/pkg/reexec" -) - -const userlandProxyCommandName = "docker-proxy-deprecated" - -func init() { - reexec.Register(userlandProxyCommandName, execProxy) -} - -type UserlandProxy interface { - Start() error - Stop() error -} - -// proxyCommand wraps an exec.Cmd to run the userland TCP and UDP -// proxies as separate processes. -type proxyCommand struct { - cmd *exec.Cmd -} - -// execProxy is the reexec function that is registered to start the userland proxies -func execProxy() { - f := os.NewFile(3, "signal-parent") - host, container := parseHostContainerAddrs() - - p, err := proxy.NewProxy(host, container) - if err != nil { - fmt.Fprintf(f, "1\n%s", err) - f.Close() - os.Exit(1) - } - go handleStopSignals(p) - fmt.Fprint(f, "0\n") - f.Close() - - // Run will block until the proxy stops - p.Run() -} - -// parseHostContainerAddrs parses the flags passed on reexec to create the TCP or UDP -// net.Addrs to map the host and container ports -func parseHostContainerAddrs() (host net.Addr, container net.Addr) { - var ( - proto = flag.String("proto", "tcp", "proxy protocol") - hostIP = flag.String("host-ip", "", "host ip") - hostPort = flag.Int("host-port", -1, "host port") - containerIP = flag.String("container-ip", "", "container ip") - containerPort = flag.Int("container-port", -1, "container port") - ) - - flag.Parse() - - switch *proto { - case "tcp": - host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort} - container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort} - case "udp": - host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort} - container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort} - default: - log.Fatalf("unsupported protocol %s", *proto) - } - - return host, container -} - -func handleStopSignals(p proxy.Proxy) { - s := make(chan os.Signal, 10) - signal.Notify(s, os.Interrupt, syscall.SIGTERM, syscall.SIGSTOP) - - for range s { - p.Close() - - os.Exit(0) - } -} - -func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int) UserlandProxy { - args := []string{ - userlandProxyCommandName, - "-proto", proto, - "-host-ip", hostIP.String(), - "-host-port", strconv.Itoa(hostPort), - "-container-ip", containerIP.String(), - "-container-port", strconv.Itoa(containerPort), - } - - return &proxyCommand{ - cmd: &exec.Cmd{ - Path: reexec.Self(), - Args: args, - SysProcAttr: &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies - }, - }, - } -} - -func (p *proxyCommand) Start() error { - r, w, err := os.Pipe() - if err != nil { - return fmt.Errorf("proxy unable to open os.Pipe %s", err) - } - defer r.Close() - p.cmd.ExtraFiles = []*os.File{w} - if err := p.cmd.Start(); err != nil { - return err - } - w.Close() - - errchan := make(chan error, 1) - go func() { - buf := make([]byte, 2) - r.Read(buf) - - if string(buf) != "0\n" { - errStr, err := ioutil.ReadAll(r) - if err != nil { - errchan <- fmt.Errorf("Error reading exit status from userland proxy: %v", err) - return - } - - errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr) - return - } - errchan <- nil - }() - - select { - case err := <-errchan: - return err - case <-time.After(16 * time.Second): - return fmt.Errorf("Timed out proxy starting the userland proxy") - } -} - -func (p *proxyCommand) Stop() error { - if p.cmd.Process != nil { - if err := p.cmd.Process.Signal(os.Interrupt); err != nil { - return err - } - return p.cmd.Wait() - } - return nil -} diff --git a/daemon/networkdriver/utils.go b/daemon/networkdriver/utils.go deleted file mode 100644 index 9f0c88cd5e..0000000000 --- a/daemon/networkdriver/utils.go +++ /dev/null @@ -1,118 +0,0 @@ -package networkdriver - -import ( - "errors" - "fmt" - "net" - - "github.com/docker/libcontainer/netlink" -) - -var ( - networkGetRoutesFct = netlink.NetworkGetRoutes - ErrNoDefaultRoute = errors.New("no default route") -) - -func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error { - if len(nameservers) > 0 { - for _, ns := range nameservers { - _, nsNetwork, err := net.ParseCIDR(ns) - if err != nil { - return err - } - if NetworkOverlaps(toCheck, nsNetwork) { - return ErrNetworkOverlapsWithNameservers - } - } - } - return nil -} - -func CheckRouteOverlaps(toCheck *net.IPNet) error { - networks, err := networkGetRoutesFct() - if err != nil { - return err - } - - for _, network := range networks { - if network.IPNet != nil && NetworkOverlaps(toCheck, network.IPNet) { - return ErrNetworkOverlaps - } - } - return nil -} - -// Detects overlap between one IPNet and another -func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { - if len(netX.IP) == len(netY.IP) { - if firstIP, _ := NetworkRange(netX); netY.Contains(firstIP) { - return true - } - if firstIP, _ := NetworkRange(netY); netX.Contains(firstIP) { - return true - } - } - return false -} - -// Calculates the first and last IP addresses in an IPNet -func NetworkRange(network *net.IPNet) (net.IP, net.IP) { - var netIP net.IP - if network.IP.To4() != nil { - netIP = network.IP.To4() - } else if network.IP.To16() != nil { - netIP = network.IP.To16() - } else { - return nil, nil - } - - lastIP := make([]byte, len(netIP), len(netIP)) - - for i := 0; i < len(netIP); i++ { - lastIP[i] = netIP[i] | ^network.Mask[i] - } - return netIP.Mask(network.Mask), net.IP(lastIP) -} - -// Return the first IPv4 address and slice of IPv6 addresses for the specified network interface -func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) { - iface, err := net.InterfaceByName(name) - if err != nil { - return nil, nil, err - } - addrs, err := iface.Addrs() - if err != nil { - return nil, nil, err - } - var addrs4 []net.Addr - var addrs6 []net.Addr - for _, addr := range addrs { - ip := (addr.(*net.IPNet)).IP - if ip4 := ip.To4(); ip4 != nil { - addrs4 = append(addrs4, addr) - } else if ip6 := ip.To16(); len(ip6) == net.IPv6len { - addrs6 = append(addrs6, addr) - } - } - switch { - case len(addrs4) == 0: - return nil, nil, fmt.Errorf("Interface %v has no IPv4 addresses", name) - case len(addrs4) > 1: - fmt.Printf("Interface %v has more than 1 IPv4 address. Defaulting to using %v\n", - name, (addrs4[0].(*net.IPNet)).IP) - } - return addrs4[0], addrs6, nil -} - -func GetDefaultRouteIface() (*net.Interface, error) { - rs, err := networkGetRoutesFct() - if err != nil { - return nil, fmt.Errorf("unable to get routes: %v", err) - } - for _, r := range rs { - if r.Default { - return r.Iface, nil - } - } - return nil, ErrNoDefaultRoute -} diff --git a/pkg/etchosts/etchosts.go b/pkg/etchosts/etchosts.go deleted file mode 100644 index bef4a480cb..0000000000 --- a/pkg/etchosts/etchosts.go +++ /dev/null @@ -1,79 +0,0 @@ -package etchosts - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "regexp" -) - -// Structure for a single host record -type Record struct { - Hosts string - IP string -} - -// Writes record to file and returns bytes written or error -func (r Record) WriteTo(w io.Writer) (int64, error) { - n, err := fmt.Fprintf(w, "%s\t%s\n", r.IP, r.Hosts) - return int64(n), err -} - -// Default hosts config records slice -var defaultContent = []Record{ - {Hosts: "localhost", IP: "127.0.0.1"}, - {Hosts: "localhost ip6-localhost ip6-loopback", IP: "::1"}, - {Hosts: "ip6-localnet", IP: "fe00::0"}, - {Hosts: "ip6-mcastprefix", IP: "ff00::0"}, - {Hosts: "ip6-allnodes", IP: "ff02::1"}, - {Hosts: "ip6-allrouters", IP: "ff02::2"}, -} - -// Build function -// path is path to host file string required -// IP, hostname, and domainname set main record leave empty for no master record -// extraContent is an array of extra host records. -func Build(path, IP, hostname, domainname string, extraContent []Record) error { - content := bytes.NewBuffer(nil) - if IP != "" { - //set main record - var mainRec Record - mainRec.IP = IP - if domainname != "" { - mainRec.Hosts = fmt.Sprintf("%s.%s %s", hostname, domainname, hostname) - } else { - mainRec.Hosts = hostname - } - if _, err := mainRec.WriteTo(content); err != nil { - return err - } - } - // Write defaultContent slice to buffer - for _, r := range defaultContent { - if _, err := r.WriteTo(content); err != nil { - return err - } - } - // Write extra content from function arguments - for _, r := range extraContent { - if _, err := r.WriteTo(content); err != nil { - return err - } - } - - return ioutil.WriteFile(path, content.Bytes(), 0644) -} - -// Update all IP addresses where hostname matches. -// path is path to host file -// IP is new IP address -// hostname is hostname to search for to replace IP -func Update(path, IP, hostname string) error { - old, err := ioutil.ReadFile(path) - if err != nil { - return err - } - var re = regexp.MustCompile(fmt.Sprintf("(\\S*)(\\t%s)", regexp.QuoteMeta(hostname))) - return ioutil.WriteFile(path, re.ReplaceAll(old, []byte(IP+"$2")), 0644) -} diff --git a/pkg/etchosts/etchosts_test.go b/pkg/etchosts/etchosts_test.go deleted file mode 100644 index c033904c31..0000000000 --- a/pkg/etchosts/etchosts_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package etchosts - -import ( - "bytes" - "io/ioutil" - "os" - "testing" -) - -func TestBuildDefault(t *testing.T) { - file, err := ioutil.TempFile("", "") - if err != nil { - t.Fatal(err) - } - defer os.Remove(file.Name()) - - // check that /etc/hosts has consistent ordering - for i := 0; i <= 5; i++ { - err = Build(file.Name(), "", "", "", nil) - if err != nil { - t.Fatal(err) - } - - content, err := ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatal(err) - } - expected := "127.0.0.1\tlocalhost\n::1\tlocalhost ip6-localhost ip6-loopback\nfe00::0\tip6-localnet\nff00::0\tip6-mcastprefix\nff02::1\tip6-allnodes\nff02::2\tip6-allrouters\n" - - if expected != string(content) { - t.Fatalf("Expected to find '%s' got '%s'", expected, content) - } - } -} - -func TestBuildHostnameDomainname(t *testing.T) { - file, err := ioutil.TempFile("", "") - if err != nil { - t.Fatal(err) - } - defer os.Remove(file.Name()) - - err = Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", nil) - if err != nil { - t.Fatal(err) - } - - content, err := ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatal(err) - } - - if expected := "10.11.12.13\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) { - t.Fatalf("Expected to find '%s' got '%s'", expected, content) - } -} - -func TestBuildHostname(t *testing.T) { - file, err := ioutil.TempFile("", "") - if err != nil { - t.Fatal(err) - } - defer os.Remove(file.Name()) - - err = Build(file.Name(), "10.11.12.13", "testhostname", "", nil) - if err != nil { - t.Fatal(err) - } - - content, err := ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatal(err) - } - - if expected := "10.11.12.13\ttesthostname\n"; !bytes.Contains(content, []byte(expected)) { - t.Fatalf("Expected to find '%s' got '%s'", expected, content) - } -} - -func TestBuildNoIP(t *testing.T) { - file, err := ioutil.TempFile("", "") - if err != nil { - t.Fatal(err) - } - defer os.Remove(file.Name()) - - err = Build(file.Name(), "", "testhostname", "", nil) - if err != nil { - t.Fatal(err) - } - - content, err := ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatal(err) - } - - if expected := ""; !bytes.Contains(content, []byte(expected)) { - t.Fatalf("Expected to find '%s' got '%s'", expected, content) - } -} - -func TestUpdate(t *testing.T) { - file, err := ioutil.TempFile("", "") - if err != nil { - t.Fatal(err) - } - defer os.Remove(file.Name()) - - if err := Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", nil); err != nil { - t.Fatal(err) - } - - content, err := ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatal(err) - } - - if expected := "10.11.12.13\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) { - t.Fatalf("Expected to find '%s' got '%s'", expected, content) - } - - if err := Update(file.Name(), "1.1.1.1", "testhostname"); err != nil { - t.Fatal(err) - } - - content, err = ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatal(err) - } - - if expected := "1.1.1.1\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) { - t.Fatalf("Expected to find '%s' got '%s'", expected, content) - } -} diff --git a/pkg/iptables/firewalld.go b/pkg/iptables/firewalld.go deleted file mode 100644 index 89f123a1ad..0000000000 --- a/pkg/iptables/firewalld.go +++ /dev/null @@ -1,159 +0,0 @@ -package iptables - -import ( - "fmt" - "strings" - - "github.com/Sirupsen/logrus" - "github.com/godbus/dbus" -) - -type IPV string - -const ( - Iptables IPV = "ipv4" - Ip6tables IPV = "ipv6" - Ebtables IPV = "eb" -) -const ( - dbusInterface = "org.fedoraproject.FirewallD1" - dbusPath = "/org/fedoraproject/FirewallD1" -) - -// Conn is a connection to firewalld dbus endpoint. -type Conn struct { - sysconn *dbus.Conn - sysobj *dbus.Object - signal chan *dbus.Signal -} - -var ( - connection *Conn - firewalldRunning bool // is Firewalld service running - onReloaded []*func() // callbacks when Firewalld has been reloaded -) - -func FirewalldInit() error { - var err error - - if connection, err = newConnection(); err != nil { - return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err) - } - if connection != nil { - go signalHandler() - } - - firewalldRunning = checkRunning() - return nil -} - -// New() establishes a connection to the system bus. -func newConnection() (*Conn, error) { - c := new(Conn) - if err := c.initConnection(); err != nil { - return nil, err - } - - return c, nil -} - -// Innitialize D-Bus connection. -func (c *Conn) initConnection() error { - var err error - - c.sysconn, err = dbus.SystemBus() - if err != nil { - return err - } - - // This never fails, even if the service is not running atm. - c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath)) - - rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'", - dbusPath, dbusInterface, dbusInterface) - c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) - - rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", - dbusInterface) - c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) - - c.signal = make(chan *dbus.Signal, 10) - c.sysconn.Signal(c.signal) - - return nil -} - -func signalHandler() { - for signal := range connection.signal { - if strings.Contains(signal.Name, "NameOwnerChanged") { - firewalldRunning = checkRunning() - dbusConnectionChanged(signal.Body) - } else if strings.Contains(signal.Name, "Reloaded") { - reloaded() - } - } -} - -func dbusConnectionChanged(args []interface{}) { - name := args[0].(string) - old_owner := args[1].(string) - new_owner := args[2].(string) - - if name != dbusInterface { - return - } - - if len(new_owner) > 0 { - connectionEstablished() - } else if len(old_owner) > 0 { - connectionLost() - } -} - -func connectionEstablished() { - reloaded() -} - -func connectionLost() { - // Doesn't do anything for now. Libvirt also doesn't react to this. -} - -// call all callbacks -func reloaded() { - for _, pf := range onReloaded { - (*pf)() - } -} - -// add callback -func OnReloaded(callback func()) { - for _, pf := range onReloaded { - if pf == &callback { - return - } - } - onReloaded = append(onReloaded, &callback) -} - -// Call some remote method to see whether the service is actually running. -func checkRunning() bool { - var zone string - var err error - - if connection != nil { - err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone) - logrus.Infof("Firewalld running: %t", err == nil) - return err == nil - } - return false -} - -// Firewalld's passthrough method simply passes args through to iptables/ip6tables -func Passthrough(ipv IPV, args ...string) ([]byte, error) { - var output string - logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args) - if err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil { - return nil, err - } - return []byte(output), nil -} diff --git a/pkg/iptables/firewalld_test.go b/pkg/iptables/firewalld_test.go deleted file mode 100644 index 547ba7e683..0000000000 --- a/pkg/iptables/firewalld_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package iptables - -import ( - "net" - "strconv" - "testing" -) - -func TestFirewalldInit(t *testing.T) { - if !checkRunning() { - t.Skip("firewalld is not running") - } - if err := FirewalldInit(); err != nil { - t.Fatal(err) - } -} - -func TestReloaded(t *testing.T) { - var err error - var fwdChain *Chain - - fwdChain, err = NewChain("FWD", "lo", Filter, false) - if err != nil { - t.Fatal(err) - } - defer fwdChain.Remove() - - // copy-pasted from iptables_test:TestLink - ip1 := net.ParseIP("192.168.1.1") - ip2 := net.ParseIP("192.168.1.2") - port := 1234 - proto := "tcp" - - err = fwdChain.Link(Append, ip1, ip2, port, proto) - if err != nil { - t.Fatal(err) - } else { - // to be re-called again later - OnReloaded(func() { fwdChain.Link(Append, ip1, ip2, port, proto) }) - } - - rule1 := []string{ - "-i", fwdChain.Bridge, - "-o", fwdChain.Bridge, - "-p", proto, - "-s", ip1.String(), - "-d", ip2.String(), - "--dport", strconv.Itoa(port), - "-j", "ACCEPT"} - - if !Exists(fwdChain.Table, fwdChain.Name, rule1...) { - t.Fatalf("rule1 does not exist") - } - - // flush all rules - fwdChain.Remove() - - reloaded() - - // make sure the rules have been recreated - if !Exists(fwdChain.Table, fwdChain.Name, rule1...) { - t.Fatalf("rule1 hasn't been recreated") - } -} - -func TestPassthrough(t *testing.T) { - rule1 := []string{ - "-i", "lo", - "-p", "udp", - "--dport", "123", - "-j", "ACCEPT"} - - if firewalldRunning { - _, err := Passthrough(Iptables, append([]string{"-A"}, rule1...)...) - if err != nil { - t.Fatal(err) - } - if !Exists(Filter, "INPUT", rule1...) { - t.Fatalf("rule1 does not exist") - } - } - -} diff --git a/pkg/iptables/iptables.go b/pkg/iptables/iptables.go deleted file mode 100644 index 9cf1bbfa5c..0000000000 --- a/pkg/iptables/iptables.go +++ /dev/null @@ -1,305 +0,0 @@ -package iptables - -import ( - "errors" - "fmt" - "net" - "os/exec" - "strconv" - "strings" - "sync" - - "github.com/Sirupsen/logrus" -) - -type Action string -type Table string - -const ( - Append Action = "-A" - Delete Action = "-D" - Insert Action = "-I" - Nat Table = "nat" - Filter Table = "filter" - Mangle Table = "mangle" -) - -var ( - iptablesPath string - supportsXlock = false - // used to lock iptables commands if xtables lock is not supported - bestEffortLock sync.Mutex - ErrIptablesNotFound = errors.New("Iptables not found") -) - -type Chain struct { - Name string - Bridge string - Table Table -} - -type ChainError struct { - Chain string - Output []byte -} - -func (e ChainError) Error() string { - return fmt.Sprintf("Error iptables %s: %s", e.Chain, string(e.Output)) -} - -func initCheck() error { - - if iptablesPath == "" { - path, err := exec.LookPath("iptables") - if err != nil { - return ErrIptablesNotFound - } - iptablesPath = path - supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil - } - return nil -} - -func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error) { - c := &Chain{ - Name: name, - Bridge: bridge, - Table: table, - } - - if string(c.Table) == "" { - c.Table = Filter - } - - // Add chain if it doesn't exist - if _, err := Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil { - if output, err := Raw("-t", string(c.Table), "-N", c.Name); err != nil { - return nil, err - } else if len(output) != 0 { - return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output) - } - } - - switch table { - case Nat: - preroute := []string{ - "-m", "addrtype", - "--dst-type", "LOCAL"} - if !Exists(Nat, "PREROUTING", preroute...) { - if err := c.Prerouting(Append, preroute...); err != nil { - return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err) - } - } - output := []string{ - "-m", "addrtype", - "--dst-type", "LOCAL"} - if !hairpinMode { - output = append(output, "!", "--dst", "127.0.0.0/8") - } - if !Exists(Nat, "OUTPUT", output...) { - if err := c.Output(Append, output...); err != nil { - return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err) - } - } - case Filter: - link := []string{ - "-o", c.Bridge, - "-j", c.Name} - if !Exists(Filter, "FORWARD", link...) { - insert := append([]string{string(Insert), "FORWARD"}, link...) - if output, err := Raw(insert...); err != nil { - return nil, err - } else if len(output) != 0 { - return nil, fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output) - } - } - } - return c, nil -} - -func RemoveExistingChain(name string, table Table) error { - c := &Chain{ - Name: name, - Table: table, - } - if string(c.Table) == "" { - c.Table = Filter - } - return c.Remove() -} - -// Add forwarding rule to 'filter' table and corresponding nat rule to 'nat' table -func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error { - daddr := ip.String() - if ip.IsUnspecified() { - // iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we - // want "0.0.0.0/0". "0/0" is correctly interpreted as "any - // value" by both iptables and ip6tables. - daddr = "0/0" - } - if output, err := Raw("-t", string(Nat), string(action), c.Name, - "-p", proto, - "-d", daddr, - "--dport", strconv.Itoa(port), - "-j", "DNAT", - "--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "FORWARD", Output: output} - } - - if output, err := Raw("-t", string(Filter), string(action), c.Name, - "!", "-i", c.Bridge, - "-o", c.Bridge, - "-p", proto, - "-d", destAddr, - "--dport", strconv.Itoa(destPort), - "-j", "ACCEPT"); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "FORWARD", Output: output} - } - - if output, err := Raw("-t", string(Nat), string(action), "POSTROUTING", - "-p", proto, - "-s", destAddr, - "-d", destAddr, - "--dport", strconv.Itoa(destPort), - "-j", "MASQUERADE"); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "FORWARD", Output: output} - } - - return nil -} - -// Add reciprocal ACCEPT rule for two supplied IP addresses. -// Traffic is allowed from ip1 to ip2 and vice-versa -func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error { - if output, err := Raw("-t", string(Filter), string(action), c.Name, - "-i", c.Bridge, "-o", c.Bridge, - "-p", proto, - "-s", ip1.String(), - "-d", ip2.String(), - "--dport", strconv.Itoa(port), - "-j", "ACCEPT"); err != nil { - return err - } else if len(output) != 0 { - return fmt.Errorf("Error iptables forward: %s", output) - } - if output, err := Raw("-t", string(Filter), string(action), c.Name, - "-i", c.Bridge, "-o", c.Bridge, - "-p", proto, - "-s", ip2.String(), - "-d", ip1.String(), - "--sport", strconv.Itoa(port), - "-j", "ACCEPT"); err != nil { - return err - } else if len(output) != 0 { - return fmt.Errorf("Error iptables forward: %s", output) - } - return nil -} - -// Add linking rule to nat/PREROUTING chain. -func (c *Chain) Prerouting(action Action, args ...string) error { - a := []string{"-t", string(Nat), string(action), "PREROUTING"} - if len(args) > 0 { - a = append(a, args...) - } - if output, err := Raw(append(a, "-j", c.Name)...); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "PREROUTING", Output: output} - } - return nil -} - -// Add linking rule to an OUTPUT chain -func (c *Chain) Output(action Action, args ...string) error { - a := []string{"-t", string(c.Table), string(action), "OUTPUT"} - if len(args) > 0 { - a = append(a, args...) - } - if output, err := Raw(append(a, "-j", c.Name)...); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "OUTPUT", Output: output} - } - return nil -} - -func (c *Chain) Remove() error { - // Ignore errors - This could mean the chains were never set up - if c.Table == Nat { - c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL") - c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8") - c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6 - - c.Prerouting(Delete) - c.Output(Delete) - } - Raw("-t", string(c.Table), "-F", c.Name) - Raw("-t", string(c.Table), "-X", c.Name) - return nil -} - -// Check if a rule exists -func Exists(table Table, chain string, rule ...string) bool { - if string(table) == "" { - table = Filter - } - - // iptables -C, --check option was added in v.1.4.11 - // http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt - - // try -C - // if exit status is 0 then return true, the rule exists - if _, err := Raw(append([]string{ - "-t", string(table), "-C", chain}, rule...)...); err == nil { - return true - } - - // parse "iptables -S" for the rule (this checks rules in a specific chain - // in a specific table) - ruleString := strings.Join(rule, " ") - existingRules, _ := exec.Command(iptablesPath, "-t", string(table), "-S", chain).Output() - - return strings.Contains(string(existingRules), ruleString) -} - -// Call 'iptables' system command, passing supplied arguments -func Raw(args ...string) ([]byte, error) { - if firewalldRunning { - output, err := Passthrough(Iptables, args...) - if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") { - return output, err - } - - } - - if err := initCheck(); err != nil { - return nil, err - } - if supportsXlock { - args = append([]string{"--wait"}, args...) - } else { - bestEffortLock.Lock() - defer bestEffortLock.Unlock() - } - - logrus.Debugf("%s, %v", iptablesPath, args) - - output, err := exec.Command(iptablesPath, args...).CombinedOutput() - if err != nil { - return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err) - } - - // ignore iptables' message about xtables lock - if strings.Contains(string(output), "waiting for it to exit") { - output = []byte("") - } - - return output, err -} diff --git a/pkg/iptables/iptables_test.go b/pkg/iptables/iptables_test.go deleted file mode 100644 index cd61739121..0000000000 --- a/pkg/iptables/iptables_test.go +++ /dev/null @@ -1,237 +0,0 @@ -package iptables - -import ( - "net" - "os/exec" - "strconv" - "strings" - "sync" - "testing" -) - -const chainName = "DOCKERTEST" - -var natChain *Chain -var filterChain *Chain - -func TestNewChain(t *testing.T) { - var err error - - natChain, err = NewChain(chainName, "lo", Nat, false) - if err != nil { - t.Fatal(err) - } - - filterChain, err = NewChain(chainName, "lo", Filter, false) - if err != nil { - t.Fatal(err) - } -} - -func TestForward(t *testing.T) { - ip := net.ParseIP("192.168.1.1") - port := 1234 - dstAddr := "172.17.0.1" - dstPort := 4321 - proto := "tcp" - - err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort) - if err != nil { - t.Fatal(err) - } - - dnatRule := []string{ - "-d", ip.String(), - "-p", proto, - "--dport", strconv.Itoa(port), - "-j", "DNAT", - "--to-destination", dstAddr + ":" + strconv.Itoa(dstPort), - } - - if !Exists(natChain.Table, natChain.Name, dnatRule...) { - t.Fatalf("DNAT rule does not exist") - } - - filterRule := []string{ - "!", "-i", filterChain.Bridge, - "-o", filterChain.Bridge, - "-d", dstAddr, - "-p", proto, - "--dport", strconv.Itoa(dstPort), - "-j", "ACCEPT", - } - - if !Exists(filterChain.Table, filterChain.Name, filterRule...) { - t.Fatalf("filter rule does not exist") - } - - masqRule := []string{ - "-d", dstAddr, - "-s", dstAddr, - "-p", proto, - "--dport", strconv.Itoa(dstPort), - "-j", "MASQUERADE", - } - - if !Exists(natChain.Table, "POSTROUTING", masqRule...) { - t.Fatalf("MASQUERADE rule does not exist") - } -} - -func TestLink(t *testing.T) { - var err error - - ip1 := net.ParseIP("192.168.1.1") - ip2 := net.ParseIP("192.168.1.2") - port := 1234 - proto := "tcp" - - err = filterChain.Link(Append, ip1, ip2, port, proto) - if err != nil { - t.Fatal(err) - } - - rule1 := []string{ - "-i", filterChain.Bridge, - "-o", filterChain.Bridge, - "-p", proto, - "-s", ip1.String(), - "-d", ip2.String(), - "--dport", strconv.Itoa(port), - "-j", "ACCEPT"} - - if !Exists(filterChain.Table, filterChain.Name, rule1...) { - t.Fatalf("rule1 does not exist") - } - - rule2 := []string{ - "-i", filterChain.Bridge, - "-o", filterChain.Bridge, - "-p", proto, - "-s", ip2.String(), - "-d", ip1.String(), - "--sport", strconv.Itoa(port), - "-j", "ACCEPT"} - - if !Exists(filterChain.Table, filterChain.Name, rule2...) { - t.Fatalf("rule2 does not exist") - } -} - -func TestPrerouting(t *testing.T) { - args := []string{ - "-i", "lo", - "-d", "192.168.1.1"} - - err := natChain.Prerouting(Insert, args...) - if err != nil { - t.Fatal(err) - } - - rule := []string{ - "-j", natChain.Name} - - rule = append(rule, args...) - - if !Exists(natChain.Table, "PREROUTING", rule...) { - t.Fatalf("rule does not exist") - } - - delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, rule...) - if _, err = Raw(delRule...); err != nil { - t.Fatal(err) - } -} - -func TestOutput(t *testing.T) { - args := []string{ - "-o", "lo", - "-d", "192.168.1.1"} - - err := natChain.Output(Insert, args...) - if err != nil { - t.Fatal(err) - } - - rule := []string{ - "-j", natChain.Name} - - rule = append(rule, args...) - - if !Exists(natChain.Table, "OUTPUT", rule...) { - t.Fatalf("rule does not exist") - } - - delRule := append([]string{"-D", "OUTPUT", "-t", - string(natChain.Table)}, rule...) - if _, err = Raw(delRule...); err != nil { - t.Fatal(err) - } -} - -func TestConcurrencyWithWait(t *testing.T) { - RunConcurrencyTest(t, true) -} - -func TestConcurrencyNoWait(t *testing.T) { - RunConcurrencyTest(t, false) -} - -// Runs 10 concurrent rule additions. This will fail if iptables -// is actually invoked simultaneously without --wait. -// Note that if iptables does not support the xtable lock on this -// system, then allowXlock has no effect -- it will always be off. -func RunConcurrencyTest(t *testing.T, allowXlock bool) { - var wg sync.WaitGroup - - if !allowXlock && supportsXlock { - supportsXlock = false - defer func() { supportsXlock = true }() - } - - ip := net.ParseIP("192.168.1.1") - port := 1234 - dstAddr := "172.17.0.1" - dstPort := 4321 - proto := "tcp" - - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() - err := natChain.Forward(Append, ip, port, proto, dstAddr, dstPort) - if err != nil { - t.Fatal(err) - } - }() - } - wg.Wait() -} - -func TestCleanup(t *testing.T) { - var err error - var rules []byte - - // Cleanup filter/FORWARD first otherwise output of iptables-save is dirty - link := []string{"-t", string(filterChain.Table), - string(Delete), "FORWARD", - "-o", filterChain.Bridge, - "-j", filterChain.Name} - if _, err = Raw(link...); err != nil { - t.Fatal(err) - } - filterChain.Remove() - - err = RemoveExistingChain(chainName, Nat) - if err != nil { - t.Fatal(err) - } - - rules, err = exec.Command("iptables-save").Output() - if err != nil { - t.Fatal(err) - } - if strings.Contains(string(rules), chainName) { - t.Fatalf("Removing chain failed. %s found in iptables-save", chainName) - } -} diff --git a/pkg/resolvconf/README.md b/pkg/resolvconf/README.md deleted file mode 100644 index cdda554ba5..0000000000 --- a/pkg/resolvconf/README.md +++ /dev/null @@ -1 +0,0 @@ -Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf diff --git a/pkg/resolvconf/dns/resolvconf.go b/pkg/resolvconf/dns/resolvconf.go deleted file mode 100644 index c2f23ef34a..0000000000 --- a/pkg/resolvconf/dns/resolvconf.go +++ /dev/null @@ -1,16 +0,0 @@ -package dns - -import ( - "regexp" -) - -const IpLocalhost = `((127\.([0-9]{1,3}.){2}[0-9]{1,3})|(::1))` - -var localhostIPRegexp = regexp.MustCompile(IpLocalhost) - -// IsLocalhost returns true if ip matches the localhost IP regular expression. -// Used for determining if nameserver settings are being passed which are -// localhost addresses -func IsLocalhost(ip string) bool { - return localhostIPRegexp.MatchString(ip) -} diff --git a/pkg/resolvconf/resolvconf.go b/pkg/resolvconf/resolvconf.go deleted file mode 100644 index 907934c5c5..0000000000 --- a/pkg/resolvconf/resolvconf.go +++ /dev/null @@ -1,187 +0,0 @@ -// Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf -package resolvconf - -import ( - "bytes" - "io/ioutil" - "regexp" - "strings" - "sync" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/resolvconf/dns" -) - -var ( - // Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS - defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"} - defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"} - ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` - ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock - // This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also - // will *not match* IPv4-Embedded IPv6 Addresses (RFC6052), but that and other variants - // -- e.g. other link-local types -- either won't work in containers or are unnecessary. - // For readability and sufficiency for Docker purposes this seemed more reasonable than a - // 1000+ character regexp with exact and complete IPv6 validation - ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})` - - localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IpLocalhost + `\s*\n*`) - nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) - nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) - searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) -) - -var lastModified struct { - sync.Mutex - sha256 string - contents []byte -} - -// Get returns the contents of /etc/resolv.conf -func Get() ([]byte, error) { - resolv, err := ioutil.ReadFile("/etc/resolv.conf") - if err != nil { - return nil, err - } - return resolv, nil -} - -// GetIfChanged retrieves the host /etc/resolv.conf file, checks against the last hash -// and, if modified since last check, returns the bytes and new hash. -// This feature is used by the resolv.conf updater for containers -func GetIfChanged() ([]byte, string, error) { - lastModified.Lock() - defer lastModified.Unlock() - - resolv, err := ioutil.ReadFile("/etc/resolv.conf") - if err != nil { - return nil, "", err - } - newHash, err := ioutils.HashData(bytes.NewReader(resolv)) - if err != nil { - return nil, "", err - } - if lastModified.sha256 != newHash { - lastModified.sha256 = newHash - lastModified.contents = resolv - return resolv, newHash, nil - } - // nothing changed, so return no data - return nil, "", nil -} - -// GetLastModified retrieves the last used contents and hash of the host resolv.conf. -// Used by containers updating on restart -func GetLastModified() ([]byte, string) { - lastModified.Lock() - defer lastModified.Unlock() - - return lastModified.contents, lastModified.sha256 -} - -// FilterResolvDns cleans up the config in resolvConf. It has two main jobs: -// 1. It looks for localhost (127.*|::1) entries in the provided -// resolv.conf, removing local nameserver entries, and, if the resulting -// cleaned config has no defined nameservers left, adds default DNS entries -// 2. Given the caller provides the enable/disable state of IPv6, the filter -// code will remove all IPv6 nameservers if it is not enabled for containers -// -// It returns a boolean to notify the caller if changes were made at all -func FilterResolvDns(resolvConf []byte, ipv6Enabled bool) ([]byte, bool) { - changed := false - cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{}) - // if IPv6 is not enabled, also clean out any IPv6 address nameserver - if !ipv6Enabled { - cleanedResolvConf = nsIPv6Regexp.ReplaceAll(cleanedResolvConf, []byte{}) - } - // if the resulting resolvConf has no more nameservers defined, add appropriate - // default DNS servers for IPv4 and (optionally) IPv6 - if len(GetNameservers(cleanedResolvConf)) == 0 { - logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers : %v", defaultIPv4Dns) - dns := defaultIPv4Dns - if ipv6Enabled { - logrus.Infof("IPv6 enabled; Adding default IPv6 external servers : %v", defaultIPv6Dns) - dns = append(dns, defaultIPv6Dns...) - } - cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...) - } - if !bytes.Equal(resolvConf, cleanedResolvConf) { - changed = true - } - return cleanedResolvConf, changed -} - -// getLines parses input into lines and strips away comments. -func getLines(input []byte, commentMarker []byte) [][]byte { - lines := bytes.Split(input, []byte("\n")) - var output [][]byte - for _, currentLine := range lines { - var commentIndex = bytes.Index(currentLine, commentMarker) - if commentIndex == -1 { - output = append(output, currentLine) - } else { - output = append(output, currentLine[:commentIndex]) - } - } - return output -} - -// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf -func GetNameservers(resolvConf []byte) []string { - nameservers := []string{} - for _, line := range getLines(resolvConf, []byte("#")) { - var ns = nsRegexp.FindSubmatch(line) - if len(ns) > 0 { - nameservers = append(nameservers, string(ns[1])) - } - } - return nameservers -} - -// GetNameserversAsCIDR returns nameservers (if any) listed in -// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") -// This function's output is intended for net.ParseCIDR -func GetNameserversAsCIDR(resolvConf []byte) []string { - nameservers := []string{} - for _, nameserver := range GetNameservers(resolvConf) { - nameservers = append(nameservers, nameserver+"/32") - } - return nameservers -} - -// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf -// If more than one search line is encountered, only the contents of the last -// one is returned. -func GetSearchDomains(resolvConf []byte) []string { - domains := []string{} - for _, line := range getLines(resolvConf, []byte("#")) { - match := searchRegexp.FindSubmatch(line) - if match == nil { - continue - } - domains = strings.Fields(string(match[1])) - } - return domains -} - -// Build writes a configuration file to path containing a "nameserver" entry -// for every element in dns, and a "search" entry for every element in -// dnsSearch. -func Build(path string, dns, dnsSearch []string) error { - content := bytes.NewBuffer(nil) - for _, dns := range dns { - if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil { - return err - } - } - if len(dnsSearch) > 0 { - if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." { - if _, err := content.WriteString("search " + searchString + "\n"); err != nil { - return err - } - } - } - - return ioutil.WriteFile(path, content.Bytes(), 0644) -} diff --git a/pkg/resolvconf/resolvconf_test.go b/pkg/resolvconf/resolvconf_test.go deleted file mode 100644 index b0647e7833..0000000000 --- a/pkg/resolvconf/resolvconf_test.go +++ /dev/null @@ -1,238 +0,0 @@ -package resolvconf - -import ( - "bytes" - "io/ioutil" - "os" - "testing" -) - -func TestGet(t *testing.T) { - resolvConfUtils, err := Get() - if err != nil { - t.Fatal(err) - } - resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf") - if err != nil { - t.Fatal(err) - } - if string(resolvConfUtils) != string(resolvConfSystem) { - t.Fatalf("/etc/resolv.conf and GetResolvConf have different content.") - } -} - -func TestGetNameservers(t *testing.T) { - for resolv, result := range map[string][]string{` -nameserver 1.2.3.4 -nameserver 40.3.200.10 -search example.com`: {"1.2.3.4", "40.3.200.10"}, - `search example.com`: {}, - `nameserver 1.2.3.4 -search example.com -nameserver 4.30.20.100`: {"1.2.3.4", "4.30.20.100"}, - ``: {}, - ` nameserver 1.2.3.4 `: {"1.2.3.4"}, - `search example.com -nameserver 1.2.3.4 -#nameserver 4.3.2.1`: {"1.2.3.4"}, - `search example.com -nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4"}, - } { - test := GetNameservers([]byte(resolv)) - if !strSlicesEqual(test, result) { - t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) - } - } -} - -func TestGetNameserversAsCIDR(t *testing.T) { - for resolv, result := range map[string][]string{` -nameserver 1.2.3.4 -nameserver 40.3.200.10 -search example.com`: {"1.2.3.4/32", "40.3.200.10/32"}, - `search example.com`: {}, - `nameserver 1.2.3.4 -search example.com -nameserver 4.30.20.100`: {"1.2.3.4/32", "4.30.20.100/32"}, - ``: {}, - ` nameserver 1.2.3.4 `: {"1.2.3.4/32"}, - `search example.com -nameserver 1.2.3.4 -#nameserver 4.3.2.1`: {"1.2.3.4/32"}, - `search example.com -nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4/32"}, - } { - test := GetNameserversAsCIDR([]byte(resolv)) - if !strSlicesEqual(test, result) { - t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) - } - } -} - -func TestGetSearchDomains(t *testing.T) { - for resolv, result := range map[string][]string{ - `search example.com`: {"example.com"}, - `search example.com # ignored`: {"example.com"}, - ` search example.com `: {"example.com"}, - ` search example.com # ignored`: {"example.com"}, - `search foo.example.com example.com`: {"foo.example.com", "example.com"}, - ` search foo.example.com example.com `: {"foo.example.com", "example.com"}, - ` search foo.example.com example.com # ignored`: {"foo.example.com", "example.com"}, - ``: {}, - `# ignored`: {}, - `nameserver 1.2.3.4 -search foo.example.com example.com`: {"foo.example.com", "example.com"}, - `nameserver 1.2.3.4 -search dup1.example.com dup2.example.com -search foo.example.com example.com`: {"foo.example.com", "example.com"}, - `nameserver 1.2.3.4 -search foo.example.com example.com -nameserver 4.30.20.100`: {"foo.example.com", "example.com"}, - } { - test := GetSearchDomains([]byte(resolv)) - if !strSlicesEqual(test, result) { - t.Fatalf("Wrong search domain string {%s} should be %v. Input: %s", test, result, resolv) - } - } -} - -func strSlicesEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - - for i, v := range a { - if v != b[i] { - return false - } - } - - return true -} - -func TestBuild(t *testing.T) { - file, err := ioutil.TempFile("", "") - if err != nil { - t.Fatal(err) - } - defer os.Remove(file.Name()) - - err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}) - if err != nil { - t.Fatal(err) - } - - content, err := ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatal(err) - } - - if expected := "nameserver ns1\nnameserver ns2\nnameserver ns3\nsearch search1\n"; !bytes.Contains(content, []byte(expected)) { - t.Fatalf("Expected to find '%s' got '%s'", expected, content) - } -} - -func TestBuildWithZeroLengthDomainSearch(t *testing.T) { - file, err := ioutil.TempFile("", "") - if err != nil { - t.Fatal(err) - } - defer os.Remove(file.Name()) - - err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}) - if err != nil { - t.Fatal(err) - } - - content, err := ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatal(err) - } - - if expected := "nameserver ns1\nnameserver ns2\nnameserver ns3\n"; !bytes.Contains(content, []byte(expected)) { - t.Fatalf("Expected to find '%s' got '%s'", expected, content) - } - if notExpected := "search ."; bytes.Contains(content, []byte(notExpected)) { - t.Fatalf("Expected to not find '%s' got '%s'", notExpected, content) - } -} - -func TestFilterResolvDns(t *testing.T) { - ns0 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\n" - - if result, _ := FilterResolvDns([]byte(ns0), false); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed No Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } - - ns1 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } - - ns1 = "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } - - ns1 = "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } - - ns1 = "nameserver ::1\nnameserver 10.16.60.14\nnameserver 127.0.2.1\nnameserver 10.16.60.21\n" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } - - ns1 = "nameserver 10.16.60.14\nnameserver ::1\nnameserver 10.16.60.21\nnameserver ::1" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } - - // with IPv6 disabled (false param), the IPv6 nameserver should be removed - ns1 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed Localhost+IPv6 off: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } - - // with IPv6 enabled, the IPv6 nameserver should be preserved - ns0 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\n" - ns1 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1" - if result, _ := FilterResolvDns([]byte(ns1), true); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed Localhost+IPv6 on: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } - - // with IPv6 enabled, and no non-localhost servers, Google defaults (both IPv4+IPv6) should be added - ns0 = "\nnameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844" - ns1 = "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1" - if result, _ := FilterResolvDns([]byte(ns1), true); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed no Localhost+IPv6 enabled: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } - - // with IPv6 disabled, and no non-localhost servers, Google defaults (only IPv4) should be added - ns0 = "\nnameserver 8.8.8.8\nnameserver 8.8.4.4" - ns1 = "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { - if ns0 != string(result) { - t.Fatalf("Failed no Localhost+IPv6 enabled: expected \n<%s> got \n<%s>", ns0, string(result)) - } - } -}