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