github.com/jaegerpicker/docker@v0.7.7-0.20150325003727-22dba32b4dab/daemon/networkdriver/bridge/driver.go (about)

     1  package bridge
     2  
     3  import (
     4  	"encoding/hex"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net"
     9  	"os"
    10  	"strings"
    11  	"sync"
    12  
    13  	log "github.com/Sirupsen/logrus"
    14  	"github.com/docker/docker/daemon/networkdriver"
    15  	"github.com/docker/docker/daemon/networkdriver/ipallocator"
    16  	"github.com/docker/docker/daemon/networkdriver/portmapper"
    17  	"github.com/docker/docker/engine"
    18  	"github.com/docker/docker/nat"
    19  	"github.com/docker/docker/pkg/iptables"
    20  	"github.com/docker/docker/pkg/parsers/kernel"
    21  	"github.com/docker/docker/pkg/resolvconf"
    22  	"github.com/docker/libcontainer/netlink"
    23  )
    24  
    25  const (
    26  	DefaultNetworkBridge     = "docker0"
    27  	MaxAllocatedPortAttempts = 10
    28  )
    29  
    30  // Network interface represents the networking stack of a container
    31  type networkInterface struct {
    32  	IP           net.IP
    33  	IPv6         net.IP
    34  	PortMappings []net.Addr // There are mappings to the host interfaces
    35  }
    36  
    37  type ifaces struct {
    38  	c map[string]*networkInterface
    39  	sync.Mutex
    40  }
    41  
    42  func (i *ifaces) Set(key string, n *networkInterface) {
    43  	i.Lock()
    44  	i.c[key] = n
    45  	i.Unlock()
    46  }
    47  
    48  func (i *ifaces) Get(key string) *networkInterface {
    49  	i.Lock()
    50  	res := i.c[key]
    51  	i.Unlock()
    52  	return res
    53  }
    54  
    55  var (
    56  	addrs = []string{
    57  		// Here we don't follow the convention of using the 1st IP of the range for the gateway.
    58  		// This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges.
    59  		// In theory this shouldn't matter - in practice there's bound to be a few scripts relying
    60  		// on the internal addressing or other stupid things like that.
    61  		// They shouldn't, but hey, let's not break them unless we really have to.
    62  		"172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23
    63  		"10.0.42.1/16",   // Don't even try using the entire /8, that's too intrusive
    64  		"10.1.42.1/16",
    65  		"10.42.42.1/16",
    66  		"172.16.42.1/24",
    67  		"172.16.43.1/24",
    68  		"172.16.44.1/24",
    69  		"10.0.42.1/24",
    70  		"10.0.43.1/24",
    71  		"192.168.42.1/24",
    72  		"192.168.43.1/24",
    73  		"192.168.44.1/24",
    74  	}
    75  
    76  	bridgeIface       string
    77  	bridgeIPv4Network *net.IPNet
    78  	bridgeIPv6Addr    net.IP
    79  	globalIPv6Network *net.IPNet
    80  
    81  	defaultBindingIP  = net.ParseIP("0.0.0.0")
    82  	currentInterfaces = ifaces{c: make(map[string]*networkInterface)}
    83  	ipAllocator       = ipallocator.New()
    84  )
    85  
    86  func InitDriver(job *engine.Job) engine.Status {
    87  	var (
    88  		networkv4      *net.IPNet
    89  		networkv6      *net.IPNet
    90  		addrv4         net.Addr
    91  		addrsv6        []net.Addr
    92  		enableIPTables = job.GetenvBool("EnableIptables")
    93  		enableIPv6     = job.GetenvBool("EnableIPv6")
    94  		icc            = job.GetenvBool("InterContainerCommunication")
    95  		ipMasq         = job.GetenvBool("EnableIpMasq")
    96  		ipForward      = job.GetenvBool("EnableIpForward")
    97  		bridgeIP       = job.Getenv("BridgeIP")
    98  		bridgeIPv6     = "fe80::1/64"
    99  		fixedCIDR      = job.Getenv("FixedCIDR")
   100  		fixedCIDRv6    = job.Getenv("FixedCIDRv6")
   101  	)
   102  
   103  	if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
   104  		defaultBindingIP = net.ParseIP(defaultIP)
   105  	}
   106  
   107  	bridgeIface = job.Getenv("BridgeIface")
   108  	usingDefaultBridge := false
   109  	if bridgeIface == "" {
   110  		usingDefaultBridge = true
   111  		bridgeIface = DefaultNetworkBridge
   112  	}
   113  
   114  	addrv4, addrsv6, err := networkdriver.GetIfaceAddr(bridgeIface)
   115  
   116  	if err != nil {
   117  		// No Bridge existent, create one
   118  		// If we're not using the default bridge, fail without trying to create it
   119  		if !usingDefaultBridge {
   120  			return job.Error(err)
   121  		}
   122  
   123  		// If the iface is not found, try to create it
   124  		if err := configureBridge(bridgeIP, bridgeIPv6, enableIPv6); err != nil {
   125  			return job.Error(err)
   126  		}
   127  
   128  		addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface)
   129  		if err != nil {
   130  			return job.Error(err)
   131  		}
   132  
   133  		if fixedCIDRv6 != "" {
   134  			// Setting route to global IPv6 subnet
   135  			log.Infof("Adding route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface)
   136  			if err := netlink.AddRoute(fixedCIDRv6, "", "", bridgeIface); err != nil {
   137  				log.Fatalf("Could not add route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface)
   138  			}
   139  		}
   140  	} else {
   141  		// Bridge exists already, getting info...
   142  		// Validate that the bridge ip matches the ip specified by BridgeIP
   143  		if bridgeIP != "" {
   144  			networkv4 = addrv4.(*net.IPNet)
   145  			bip, _, err := net.ParseCIDR(bridgeIP)
   146  			if err != nil {
   147  				return job.Error(err)
   148  			}
   149  			if !networkv4.IP.Equal(bip) {
   150  				return job.Errorf("Bridge ip (%s) does not match existing bridge configuration %s", networkv4.IP, bip)
   151  			}
   152  		}
   153  
   154  		// A bridge might exist but not have any IPv6 addr associated with it yet
   155  		// (for example, an existing Docker installation that has only been used
   156  		// with IPv4 and docker0 already is set up) In that case, we can perform
   157  		// the bridge init for IPv6 here, else we will error out below if --ipv6=true
   158  		if len(addrsv6) == 0 && enableIPv6 {
   159  			if err := setupIPv6Bridge(bridgeIPv6); err != nil {
   160  				return job.Error(err)
   161  			}
   162  			// Recheck addresses now that IPv6 is setup on the bridge
   163  			addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface)
   164  			if err != nil {
   165  				return job.Error(err)
   166  			}
   167  		}
   168  
   169  		// TODO: Check if route to fixedCIDRv6 is set
   170  	}
   171  
   172  	if enableIPv6 {
   173  		bip6, _, err := net.ParseCIDR(bridgeIPv6)
   174  		if err != nil {
   175  			return job.Error(err)
   176  		}
   177  		found := false
   178  		for _, addrv6 := range addrsv6 {
   179  			networkv6 = addrv6.(*net.IPNet)
   180  			if networkv6.IP.Equal(bip6) {
   181  				found = true
   182  				break
   183  			}
   184  		}
   185  		if !found {
   186  			return job.Errorf("Bridge IPv6 does not match existing bridge configuration %s", bip6)
   187  		}
   188  	}
   189  
   190  	networkv4 = addrv4.(*net.IPNet)
   191  
   192  	if enableIPv6 {
   193  		if len(addrsv6) == 0 {
   194  			return job.Error(errors.New("IPv6 enabled but no IPv6 detected"))
   195  		}
   196  		bridgeIPv6Addr = networkv6.IP
   197  	}
   198  
   199  	// Configure iptables for link support
   200  	if enableIPTables {
   201  		if err := setupIPTables(addrv4, icc, ipMasq); err != nil {
   202  			return job.Error(err)
   203  		}
   204  
   205  	}
   206  
   207  	if ipForward {
   208  		// Enable IPv4 forwarding
   209  		if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil {
   210  			job.Logf("WARNING: unable to enable IPv4 forwarding: %s\n", err)
   211  		}
   212  
   213  		if fixedCIDRv6 != "" {
   214  			// Enable IPv6 forwarding
   215  			if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, 0644); err != nil {
   216  				job.Logf("WARNING: unable to enable IPv6 default forwarding: %s\n", err)
   217  			}
   218  			if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, 0644); err != nil {
   219  				job.Logf("WARNING: unable to enable IPv6 all forwarding: %s\n", err)
   220  			}
   221  		}
   222  	}
   223  
   224  	// We can always try removing the iptables
   225  	if err := iptables.RemoveExistingChain("DOCKER", iptables.Nat); err != nil {
   226  		return job.Error(err)
   227  	}
   228  
   229  	if enableIPTables {
   230  		_, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Nat)
   231  		if err != nil {
   232  			return job.Error(err)
   233  		}
   234  		chain, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter)
   235  		if err != nil {
   236  			return job.Error(err)
   237  		}
   238  		portmapper.SetIptablesChain(chain)
   239  	}
   240  
   241  	bridgeIPv4Network = networkv4
   242  	if fixedCIDR != "" {
   243  		_, subnet, err := net.ParseCIDR(fixedCIDR)
   244  		if err != nil {
   245  			return job.Error(err)
   246  		}
   247  		log.Debugf("Subnet: %v", subnet)
   248  		if err := ipAllocator.RegisterSubnet(bridgeIPv4Network, subnet); err != nil {
   249  			return job.Error(err)
   250  		}
   251  	}
   252  
   253  	if fixedCIDRv6 != "" {
   254  		_, subnet, err := net.ParseCIDR(fixedCIDRv6)
   255  		if err != nil {
   256  			return job.Error(err)
   257  		}
   258  		log.Debugf("Subnet: %v", subnet)
   259  		if err := ipAllocator.RegisterSubnet(subnet, subnet); err != nil {
   260  			return job.Error(err)
   261  		}
   262  		globalIPv6Network = subnet
   263  	}
   264  
   265  	// Block BridgeIP in IP allocator
   266  	ipAllocator.RequestIP(bridgeIPv4Network, bridgeIPv4Network.IP)
   267  
   268  	// https://github.com/docker/docker/issues/2768
   269  	job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeIPv4Network.IP)
   270  
   271  	for name, f := range map[string]engine.Handler{
   272  		"allocate_interface": Allocate,
   273  		"release_interface":  Release,
   274  		"allocate_port":      AllocatePort,
   275  		"link":               LinkContainers,
   276  	} {
   277  		if err := job.Eng.Register(name, f); err != nil {
   278  			return job.Error(err)
   279  		}
   280  	}
   281  	return engine.StatusOK
   282  }
   283  
   284  func setupIPTables(addr net.Addr, icc, ipmasq bool) error {
   285  	// Enable NAT
   286  
   287  	if ipmasq {
   288  		natArgs := []string{"-s", addr.String(), "!", "-o", bridgeIface, "-j", "MASQUERADE"}
   289  
   290  		if !iptables.Exists(iptables.Nat, "POSTROUTING", natArgs...) {
   291  			if output, err := iptables.Raw(append([]string{
   292  				"-t", string(iptables.Nat), "-I", "POSTROUTING"}, natArgs...)...); err != nil {
   293  				return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
   294  			} else if len(output) != 0 {
   295  				return &iptables.ChainError{Chain: "POSTROUTING", Output: output}
   296  			}
   297  		}
   298  	}
   299  
   300  	var (
   301  		args       = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"}
   302  		acceptArgs = append(args, "ACCEPT")
   303  		dropArgs   = append(args, "DROP")
   304  	)
   305  
   306  	if !icc {
   307  		iptables.Raw(append([]string{"-D", "FORWARD"}, acceptArgs...)...)
   308  
   309  		if !iptables.Exists(iptables.Filter, "FORWARD", dropArgs...) {
   310  			log.Debugf("Disable inter-container communication")
   311  			if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, dropArgs...)...); err != nil {
   312  				return fmt.Errorf("Unable to prevent intercontainer communication: %s", err)
   313  			} else if len(output) != 0 {
   314  				return fmt.Errorf("Error disabling intercontainer communication: %s", output)
   315  			}
   316  		}
   317  	} else {
   318  		iptables.Raw(append([]string{"-D", "FORWARD"}, dropArgs...)...)
   319  
   320  		if !iptables.Exists(iptables.Filter, "FORWARD", acceptArgs...) {
   321  			log.Debugf("Enable inter-container communication")
   322  			if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, acceptArgs...)...); err != nil {
   323  				return fmt.Errorf("Unable to allow intercontainer communication: %s", err)
   324  			} else if len(output) != 0 {
   325  				return fmt.Errorf("Error enabling intercontainer communication: %s", output)
   326  			}
   327  		}
   328  	}
   329  
   330  	// Accept all non-intercontainer outgoing packets
   331  	outgoingArgs := []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}
   332  	if !iptables.Exists(iptables.Filter, "FORWARD", outgoingArgs...) {
   333  		if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, outgoingArgs...)...); err != nil {
   334  			return fmt.Errorf("Unable to allow outgoing packets: %s", err)
   335  		} else if len(output) != 0 {
   336  			return &iptables.ChainError{Chain: "FORWARD outgoing", Output: output}
   337  		}
   338  	}
   339  
   340  	// Accept incoming packets for existing connections
   341  	existingArgs := []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}
   342  
   343  	if !iptables.Exists(iptables.Filter, "FORWARD", existingArgs...) {
   344  		if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, existingArgs...)...); err != nil {
   345  			return fmt.Errorf("Unable to allow incoming packets: %s", err)
   346  		} else if len(output) != 0 {
   347  			return &iptables.ChainError{Chain: "FORWARD incoming", Output: output}
   348  		}
   349  	}
   350  	return nil
   351  }
   352  
   353  // configureBridge attempts to create and configure a network bridge interface named `bridgeIface` on the host
   354  // If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges
   355  // If the bridge `bridgeIface` already exists, it will only perform the IP address association with the existing
   356  // bridge (fixes issue #8444)
   357  // If an address which doesn't conflict with existing interfaces can't be found, an error is returned.
   358  func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error {
   359  	nameservers := []string{}
   360  	resolvConf, _ := resolvconf.Get()
   361  	// We don't check for an error here, because we don't really care
   362  	// if we can't read /etc/resolv.conf. So instead we skip the append
   363  	// if resolvConf is nil. It either doesn't exist, or we can't read it
   364  	// for some reason.
   365  	if resolvConf != nil {
   366  		nameservers = append(nameservers, resolvconf.GetNameserversAsCIDR(resolvConf)...)
   367  	}
   368  
   369  	var ifaceAddr string
   370  	if len(bridgeIP) != 0 {
   371  		_, _, err := net.ParseCIDR(bridgeIP)
   372  		if err != nil {
   373  			return err
   374  		}
   375  		ifaceAddr = bridgeIP
   376  	} else {
   377  		for _, addr := range addrs {
   378  			_, dockerNetwork, err := net.ParseCIDR(addr)
   379  			if err != nil {
   380  				return err
   381  			}
   382  			if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil {
   383  				if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil {
   384  					ifaceAddr = addr
   385  					break
   386  				} else {
   387  					log.Debugf("%s %s", addr, err)
   388  				}
   389  			}
   390  		}
   391  	}
   392  
   393  	if ifaceAddr == "" {
   394  		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)
   395  	}
   396  	log.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr)
   397  
   398  	if err := createBridgeIface(bridgeIface); err != nil {
   399  		// The bridge may already exist, therefore we can ignore an "exists" error
   400  		if !os.IsExist(err) {
   401  			return err
   402  		}
   403  	}
   404  
   405  	iface, err := net.InterfaceByName(bridgeIface)
   406  	if err != nil {
   407  		return err
   408  	}
   409  
   410  	ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr)
   411  	if err != nil {
   412  		return err
   413  	}
   414  
   415  	if err := netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {
   416  		return fmt.Errorf("Unable to add private network: %s", err)
   417  	}
   418  
   419  	if enableIPv6 {
   420  		if err := setupIPv6Bridge(bridgeIPv6); err != nil {
   421  			return err
   422  		}
   423  	}
   424  
   425  	if err := netlink.NetworkLinkUp(iface); err != nil {
   426  		return fmt.Errorf("Unable to start network bridge: %s", err)
   427  	}
   428  	return nil
   429  }
   430  
   431  func setupIPv6Bridge(bridgeIPv6 string) error {
   432  
   433  	iface, err := net.InterfaceByName(bridgeIface)
   434  	if err != nil {
   435  		return err
   436  	}
   437  	// Enable IPv6 on the bridge
   438  	procFile := "/proc/sys/net/ipv6/conf/" + iface.Name + "/disable_ipv6"
   439  	if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
   440  		return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
   441  	}
   442  
   443  	ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6)
   444  	if err != nil {
   445  		return fmt.Errorf("Unable to parse bridge IPv6 address: %q, error: %v", bridgeIPv6, err)
   446  	}
   447  
   448  	if err := netlink.NetworkLinkAddIp(iface, ipAddr6, ipNet6); err != nil {
   449  		return fmt.Errorf("Unable to add private IPv6 network: %v", err)
   450  	}
   451  
   452  	return nil
   453  }
   454  
   455  func createBridgeIface(name string) error {
   456  	kv, err := kernel.GetKernelVersion()
   457  	// Only set the bridge's mac address if the kernel version is > 3.3
   458  	// before that it was not supported
   459  	setBridgeMacAddr := err == nil && (kv.Kernel >= 3 && kv.Major >= 3)
   460  	log.Debugf("setting bridge mac address = %v", setBridgeMacAddr)
   461  	return netlink.CreateBridge(name, setBridgeMacAddr)
   462  }
   463  
   464  // Generate a IEEE802 compliant MAC address from the given IP address.
   465  //
   466  // The generator is guaranteed to be consistent: the same IP will always yield the same
   467  // MAC address. This is to avoid ARP cache issues.
   468  func generateMacAddr(ip net.IP) net.HardwareAddr {
   469  	hw := make(net.HardwareAddr, 6)
   470  
   471  	// The first byte of the MAC address has to comply with these rules:
   472  	// 1. Unicast: Set the least-significant bit to 0.
   473  	// 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1.
   474  	// 3. As "small" as possible: The veth address has to be "smaller" than the bridge address.
   475  	hw[0] = 0x02
   476  
   477  	// The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI).
   478  	// Since this address is locally administered, we can do whatever we want as long as
   479  	// it doesn't conflict with other addresses.
   480  	hw[1] = 0x42
   481  
   482  	// Insert the IP address into the last 32 bits of the MAC address.
   483  	// This is a simple way to guarantee the address will be consistent and unique.
   484  	copy(hw[2:], ip.To4())
   485  
   486  	return hw
   487  }
   488  
   489  func linkLocalIPv6FromMac(mac string) (string, error) {
   490  	hx := strings.Replace(mac, ":", "", -1)
   491  	hw, err := hex.DecodeString(hx)
   492  	if err != nil {
   493  		return "", errors.New("Could not parse MAC address " + mac)
   494  	}
   495  
   496  	hw[0] ^= 0x2
   497  
   498  	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
   499  }
   500  
   501  // Allocate a network interface
   502  func Allocate(job *engine.Job) engine.Status {
   503  	var (
   504  		ip            net.IP
   505  		mac           net.HardwareAddr
   506  		err           error
   507  		id            = job.Args[0]
   508  		requestedIP   = net.ParseIP(job.Getenv("RequestedIP"))
   509  		requestedIPv6 = net.ParseIP(job.Getenv("RequestedIPv6"))
   510  		globalIPv6    net.IP
   511  	)
   512  
   513  	ip, err = ipAllocator.RequestIP(bridgeIPv4Network, requestedIP)
   514  	if err != nil {
   515  		return job.Error(err)
   516  	}
   517  
   518  	// If no explicit mac address was given, generate a random one.
   519  	if mac, err = net.ParseMAC(job.Getenv("RequestedMac")); err != nil {
   520  		mac = generateMacAddr(ip)
   521  	}
   522  
   523  	if globalIPv6Network != nil {
   524  		// If globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address
   525  		netmask_ones, _ := globalIPv6Network.Mask.Size()
   526  		if requestedIPv6 == nil && netmask_ones <= 80 {
   527  			requestedIPv6 = make(net.IP, len(globalIPv6Network.IP))
   528  			copy(requestedIPv6, globalIPv6Network.IP)
   529  			for i, h := range mac {
   530  				requestedIPv6[i+10] = h
   531  			}
   532  		}
   533  
   534  		globalIPv6, err = ipAllocator.RequestIP(globalIPv6Network, requestedIPv6)
   535  		if err != nil {
   536  			log.Errorf("Allocator: RequestIP v6: %v", err)
   537  			return job.Error(err)
   538  		}
   539  		log.Infof("Allocated IPv6 %s", globalIPv6)
   540  	}
   541  
   542  	out := engine.Env{}
   543  	out.Set("IP", ip.String())
   544  	out.Set("Mask", bridgeIPv4Network.Mask.String())
   545  	out.Set("Gateway", bridgeIPv4Network.IP.String())
   546  	out.Set("MacAddress", mac.String())
   547  	out.Set("Bridge", bridgeIface)
   548  
   549  	size, _ := bridgeIPv4Network.Mask.Size()
   550  	out.SetInt("IPPrefixLen", size)
   551  
   552  	// If linklocal IPv6
   553  	localIPv6Net, err := linkLocalIPv6FromMac(mac.String())
   554  	if err != nil {
   555  		return job.Error(err)
   556  	}
   557  	localIPv6, _, _ := net.ParseCIDR(localIPv6Net)
   558  	out.Set("LinkLocalIPv6", localIPv6.String())
   559  	out.Set("MacAddress", mac.String())
   560  
   561  	if globalIPv6Network != nil {
   562  		out.Set("GlobalIPv6", globalIPv6.String())
   563  		sizev6, _ := globalIPv6Network.Mask.Size()
   564  		out.SetInt("GlobalIPv6PrefixLen", sizev6)
   565  		out.Set("IPv6Gateway", bridgeIPv6Addr.String())
   566  	}
   567  
   568  	currentInterfaces.Set(id, &networkInterface{
   569  		IP:   ip,
   570  		IPv6: globalIPv6,
   571  	})
   572  
   573  	out.WriteTo(job.Stdout)
   574  
   575  	return engine.StatusOK
   576  }
   577  
   578  // Release an interface for a select ip
   579  func Release(job *engine.Job) engine.Status {
   580  	var (
   581  		id                 = job.Args[0]
   582  		containerInterface = currentInterfaces.Get(id)
   583  	)
   584  
   585  	if containerInterface == nil {
   586  		return job.Errorf("No network information to release for %s", id)
   587  	}
   588  
   589  	for _, nat := range containerInterface.PortMappings {
   590  		if err := portmapper.Unmap(nat); err != nil {
   591  			log.Infof("Unable to unmap port %s: %s", nat, err)
   592  		}
   593  	}
   594  
   595  	if err := ipAllocator.ReleaseIP(bridgeIPv4Network, containerInterface.IP); err != nil {
   596  		log.Infof("Unable to release IPv4 %s", err)
   597  	}
   598  	if globalIPv6Network != nil {
   599  		if err := ipAllocator.ReleaseIP(globalIPv6Network, containerInterface.IPv6); err != nil {
   600  			log.Infof("Unable to release IPv6 %s", err)
   601  		}
   602  	}
   603  	return engine.StatusOK
   604  }
   605  
   606  // Allocate an external port and map it to the interface
   607  func AllocatePort(job *engine.Job) engine.Status {
   608  	var (
   609  		err error
   610  
   611  		ip            = defaultBindingIP
   612  		id            = job.Args[0]
   613  		hostIP        = job.Getenv("HostIP")
   614  		hostPort      = job.GetenvInt("HostPort")
   615  		containerPort = job.GetenvInt("ContainerPort")
   616  		proto         = job.Getenv("Proto")
   617  		network       = currentInterfaces.Get(id)
   618  	)
   619  
   620  	if hostIP != "" {
   621  		ip = net.ParseIP(hostIP)
   622  		if ip == nil {
   623  			return job.Errorf("Bad parameter: invalid host ip %s", hostIP)
   624  		}
   625  	}
   626  
   627  	// host ip, proto, and host port
   628  	var container net.Addr
   629  	switch proto {
   630  	case "tcp":
   631  		container = &net.TCPAddr{IP: network.IP, Port: containerPort}
   632  	case "udp":
   633  		container = &net.UDPAddr{IP: network.IP, Port: containerPort}
   634  	default:
   635  		return job.Errorf("unsupported address type %s", proto)
   636  	}
   637  
   638  	//
   639  	// Try up to 10 times to get a port that's not already allocated.
   640  	//
   641  	// In the event of failure to bind, return the error that portmapper.Map
   642  	// yields.
   643  	//
   644  
   645  	var host net.Addr
   646  	for i := 0; i < MaxAllocatedPortAttempts; i++ {
   647  		if host, err = portmapper.Map(container, ip, hostPort); err == nil {
   648  			break
   649  		}
   650  		// There is no point in immediately retrying to map an explicitly
   651  		// chosen port.
   652  		if hostPort != 0 {
   653  			job.Logf("Failed to allocate and map port %d: %s", hostPort, err)
   654  			break
   655  		}
   656  		job.Logf("Failed to allocate and map port: %s, retry: %d", err, i+1)
   657  	}
   658  
   659  	if err != nil {
   660  		return job.Error(err)
   661  	}
   662  
   663  	network.PortMappings = append(network.PortMappings, host)
   664  
   665  	out := engine.Env{}
   666  	switch netAddr := host.(type) {
   667  	case *net.TCPAddr:
   668  		out.Set("HostIP", netAddr.IP.String())
   669  		out.SetInt("HostPort", netAddr.Port)
   670  	case *net.UDPAddr:
   671  		out.Set("HostIP", netAddr.IP.String())
   672  		out.SetInt("HostPort", netAddr.Port)
   673  	}
   674  	if _, err := out.WriteTo(job.Stdout); err != nil {
   675  		return job.Error(err)
   676  	}
   677  
   678  	return engine.StatusOK
   679  }
   680  
   681  func LinkContainers(job *engine.Job) engine.Status {
   682  	var (
   683  		action       = job.Args[0]
   684  		nfAction     iptables.Action
   685  		childIP      = job.Getenv("ChildIP")
   686  		parentIP     = job.Getenv("ParentIP")
   687  		ignoreErrors = job.GetenvBool("IgnoreErrors")
   688  		ports        = job.GetenvList("Ports")
   689  	)
   690  
   691  	switch action {
   692  	case "-A":
   693  		nfAction = iptables.Append
   694  	case "-I":
   695  		nfAction = iptables.Insert
   696  	case "-D":
   697  		nfAction = iptables.Delete
   698  	default:
   699  		return job.Errorf("Invalid action '%s' specified", action)
   700  	}
   701  
   702  	ip1 := net.ParseIP(parentIP)
   703  	if ip1 == nil {
   704  		return job.Errorf("Parent IP '%s' is invalid", parentIP)
   705  	}
   706  	ip2 := net.ParseIP(childIP)
   707  	if ip2 == nil {
   708  		return job.Errorf("Child IP '%s' is invalid", childIP)
   709  	}
   710  
   711  	chain := iptables.Chain{Name: "DOCKER", Bridge: bridgeIface}
   712  	for _, p := range ports {
   713  		port := nat.Port(p)
   714  		if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil {
   715  			return job.Error(err)
   716  		}
   717  	}
   718  	return engine.StatusOK
   719  }