Ver código fonte

Docker integration with libnetwork

    - Updated Dockerfile to satisfy libnetwork GOPATH requirements.
    - Reworked daemon to allocate network resources using libnetwork.
    - Reworked remove link code to also update network resources in libnetwork.
    - Adjusted the exec driver command population to reflect libnetwork design.
    - Adjusted the exec driver create command steps.
    - Updated a few test cases to reflect the change in design.
    - Removed the dns setup code from docker as resolv.conf is entirely managed
      in libnetwork.
    - Integrated with lxc exec driver.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
Jana Radhakrishnan 10 anos atrás
pai
commit
d18919e304

+ 1 - 0
Makefile

@@ -9,6 +9,7 @@ DOCKER_ENVS := \
 	-e DOCKER_EXECDRIVER \
 	-e DOCKER_GRAPHDRIVER \
 	-e DOCKER_STORAGE_OPTS \
+	-e DOCKER_USERLANDPROXY \
 	-e TESTDIRS \
 	-e TESTFLAGS \
 	-e TIMEOUT

+ 1 - 0
api/client/run.go

@@ -12,6 +12,7 @@ import (
 	"github.com/docker/docker/pkg/resolvconf/dns"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/runconfig"
+	"github.com/docker/libnetwork/resolvconf/dns"
 )
 
 func (cid *cidFile) Close() error {

+ 3 - 2
api/server/server.go

@@ -23,7 +23,6 @@ import (
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
-	"github.com/docker/docker/daemon/networkdriver/bridge"
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/jsonmessage"
@@ -36,6 +35,7 @@ import (
 	"github.com/docker/docker/pkg/version"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
+	"github.com/docker/libnetwork/portallocator"
 )
 
 type ServerConfig struct {
@@ -1548,8 +1548,9 @@ func allocateDaemonPort(addr string) error {
 		return fmt.Errorf("failed to lookup %s address in host specification", host)
 	}
 
+	pa := portallocator.Get()
 	for _, hostIP := range hostIPs {
-		if _, err := bridge.RequestPort(hostIP, "tcp", intPort); err != nil {
+		if _, err := pa.RequestPort(hostIP, "tcp", intPort); err != nil {
 			return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
 		}
 	}

+ 27 - 15
daemon/config.go

@@ -1,8 +1,8 @@
 package daemon
 
 import (
-	"github.com/docker/docker/daemon/networkdriver"
-	"github.com/docker/docker/daemon/networkdriver/bridge"
+	"net"
+
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/runconfig"
@@ -16,8 +16,9 @@ const (
 // CommonConfig defines the configuration of a docker daemon which are
 // common across platforms.
 type CommonConfig struct {
-	AutoRestart    bool
-	Bridge         bridge.Config
+	AutoRestart bool
+	// Bridge holds bridge network specific configuration.
+	Bridge         bridgeConfig
 	Context        map[string][]string
 	CorsHeaders    string
 	DisableNetwork bool
@@ -35,6 +36,24 @@ type CommonConfig struct {
 	TrustKeyPath   string
 }
 
+// bridgeConfig stores all the bridge driver specific
+// configuration.
+type bridgeConfig 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
+}
+
 // InstallCommonFlags adds command-line options to the top-level flag parser for
 // the current process.
 // Subsequent calls to `flag.Parse` will populate config with values parsed
@@ -45,9 +64,9 @@ func (config *Config) InstallCommonFlags() {
 	flag.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, "Root of the Docker runtime")
 	flag.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", "Root of the Docker execdriver")
 	flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run")
-	flag.BoolVar(&config.Bridge.EnableIptables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules")
-	flag.BoolVar(&config.Bridge.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
-	flag.BoolVar(&config.Bridge.EnableIpMasq, []string{"-ip-masq"}, true, "Enable IP masquerading")
+	flag.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules")
+	flag.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
+	flag.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, "Enable IP masquerading")
 	flag.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, "Enable IPv6 networking")
 	flag.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", "Specify network bridge IP")
 	flag.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", "Attach containers to a network bridge")
@@ -61,7 +80,7 @@ func (config *Config) InstallCommonFlags() {
 	flag.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, "Set the containers network MTU")
 	flag.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, "Enable CORS headers in the remote API, this is deprecated by --api-cors-header")
 	flag.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", "Set CORS headers in the remote API")
-	opts.IPVar(&config.Bridge.DefaultIp, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports")
+	opts.IPVar(&config.Bridge.DefaultIP, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports")
 	// FIXME: why the inconsistency between "hosts" and "sockets"?
 	opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use")
 	opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use")
@@ -71,10 +90,3 @@ func (config *Config) InstallCommonFlags() {
 	flag.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, "Use userland proxy for loopback traffic")
 
 }
-
-func getDefaultNetworkMtu() int {
-	if iface, err := networkdriver.GetDefaultRouteIface(); err == nil {
-		return iface.MTU
-	}
-	return defaultNetworkMtu
-}

+ 0 - 6
daemon/container.go

@@ -252,18 +252,12 @@ func (container *Container) Start() (err error) {
 		}
 	}()
 
-	if err := container.setupContainerDns(); err != nil {
-		return err
-	}
 	if err := container.Mount(); err != nil {
 		return err
 	}
 	if err := container.initializeNetworking(); err != nil {
 		return err
 	}
-	if err := container.updateParentsHosts(); err != nil {
-		return err
-	}
 	container.verifyDaemonSettings()
 	if err := container.prepareVolumes(); err != nil {
 		return err

+ 425 - 349
daemon/container_linux.go

@@ -3,11 +3,13 @@
 package daemon
 
 import (
-	"bytes"
 	"fmt"
 	"io/ioutil"
+	"net"
 	"os"
+	"path"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"syscall"
 	"time"
@@ -15,20 +17,21 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/daemon/execdriver"
 	"github.com/docker/docker/daemon/network"
-	"github.com/docker/docker/daemon/networkdriver/bridge"
 	"github.com/docker/docker/links"
 	"github.com/docker/docker/nat"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/directory"
-	"github.com/docker/docker/pkg/etchosts"
 	"github.com/docker/docker/pkg/ioutils"
-	"github.com/docker/docker/pkg/resolvconf"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/ulimit"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
 	"github.com/docker/libcontainer/configs"
 	"github.com/docker/libcontainer/devices"
+	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netlabel"
+	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/options"
 )
 
 const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
@@ -65,179 +68,6 @@ func killProcessDirectly(container *Container) error {
 	return nil
 }
 
-func (container *Container) setupContainerDns() error {
-	if container.ResolvConfPath != "" {
-		// check if this is an existing container that needs DNS update:
-		if container.UpdateDns {
-			// read the host's resolv.conf, get the hash and call updateResolvConf
-			logrus.Debugf("Check container (%s) for update to resolv.conf - UpdateDns flag was set", container.ID)
-			latestResolvConf, latestHash := resolvconf.GetLastModified()
-
-			// clean container resolv.conf re: localhost nameservers and IPv6 NS (if IPv6 disabled)
-			updatedResolvConf, modified := resolvconf.FilterResolvDns(latestResolvConf, container.daemon.config.Bridge.EnableIPv6)
-			if modified {
-				// changes have occurred during resolv.conf localhost cleanup: generate an updated hash
-				newHash, err := ioutils.HashData(bytes.NewReader(updatedResolvConf))
-				if err != nil {
-					return err
-				}
-				latestHash = newHash
-			}
-
-			if err := container.updateResolvConf(updatedResolvConf, latestHash); err != nil {
-				return err
-			}
-			// successful update of the restarting container; set the flag off
-			container.UpdateDns = false
-		}
-		return nil
-	}
-
-	var (
-		config = container.hostConfig
-		daemon = container.daemon
-	)
-
-	resolvConf, err := resolvconf.Get()
-	if err != nil {
-		return err
-	}
-	container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf")
-	if err != nil {
-		return err
-	}
-
-	if config.NetworkMode.IsBridge() || config.NetworkMode.IsNone() {
-		// check configurations for any container/daemon dns settings
-		if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 {
-			var (
-				dns       = resolvconf.GetNameservers(resolvConf)
-				dnsSearch = resolvconf.GetSearchDomains(resolvConf)
-			)
-			if len(config.Dns) > 0 {
-				dns = config.Dns
-			} else if len(daemon.config.Dns) > 0 {
-				dns = daemon.config.Dns
-			}
-			if len(config.DnsSearch) > 0 {
-				dnsSearch = config.DnsSearch
-			} else if len(daemon.config.DnsSearch) > 0 {
-				dnsSearch = daemon.config.DnsSearch
-			}
-			return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch)
-		}
-
-		// replace any localhost/127.*, and remove IPv6 nameservers if IPv6 disabled in daemon
-		resolvConf, _ = resolvconf.FilterResolvDns(resolvConf, daemon.config.Bridge.EnableIPv6)
-	}
-	//get a sha256 hash of the resolv conf at this point so we can check
-	//for changes when the host resolv.conf changes (e.g. network update)
-	resolvHash, err := ioutils.HashData(bytes.NewReader(resolvConf))
-	if err != nil {
-		return err
-	}
-	resolvHashFile := container.ResolvConfPath + ".hash"
-	if err = ioutil.WriteFile(resolvHashFile, []byte(resolvHash), 0644); err != nil {
-		return err
-	}
-	return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644)
-}
-
-// called when the host's resolv.conf changes to check whether container's resolv.conf
-// is unchanged by the container "user" since container start: if unchanged, the
-// container's resolv.conf will be updated to match the host's new resolv.conf
-func (container *Container) updateResolvConf(updatedResolvConf []byte, newResolvHash string) error {
-
-	if container.ResolvConfPath == "" {
-		return nil
-	}
-	if container.Running {
-		//set a marker in the hostConfig to update on next start/restart
-		container.UpdateDns = true
-		return nil
-	}
-
-	resolvHashFile := container.ResolvConfPath + ".hash"
-
-	//read the container's current resolv.conf and compute the hash
-	resolvBytes, err := ioutil.ReadFile(container.ResolvConfPath)
-	if err != nil {
-		return err
-	}
-	curHash, err := ioutils.HashData(bytes.NewReader(resolvBytes))
-	if err != nil {
-		return err
-	}
-
-	//read the hash from the last time we wrote resolv.conf in the container
-	hashBytes, err := ioutil.ReadFile(resolvHashFile)
-	if err != nil {
-		if !os.IsNotExist(err) {
-			return err
-		}
-		// backwards compat: if no hash file exists, this container pre-existed from
-		// a Docker daemon that didn't contain this update feature. Given we can't know
-		// if the user has modified the resolv.conf since container start time, safer
-		// to just never update the container's resolv.conf during it's lifetime which
-		// we can control by setting hashBytes to an empty string
-		hashBytes = []byte("")
-	}
-
-	//if the user has not modified the resolv.conf of the container since we wrote it last
-	//we will replace it with the updated resolv.conf from the host
-	if string(hashBytes) == curHash {
-		logrus.Debugf("replacing %q with updated host resolv.conf", container.ResolvConfPath)
-
-		// for atomic updates to these files, use temporary files with os.Rename:
-		dir := filepath.Dir(container.ResolvConfPath)
-		tmpHashFile, err := ioutil.TempFile(dir, "hash")
-		if err != nil {
-			return err
-		}
-		tmpResolvFile, err := ioutil.TempFile(dir, "resolv")
-		if err != nil {
-			return err
-		}
-
-		// write the updates to the temp files
-		if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newResolvHash), 0644); err != nil {
-			return err
-		}
-		if err = ioutil.WriteFile(tmpResolvFile.Name(), updatedResolvConf, 0644); err != nil {
-			return err
-		}
-
-		// rename the temp files for atomic replace
-		if err = os.Rename(tmpHashFile.Name(), resolvHashFile); err != nil {
-			return err
-		}
-		return os.Rename(tmpResolvFile.Name(), container.ResolvConfPath)
-	}
-	return nil
-}
-
-func (container *Container) updateParentsHosts() error {
-	refs := container.daemon.ContainerGraph().RefPaths(container.ID)
-	for _, ref := range refs {
-		if ref.ParentID == "0" {
-			continue
-		}
-
-		c, err := container.daemon.Get(ref.ParentID)
-		if err != nil {
-			logrus.Error(err)
-		}
-
-		if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() {
-			logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress)
-			if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, ref.Name); err != nil {
-				logrus.Errorf("Failed to update /etc/hosts in parent container %s for alias %s: %v", c.ID, ref.Name, err)
-			}
-		}
-	}
-	return nil
-}
-
 func (container *Container) setupLinkedContainers() ([]string, error) {
 	var (
 		env    []string
@@ -360,39 +190,16 @@ func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs.
 
 func populateCommand(c *Container, env []string) error {
 	en := &execdriver.Network{
-		Mtu:       c.daemon.config.Mtu,
-		Interface: nil,
+		NamespacePath: c.NetworkSettings.SandboxKey,
 	}
 
 	parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2)
-	switch parts[0] {
-	case "none":
-	case "host":
-		en.HostNetworking = true
-	case "bridge", "": // empty string to support existing containers
-		if !c.Config.NetworkDisabled {
-			network := c.NetworkSettings
-			en.Interface = &execdriver.NetworkInterface{
-				Gateway:              network.Gateway,
-				Bridge:               network.Bridge,
-				IPAddress:            network.IPAddress,
-				IPPrefixLen:          network.IPPrefixLen,
-				MacAddress:           network.MacAddress,
-				LinkLocalIPv6Address: network.LinkLocalIPv6Address,
-				GlobalIPv6Address:    network.GlobalIPv6Address,
-				GlobalIPv6PrefixLen:  network.GlobalIPv6PrefixLen,
-				IPv6Gateway:          network.IPv6Gateway,
-				HairpinMode:          network.HairpinMode,
-			}
-		}
-	case "container":
+	if parts[0] == "container" {
 		nc, err := c.getNetworkedContainer()
 		if err != nil {
 			return err
 		}
 		en.ContainerID = nc.ID
-	default:
-		return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode)
 	}
 
 	ipc := &execdriver.Ipc{}
@@ -537,40 +344,318 @@ func (container *Container) GetSize() (int64, int64) {
 	return sizeRw, sizeRootfs
 }
 
-func (container *Container) AllocateNetwork() error {
-	mode := container.hostConfig.NetworkMode
-	if container.Config.NetworkDisabled || !mode.IsPrivate() {
+func (container *Container) buildHostnameFile() error {
+	hostnamePath, err := container.GetRootResourcePath("hostname")
+	if err != nil {
+		return err
+	}
+	container.HostnamePath = hostnamePath
+
+	if container.Config.Domainname != "" {
+		return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644)
+	}
+	return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
+}
+
+func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, error) {
+	var (
+		joinOptions []libnetwork.EndpointOption
+		err         error
+		dns         []string
+		dnsSearch   []string
+	)
+
+	joinOptions = append(joinOptions, libnetwork.JoinOptionHostname(container.Config.Hostname),
+		libnetwork.JoinOptionDomainname(container.Config.Domainname))
+
+	if container.hostConfig.NetworkMode.IsHost() {
+		joinOptions = append(joinOptions, libnetwork.JoinOptionUseDefaultSandbox())
+	}
+
+	container.HostsPath, err = container.GetRootResourcePath("hosts")
+	if err != nil {
+		return nil, err
+	}
+	joinOptions = append(joinOptions, libnetwork.JoinOptionHostsPath(container.HostsPath))
+
+	container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf")
+	if err != nil {
+		return nil, err
+	}
+	joinOptions = append(joinOptions, libnetwork.JoinOptionResolvConfPath(container.ResolvConfPath))
+
+	if len(container.hostConfig.Dns) > 0 {
+		dns = container.hostConfig.Dns
+	} else if len(container.daemon.config.Dns) > 0 {
+		dns = container.daemon.config.Dns
+	}
+
+	for _, d := range dns {
+		joinOptions = append(joinOptions, libnetwork.JoinOptionDNS(d))
+	}
+
+	if len(container.hostConfig.DnsSearch) > 0 {
+		dnsSearch = container.hostConfig.DnsSearch
+	} else if len(container.daemon.config.DnsSearch) > 0 {
+		dnsSearch = container.daemon.config.DnsSearch
+	}
+
+	for _, ds := range dnsSearch {
+		joinOptions = append(joinOptions, libnetwork.JoinOptionDNSSearch(ds))
+	}
+
+	if container.NetworkSettings.SecondaryIPAddresses != nil {
+		name := container.Config.Hostname
+		if container.Config.Domainname != "" {
+			name = name + "." + container.Config.Domainname
+		}
+
+		for _, a := range container.NetworkSettings.SecondaryIPAddresses {
+			joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(name, a.Addr))
+		}
+	}
+
+	var childEndpoints, parentEndpoints []string
+
+	children, err := container.daemon.Children(container.Name)
+	if err != nil {
+		return nil, err
+	}
+
+	for linkAlias, child := range children {
+		_, alias := path.Split(linkAlias)
+		// allow access to the linked container via the alias, real name, and container hostname
+		aliasList := alias + " " + child.Config.Hostname
+		// only add the name if alias isn't equal to the name
+		if alias != child.Name[1:] {
+			aliasList = aliasList + " " + child.Name[1:]
+		}
+		joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(aliasList, child.NetworkSettings.IPAddress))
+		if child.NetworkSettings.EndpointID != "" {
+			childEndpoints = append(childEndpoints, child.NetworkSettings.EndpointID)
+		}
+	}
+
+	for _, extraHost := range container.hostConfig.ExtraHosts {
+		// allow IPv6 addresses in extra hosts; only split on first ":"
+		parts := strings.SplitN(extraHost, ":", 2)
+		joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(parts[0], parts[1]))
+	}
+
+	refs := container.daemon.ContainerGraph().RefPaths(container.ID)
+	for _, ref := range refs {
+		if ref.ParentID == "0" {
+			continue
+		}
+
+		c, err := container.daemon.Get(ref.ParentID)
+		if err != nil {
+			logrus.Error(err)
+		}
+
+		if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() {
+			logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress)
+			joinOptions = append(joinOptions, libnetwork.JoinOptionParentUpdate(c.NetworkSettings.EndpointID, ref.Name, container.NetworkSettings.IPAddress))
+			if c.NetworkSettings.EndpointID != "" {
+				parentEndpoints = append(parentEndpoints, c.NetworkSettings.EndpointID)
+			}
+		}
+	}
+
+	linkOptions := options.Generic{
+		netlabel.GenericData: options.Generic{
+			"ParentEndpoints": parentEndpoints,
+			"ChildEndpoints":  childEndpoints,
+		},
+	}
+
+	joinOptions = append(joinOptions, libnetwork.JoinOptionGeneric(linkOptions))
+
+	return joinOptions, nil
+}
+
+func (container *Container) buildPortMapInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) {
+	if ep == nil {
+		return nil, fmt.Errorf("invalid endpoint while building port map info")
+	}
+
+	if networkSettings == nil {
+		return nil, fmt.Errorf("invalid networksettings while building port map info")
+	}
+
+	driverInfo, err := ep.DriverInfo()
+	if err != nil {
+		return nil, err
+	}
+
+	if driverInfo == nil {
+		// It is not an error for epInfo to be nil
+		return networkSettings, nil
+	}
+
+	if mac, ok := driverInfo[netlabel.MacAddress]; ok {
+		networkSettings.MacAddress = mac.(net.HardwareAddr).String()
+	}
+
+	mapData, ok := driverInfo[netlabel.PortMap]
+	if !ok {
+		return networkSettings, nil
+	}
+
+	if portMapping, ok := mapData.([]netutils.PortBinding); ok {
+		networkSettings.Ports = nat.PortMap{}
+		for _, pp := range portMapping {
+			natPort := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port)))
+			natBndg := nat.PortBinding{HostIp: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))}
+			networkSettings.Ports[natPort] = append(networkSettings.Ports[natPort], natBndg)
+		}
+	}
+
+	return networkSettings, nil
+}
+
+func (container *Container) buildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) {
+	if ep == nil {
+		return nil, fmt.Errorf("invalid endpoint while building port map info")
+	}
+
+	if networkSettings == nil {
+		return nil, fmt.Errorf("invalid networksettings while building port map info")
+	}
+
+	epInfo := ep.Info()
+	if epInfo == nil {
+		// It is not an error to get an empty endpoint info
+		return networkSettings, nil
+	}
+
+	ifaceList := epInfo.InterfaceList()
+	if len(ifaceList) == 0 {
+		return networkSettings, nil
+	}
+
+	iface := ifaceList[0]
+
+	ones, _ := iface.Address().Mask.Size()
+	networkSettings.IPAddress = iface.Address().IP.String()
+	networkSettings.IPPrefixLen = ones
+
+	if iface.AddressIPv6().IP.To16() != nil {
+		onesv6, _ := iface.AddressIPv6().Mask.Size()
+		networkSettings.GlobalIPv6Address = iface.AddressIPv6().IP.String()
+		networkSettings.GlobalIPv6PrefixLen = onesv6
+	}
+
+	if len(ifaceList) == 1 {
+		return networkSettings, nil
+	}
+
+	networkSettings.SecondaryIPAddresses = make([]network.Address, 0, len(ifaceList)-1)
+	networkSettings.SecondaryIPv6Addresses = make([]network.Address, 0, len(ifaceList)-1)
+	for _, iface := range ifaceList[1:] {
+		ones, _ := iface.Address().Mask.Size()
+		addr := network.Address{Addr: iface.Address().IP.String(), PrefixLen: ones}
+		networkSettings.SecondaryIPAddresses = append(networkSettings.SecondaryIPAddresses, addr)
+
+		if iface.AddressIPv6().IP.To16() != nil {
+			onesv6, _ := iface.AddressIPv6().Mask.Size()
+			addrv6 := network.Address{Addr: iface.AddressIPv6().IP.String(), PrefixLen: onesv6}
+			networkSettings.SecondaryIPv6Addresses = append(networkSettings.SecondaryIPv6Addresses, addrv6)
+		}
+	}
+
+	return networkSettings, nil
+}
+
+func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error {
+	epInfo := ep.Info()
+	if epInfo == nil {
+		// It is not an error to get an empty endpoint info
 		return nil
 	}
 
-	var err error
+	container.NetworkSettings.Gateway = epInfo.Gateway().String()
+	if epInfo.GatewayIPv6().To16() != nil {
+		container.NetworkSettings.IPv6Gateway = epInfo.GatewayIPv6().String()
+	}
+
+	container.NetworkSettings.SandboxKey = epInfo.SandboxKey()
 
-	networkSettings, err := bridge.Allocate(container.ID, container.Config.MacAddress, "", "")
+	return nil
+}
+
+func (container *Container) updateNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error {
+	networkSettings := &network.Settings{NetworkID: n.ID(), EndpointID: ep.ID()}
+
+	networkSettings, err := container.buildPortMapInfo(n, ep, networkSettings)
 	if err != nil {
 		return err
 	}
 
-	// Error handling: At this point, the interface is allocated so we have to
-	// make sure that it is always released in case of error, otherwise we
-	// might leak resources.
+	networkSettings, err = container.buildEndpointInfo(n, ep, networkSettings)
+	if err != nil {
+		return err
+	}
+
+	if container.hostConfig.NetworkMode == runconfig.NetworkMode("bridge") {
+		networkSettings.Bridge = container.daemon.config.Bridge.Iface
+	}
+
+	container.NetworkSettings = networkSettings
+	return nil
+}
+
+func (container *Container) UpdateNetwork() error {
+	n, err := container.daemon.netController.NetworkByID(container.NetworkSettings.NetworkID)
+	if err != nil {
+		return fmt.Errorf("error locating network id %s: %v", container.NetworkSettings.NetworkID, err)
+	}
+
+	ep, err := n.EndpointByID(container.NetworkSettings.EndpointID)
+	if err != nil {
+		return fmt.Errorf("error locating endpoint id %s: %v", container.NetworkSettings.EndpointID, err)
+	}
+
+	if err := ep.Leave(container.ID); err != nil {
+		return fmt.Errorf("endpoint leave failed: %v", err)
+
+	}
+
+	joinOptions, err := container.buildJoinOptions()
+	if err != nil {
+		return fmt.Errorf("Update network failed: %v", err)
+	}
+
+	if _, err := ep.Join(container.ID, joinOptions...); err != nil {
+		return fmt.Errorf("endpoint join failed: %v", err)
+	}
+
+	if err := container.updateJoinInfo(ep); err != nil {
+		return fmt.Errorf("Updating join info failed: %v", err)
+	}
+
+	return nil
+}
+
+func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointOption, error) {
+	var (
+		portSpecs     = make(nat.PortSet)
+		bindings      = make(nat.PortMap)
+		pbList        []netutils.PortBinding
+		exposeList    []netutils.TransportPort
+		createOptions []libnetwork.EndpointOption
+	)
 
 	if container.Config.PortSpecs != nil {
-		if err = migratePortMappings(container.Config, container.hostConfig); err != nil {
-			bridge.Release(container.ID)
-			return err
+		if err := migratePortMappings(container.Config, container.hostConfig); err != nil {
+			return nil, err
 		}
 		container.Config.PortSpecs = nil
-		if err = container.WriteHostConfig(); err != nil {
-			bridge.Release(container.ID)
-			return err
+		if err := container.WriteHostConfig(); err != nil {
+			return nil, err
 		}
 	}
 
-	var (
-		portSpecs = make(nat.PortSet)
-		bindings  = make(nat.PortMap)
-	)
-
 	if container.Config.ExposedPorts != nil {
 		portSpecs = container.Config.ExposedPorts
 	}
@@ -597,52 +682,99 @@ func (container *Container) AllocateNetwork() error {
 	}
 	nat.SortPortMap(ports, bindings)
 	for _, port := range ports {
-		if err = container.allocatePort(port, bindings); err != nil {
-			bridge.Release(container.ID)
-			return err
+		expose := netutils.TransportPort{}
+		expose.Proto = netutils.ParseProtocol(port.Proto())
+		expose.Port = uint16(port.Int())
+		exposeList = append(exposeList, expose)
+
+		pb := netutils.PortBinding{Port: expose.Port, Proto: expose.Proto}
+		binding := bindings[port]
+		for i := 0; i < len(binding); i++ {
+			pbCopy := pb.GetCopy()
+			pbCopy.HostPort = uint16(nat.Port(binding[i].HostPort).Int())
+			pbCopy.HostIP = net.ParseIP(binding[i].HostIp)
+			pbList = append(pbList, pbCopy)
 		}
-	}
-	container.WriteHostConfig()
 
-	networkSettings.Ports = bindings
-	container.NetworkSettings = networkSettings
+		if container.hostConfig.PublishAllPorts && len(binding) == 0 {
+			pbList = append(pbList, pb)
+		}
+	}
 
-	return nil
-}
+	createOptions = append(createOptions,
+		libnetwork.CreateOptionPortMapping(pbList),
+		libnetwork.CreateOptionExposedPorts(exposeList))
 
-func (container *Container) initializeNetworking() error {
-	var err error
-	if container.hostConfig.NetworkMode.IsHost() {
-		container.Config.Hostname, err = os.Hostname()
+	if container.Config.MacAddress != "" {
+		mac, err := net.ParseMAC(container.Config.MacAddress)
 		if err != nil {
-			return err
+			return nil, err
 		}
 
-		parts := strings.SplitN(container.Config.Hostname, ".", 2)
-		if len(parts) > 1 {
-			container.Config.Hostname = parts[0]
-			container.Config.Domainname = parts[1]
+		genericOption := options.Generic{
+			netlabel.MacAddress: mac,
 		}
 
-		content, err := ioutil.ReadFile("/etc/hosts")
-		if os.IsNotExist(err) {
-			return container.buildHostnameAndHostsFiles("")
-		} else if err != nil {
-			return err
-		}
+		createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption))
+	}
 
-		if err := container.buildHostnameFile(); err != nil {
-			return err
-		}
+	return createOptions, nil
+}
 
-		hostsPath, err := container.GetRootResourcePath("hosts")
-		if err != nil {
-			return err
-		}
-		container.HostsPath = hostsPath
+func (container *Container) AllocateNetwork() error {
+	mode := container.hostConfig.NetworkMode
+	if container.Config.NetworkDisabled || mode.IsContainer() {
+		return nil
+	}
+
+	var err error
+
+	n, err := container.daemon.netController.NetworkByName(string(mode))
+	if err != nil {
+		return fmt.Errorf("error locating network with name %s: %v", string(mode), err)
+	}
 
-		return ioutil.WriteFile(container.HostsPath, content, 0644)
+	createOptions, err := container.buildCreateEndpointOptions()
+	if err != nil {
+		return err
+	}
+
+	ep, err := n.CreateEndpoint(container.Name, createOptions...)
+	if err != nil {
+		return err
+	}
+
+	if err := container.updateNetworkSettings(n, ep); err != nil {
+		return err
+	}
+
+	joinOptions, err := container.buildJoinOptions()
+	if err != nil {
+		return err
 	}
+
+	if _, err := ep.Join(container.ID, joinOptions...); err != nil {
+		return err
+	}
+
+	if err := container.updateJoinInfo(ep); err != nil {
+		return fmt.Errorf("Updating join info failed: %v", err)
+	}
+
+	container.WriteHostConfig()
+
+	return nil
+}
+
+func (container *Container) initializeNetworking() error {
+	var err error
+
+	// Make sure NetworkMode has an acceptable value before
+	// initializing networking.
+	if container.hostConfig.NetworkMode == runconfig.NetworkMode("") {
+		container.hostConfig.NetworkMode = runconfig.NetworkMode("bridge")
+	}
+
 	if container.hostConfig.NetworkMode.IsContainer() {
 		// we need to get the hosts files from the container to join
 		nc, err := container.getNetworkedContainer()
@@ -656,14 +788,30 @@ func (container *Container) initializeNetworking() error {
 		container.Config.Domainname = nc.Config.Domainname
 		return nil
 	}
+
 	if container.daemon.config.DisableNetwork {
 		container.Config.NetworkDisabled = true
-		return container.buildHostnameAndHostsFiles("127.0.1.1")
 	}
+
+	if container.hostConfig.NetworkMode.IsHost() {
+		container.Config.Hostname, err = os.Hostname()
+		if err != nil {
+			return err
+		}
+
+		parts := strings.SplitN(container.Config.Hostname, ".", 2)
+		if len(parts) > 1 {
+			container.Config.Hostname = parts[0]
+			container.Config.Domainname = parts[1]
+		}
+
+	}
+
 	if err := container.AllocateNetwork(); err != nil {
 		return err
 	}
-	return container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress)
+
+	return container.buildHostnameFile()
 }
 
 // Make sure the config is compatible with the current kernel
@@ -701,62 +849,6 @@ func (container *Container) ExportRw() (archive.Archive, error) {
 		nil
 }
 
-func (container *Container) buildHostnameFile() error {
-	hostnamePath, err := container.GetRootResourcePath("hostname")
-	if err != nil {
-		return err
-	}
-	container.HostnamePath = hostnamePath
-
-	if container.Config.Domainname != "" {
-		return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644)
-	}
-	return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
-}
-
-func (container *Container) buildHostsFiles(IP string) error {
-
-	hostsPath, err := container.GetRootResourcePath("hosts")
-	if err != nil {
-		return err
-	}
-	container.HostsPath = hostsPath
-
-	var extraContent []etchosts.Record
-
-	children, err := container.daemon.Children(container.Name)
-	if err != nil {
-		return err
-	}
-
-	for linkAlias, child := range children {
-		_, alias := filepath.Split(linkAlias)
-		// allow access to the linked container via the alias, real name, and container hostname
-		aliasList := alias + " " + child.Config.Hostname
-		// only add the name if alias isn't equal to the name
-		if alias != child.Name[1:] {
-			aliasList = aliasList + " " + child.Name[1:]
-		}
-		extraContent = append(extraContent, etchosts.Record{Hosts: aliasList, IP: child.NetworkSettings.IPAddress})
-	}
-
-	for _, extraHost := range container.hostConfig.ExtraHosts {
-		// allow IPv6 addresses in extra hosts; only split on first ":"
-		parts := strings.SplitN(extraHost, ":", 2)
-		extraContent = append(extraContent, etchosts.Record{Hosts: parts[0], IP: parts[1]})
-	}
-
-	return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, extraContent)
-}
-
-func (container *Container) buildHostnameAndHostsFiles(IP string) error {
-	if err := container.buildHostnameFile(); err != nil {
-		return err
-	}
-
-	return container.buildHostsFiles(IP)
-}
-
 func (container *Container) getIpcContainer() (*Container, error) {
 	containerID := container.hostConfig.IpcMode.Container()
 	c, err := container.daemon.Get(containerID)
@@ -795,23 +887,6 @@ func (container *Container) setupWorkingDirectory() error {
 	return nil
 }
 
-func (container *Container) allocatePort(port nat.Port, bindings nat.PortMap) error {
-	binding := bindings[port]
-	if container.hostConfig.PublishAllPorts && len(binding) == 0 {
-		binding = append(binding, nat.PortBinding{})
-	}
-
-	for i := 0; i < len(binding); i++ {
-		b, err := bridge.AllocatePort(container.ID, port, binding[i])
-		if err != nil {
-			return err
-		}
-		binding[i] = b
-	}
-	bindings[port] = binding
-	return nil
-}
-
 func (container *Container) getNetworkedContainer() (*Container, error) {
 	parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2)
 	switch parts[0] {
@@ -836,34 +911,31 @@ func (container *Container) getNetworkedContainer() (*Container, error) {
 }
 
 func (container *Container) ReleaseNetwork() {
-	if container.Config.NetworkDisabled || !container.hostConfig.NetworkMode.IsPrivate() {
+	if container.hostConfig.NetworkMode.IsContainer() {
 		return
 	}
-	bridge.Release(container.ID)
-	container.NetworkSettings = &network.Settings{}
-}
 
-func (container *Container) RestoreNetwork() error {
-	mode := container.hostConfig.NetworkMode
-	// Don't attempt a restore if we previously didn't allocate networking.
-	// This might be a legacy container with no network allocated, in which case the
-	// allocation will happen once and for all at start.
-	if !container.isNetworkAllocated() || container.Config.NetworkDisabled || !mode.IsPrivate() {
-		return nil
+	n, err := container.daemon.netController.NetworkByID(container.NetworkSettings.NetworkID)
+	if err != nil {
+		logrus.Errorf("error locating network id %s: %v", container.NetworkSettings.NetworkID, err)
+		return
 	}
 
-	// Re-allocate the interface with the same IP and MAC address.
-	if _, err := bridge.Allocate(container.ID, container.NetworkSettings.MacAddress, container.NetworkSettings.IPAddress, ""); err != nil {
-		return err
+	ep, err := n.EndpointByID(container.NetworkSettings.EndpointID)
+	if err != nil {
+		logrus.Errorf("error locating endpoint id %s: %v", container.NetworkSettings.EndpointID, err)
+		return
 	}
 
-	// Re-allocate any previously allocated ports.
-	for port := range container.NetworkSettings.Ports {
-		if err := container.allocatePort(port, container.NetworkSettings.Ports); err != nil {
-			return err
-		}
+	if err := ep.Leave(container.ID); err != nil {
+		logrus.Errorf("leaving endpoint failed: %v", err)
 	}
-	return nil
+
+	if err := ep.Delete(); err != nil {
+		logrus.Errorf("deleting endpoint failed: %v", err)
+	}
+
+	container.NetworkSettings = &network.Settings{}
 }
 
 func disableAllActiveLinks(container *Container) {
@@ -878,6 +950,10 @@ func (container *Container) DisableLink(name string) {
 	if container.activeLinks != nil {
 		if link, exists := container.activeLinks[name]; exists {
 			link.Disable()
+			delete(container.activeLinks, name)
+			if err := container.UpdateNetwork(); err != nil {
+				logrus.Debugf("Could not update network to remove link: %v", err)
+			}
 		} else {
 			logrus.Debugf("Could not find active link for %s", name)
 		}

+ 100 - 72
daemon/daemon.go

@@ -1,10 +1,10 @@
 package daemon
 
 import (
-	"bytes"
 	"fmt"
 	"io"
 	"io/ioutil"
+	"net"
 	"os"
 	"path"
 	"path/filepath"
@@ -15,6 +15,9 @@ import (
 	"time"
 
 	"github.com/docker/libcontainer/label"
+	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netlabel"
+	"github.com/docker/libnetwork/options"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api"
@@ -26,7 +29,6 @@ import (
 	_ "github.com/docker/docker/daemon/graphdriver/vfs"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/network"
-	"github.com/docker/docker/daemon/networkdriver/bridge"
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/archive"
@@ -37,7 +39,6 @@ import (
 	"github.com/docker/docker/pkg/namesgenerator"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers/kernel"
-	"github.com/docker/docker/pkg/resolvconf"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/truncindex"
@@ -46,8 +47,6 @@ import (
 	"github.com/docker/docker/trust"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/volumes"
-
-	"github.com/go-fsnotify/fsnotify"
 )
 
 var (
@@ -109,6 +108,7 @@ type Daemon struct {
 	defaultLogConfig runconfig.LogConfig
 	RegistryService  *registry.Service
 	EventsService    *events.Events
+	netController    libnetwork.NetworkController
 }
 
 // Get looks for a container using the provided information, which could be
@@ -349,61 +349,6 @@ func (daemon *Daemon) restore() error {
 	return nil
 }
 
-// set up the watch on the host's /etc/resolv.conf so that we can update container's
-// live resolv.conf when the network changes on the host
-func (daemon *Daemon) setupResolvconfWatcher() error {
-
-	watcher, err := fsnotify.NewWatcher()
-	if err != nil {
-		return err
-	}
-
-	//this goroutine listens for the events on the watch we add
-	//on the resolv.conf file on the host
-	go func() {
-		for {
-			select {
-			case event := <-watcher.Events:
-				if event.Name == "/etc/resolv.conf" &&
-					(event.Op&(fsnotify.Write|fsnotify.Create) != 0) {
-					// verify a real change happened before we go further--a file write may have happened
-					// without an actual change to the file
-					updatedResolvConf, newResolvConfHash, err := resolvconf.GetIfChanged()
-					if err != nil {
-						logrus.Debugf("Error retrieving updated host resolv.conf: %v", err)
-					} else if updatedResolvConf != nil {
-						// because the new host resolv.conf might have localhost nameservers..
-						updatedResolvConf, modified := resolvconf.FilterResolvDns(updatedResolvConf, daemon.config.Bridge.EnableIPv6)
-						if modified {
-							// changes have occurred during localhost cleanup: generate an updated hash
-							newHash, err := ioutils.HashData(bytes.NewReader(updatedResolvConf))
-							if err != nil {
-								logrus.Debugf("Error generating hash of new resolv.conf: %v", err)
-							} else {
-								newResolvConfHash = newHash
-							}
-						}
-						logrus.Debug("host network resolv.conf changed--walking container list for updates")
-						contList := daemon.containers.List()
-						for _, container := range contList {
-							if err := container.updateResolvConf(updatedResolvConf, newResolvConfHash); err != nil {
-								logrus.Debugf("Error on resolv.conf update check for container ID: %s: %v", container.ID, err)
-							}
-						}
-					}
-				}
-			case err := <-watcher.Errors:
-				logrus.Debugf("host resolv.conf notify error: %v", err)
-			}
-		}
-	}()
-
-	if err := watcher.Add("/etc"); err != nil {
-		return err
-	}
-	return nil
-}
-
 func (daemon *Daemon) checkDeprecatedExpose(config *runconfig.Config) bool {
 	if config != nil {
 		if config.PortSpecs != nil {
@@ -727,18 +672,15 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.
 }
 
 func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemon, err error) {
-	if config.Mtu == 0 {
-		config.Mtu = getDefaultNetworkMtu()
-	}
 	// Check for mutually incompatible config options
 	if config.Bridge.Iface != "" && config.Bridge.IP != "" {
 		return nil, fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.")
 	}
-	if !config.Bridge.EnableIptables && !config.Bridge.InterContainerCommunication {
+	if !config.Bridge.EnableIPTables && !config.Bridge.InterContainerCommunication {
 		return nil, fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.")
 	}
-	if !config.Bridge.EnableIptables && config.Bridge.EnableIpMasq {
-		config.Bridge.EnableIpMasq = false
+	if !config.Bridge.EnableIPTables && config.Bridge.EnableIPMasq {
+		config.Bridge.EnableIPMasq = false
 	}
 	config.DisableNetwork = config.Bridge.Iface == disableNetworkBridge
 
@@ -882,8 +824,9 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
 	}
 
 	if !config.DisableNetwork {
-		if err := bridge.InitDriver(&config.Bridge); err != nil {
-			return nil, fmt.Errorf("Error initializing Bridge: %v", err)
+		d.netController, err = initNetworkController(config)
+		if err != nil {
+			return nil, fmt.Errorf("Error initializing network controller: %v", err)
 		}
 	}
 
@@ -942,12 +885,97 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
 		return nil, err
 	}
 
-	// set up filesystem watch on resolv.conf for network changes
-	if err := d.setupResolvconfWatcher(); err != nil {
-		return nil, err
+	return d, nil
+}
+
+func initNetworkController(config *Config) (libnetwork.NetworkController, error) {
+	controller, err := libnetwork.New()
+	if err != nil {
+		return nil, fmt.Errorf("error obtaining controller instance: %v", err)
 	}
 
-	return d, nil
+	// Initialize default driver "null"
+
+	if err := controller.ConfigureNetworkDriver("null", options.Generic{}); err != nil {
+		return nil, fmt.Errorf("Error initializing null driver: %v", err)
+	}
+
+	// Initialize default network on "null"
+	if _, err := controller.NewNetwork("null", "none"); err != nil {
+		return nil, fmt.Errorf("Error creating default \"null\" network: %v", err)
+	}
+
+	// Initialize default driver "host"
+	if err := controller.ConfigureNetworkDriver("host", options.Generic{}); err != nil {
+		return nil, fmt.Errorf("Error initializing host driver: %v", err)
+	}
+
+	// Initialize default network on "host"
+	if _, err := controller.NewNetwork("host", "host"); err != nil {
+		return nil, fmt.Errorf("Error creating default \"host\" network: %v", err)
+	}
+
+	// Initialize default driver "bridge"
+	option := options.Generic{
+		"EnableIPForwarding": config.Bridge.EnableIPForward}
+
+	if err := controller.ConfigureNetworkDriver("bridge", options.Generic{netlabel.GenericData: option}); err != nil {
+		return nil, fmt.Errorf("Error initializing bridge driver: %v", err)
+	}
+
+	netOption := options.Generic{
+		"BridgeName":          config.Bridge.Iface,
+		"Mtu":                 config.Mtu,
+		"EnableIPTables":      config.Bridge.EnableIPTables,
+		"EnableIPMasquerade":  config.Bridge.EnableIPMasq,
+		"EnableICC":           config.Bridge.InterContainerCommunication,
+		"EnableUserlandProxy": config.Bridge.EnableUserlandProxy,
+	}
+
+	if config.Bridge.IP != "" {
+		ip, bipNet, err := net.ParseCIDR(config.Bridge.IP)
+		if err != nil {
+			return nil, err
+		}
+
+		bipNet.IP = ip
+		netOption["AddressIPv4"] = bipNet
+	}
+
+	if config.Bridge.FixedCIDR != "" {
+		_, fCIDR, err := net.ParseCIDR(config.Bridge.FixedCIDR)
+		if err != nil {
+			return nil, err
+		}
+
+		netOption["FixedCIDR"] = fCIDR
+	}
+
+	if config.Bridge.FixedCIDRv6 != "" {
+		_, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6)
+		if err != nil {
+			return nil, err
+		}
+
+		netOption["FixedCIDRv6"] = fCIDRv6
+	}
+
+	// --ip processing
+	if config.Bridge.DefaultIP != nil {
+		netOption["DefaultBindingIP"] = config.Bridge.DefaultIP
+	}
+
+	// Initialize default network on "bridge" with the same name
+	_, err = controller.NewNetwork("bridge", "bridge",
+		libnetwork.NetworkOptionGeneric(options.Generic{
+			netlabel.GenericData: netOption,
+			netlabel.EnableIPv6:  config.Bridge.EnableIPv6,
+		}))
+	if err != nil {
+		return nil, fmt.Errorf("Error creating default \"bridge\" network: %v", err)
+	}
+
+	return controller, nil
 }
 
 func (daemon *Daemon) Shutdown() error {

+ 4 - 3
daemon/delete.go

@@ -35,13 +35,14 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error
 		}
 		parentContainer, _ := daemon.Get(pe.ID())
 
+		if err := daemon.ContainerGraph().Delete(name); err != nil {
+			return err
+		}
+
 		if parentContainer != nil {
 			parentContainer.DisableLink(n)
 		}
 
-		if err := daemon.ContainerGraph().Delete(name); err != nil {
-			return err
-		}
 		return nil
 	}
 

+ 1 - 0
daemon/execdriver/driver.go

@@ -72,6 +72,7 @@ type Network struct {
 	Interface      *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
 	Mtu            int               `json:"mtu"`
 	ContainerID    string            `json:"container_id"` // id of the container to join network.
+	NamespacePath  string            `json:"namespace_path"`
 	HostNetworking bool              `json:"host_networking"`
 }
 

+ 55 - 9
daemon/execdriver/lxc/driver.go

@@ -12,6 +12,7 @@ import (
 	"os/exec"
 	"path"
 	"path/filepath"
+	"runtime"
 	"strconv"
 	"strings"
 	"sync"
@@ -30,6 +31,7 @@ import (
 	"github.com/docker/libcontainer/system"
 	"github.com/docker/libcontainer/user"
 	"github.com/kr/pty"
+	"github.com/vishvananda/netns"
 )
 
 const DriverName = "lxc"
@@ -80,6 +82,41 @@ func (d *driver) Name() string {
 	return fmt.Sprintf("%s-%s", DriverName, version)
 }
 
+func setupNetNs(nsPath string) (*os.Process, error) {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	origns, err := netns.Get()
+	if err != nil {
+		return nil, err
+	}
+	defer origns.Close()
+
+	f, err := os.OpenFile(nsPath, os.O_RDONLY, 0)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get network namespace %q: %v", nsPath, err)
+	}
+	defer f.Close()
+
+	nsFD := f.Fd()
+	if err := netns.Set(netns.NsHandle(nsFD)); err != nil {
+		return nil, fmt.Errorf("failed to set network namespace %q: %v", nsPath, err)
+	}
+	defer netns.Set(origns)
+
+	cmd := exec.Command("/bin/sh", "-c", "while true; do sleep 1; done")
+	if err := cmd.Start(); err != nil {
+		return nil, fmt.Errorf("failed to start netns process: %v", err)
+	}
+
+	return cmd.Process, nil
+}
+
+func killNetNsProc(proc *os.Process) {
+	proc.Kill()
+	proc.Wait()
+}
+
 func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
 	var (
 		term     execdriver.Terminal
@@ -87,6 +124,10 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 		dataPath = d.containerDir(c.ID)
 	)
 
+	if c.Network.NamespacePath == "" && c.Network.ContainerID == "" {
+		return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("empty namespace path for non-container network")
+	}
+
 	container, err := d.createContainer(c)
 	if err != nil {
 		return execdriver.ExitStatus{ExitCode: -1}, err
@@ -136,10 +177,20 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 		params = append(params, "-F")
 	}
 
+	proc := &os.Process{}
 	if c.Network.ContainerID != "" {
 		params = append(params,
 			"--share-net", c.Network.ContainerID,
 		)
+	} else {
+		proc, err = setupNetNs(c.Network.NamespacePath)
+		if err != nil {
+			return execdriver.ExitStatus{ExitCode: -1}, err
+		}
+
+		pidStr := fmt.Sprintf("%d", proc.Pid)
+		params = append(params,
+			"--share-net", pidStr)
 	}
 	if c.Ipc != nil {
 		if c.Ipc.ContainerID != "" {
@@ -157,15 +208,6 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 		"--",
 		c.InitPath,
 	)
-	if c.Network.Interface != nil {
-		params = append(params,
-			"-g", c.Network.Interface.Gateway,
-			"-i", fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
-		)
-	}
-	params = append(params,
-		"-mtu", strconv.Itoa(c.Network.Mtu),
-	)
 
 	if c.ProcessConfig.User != "" {
 		params = append(params, "-u", c.ProcessConfig.User)
@@ -214,10 +256,12 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 	c.ProcessConfig.Args = append([]string{name}, arg...)
 
 	if err := createDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil {
+		killNetNsProc(proc)
 		return execdriver.ExitStatus{ExitCode: -1}, err
 	}
 
 	if err := c.ProcessConfig.Start(); err != nil {
+		killNetNsProc(proc)
 		return execdriver.ExitStatus{ExitCode: -1}, err
 	}
 
@@ -245,8 +289,10 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 	// Poll lxc for RUNNING status
 	pid, err := d.waitForStart(c, waitLock)
 	if err != nil {
+		killNetNsProc(proc)
 		return terminate(err)
 	}
+	killNetNsProc(proc)
 
 	cgroupPaths, err := cgroupPaths(c.ID)
 	if err != nil {

+ 5 - 4
daemon/execdriver/lxc/lxc_template.go

@@ -15,8 +15,7 @@ import (
 	"github.com/docker/libcontainer/label"
 )
 
-const LxcTemplate = `
-{{if .Network.Interface}}
+/* {{if .Network.Interface}}
 # network configuration
 lxc.network.type = veth
 lxc.network.link = {{.Network.Interface.Bridge}}
@@ -30,8 +29,10 @@ lxc.network.type = none
 lxc.network.type = empty
 lxc.network.flags = up
 lxc.network.mtu = {{.Network.Mtu}}
-{{end}}
+{{end}} */
 
+const LxcTemplate = `
+lxc.network.type = none
 # root filesystem
 {{$ROOTFS := .Rootfs}}
 lxc.rootfs = {{$ROOTFS}}
@@ -145,6 +146,7 @@ lxc.network.ipv4.gateway = {{.Network.Interface.Gateway}}
 {{if .Network.Interface.MacAddress}}
 lxc.network.hwaddr = {{.Network.Interface.MacAddress}}
 {{end}}
+{{end}}
 {{if .ProcessConfig.Env}}
 lxc.utsname = {{getHostname .ProcessConfig.Env}}
 {{end}}
@@ -164,7 +166,6 @@ lxc.cap.drop = {{.}}
 		{{end}}
 	{{end}}
 {{end}}
-{{end}}
 `
 
 var LxcTemplateCompiled *template.Template

+ 4 - 28
daemon/execdriver/lxc/lxc_template_unit_test.go

@@ -264,13 +264,8 @@ func TestCustomLxcConfigMisc(t *testing.T) {
 			"lxc.cgroup.cpuset.cpus = 0,1",
 		},
 		Network: &execdriver.Network{
-			Mtu: 1500,
-			Interface: &execdriver.NetworkInterface{
-				Gateway:     "10.10.10.1",
-				IPAddress:   "10.10.10.10",
-				IPPrefixLen: 24,
-				Bridge:      "docker0",
-			},
+			Mtu:       1500,
+			Interface: nil,
 		},
 		ProcessConfig:   processConfig,
 		CapAdd:          []string{"net_admin", "syslog"},
@@ -282,13 +277,6 @@ func TestCustomLxcConfigMisc(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	// network
-	grepFile(t, p, "lxc.network.type = veth")
-	grepFile(t, p, "lxc.network.link = docker0")
-	grepFile(t, p, "lxc.network.name = eth0")
-	grepFile(t, p, "lxc.network.ipv4 = 10.10.10.10/24")
-	grepFile(t, p, "lxc.network.ipv4.gateway = 10.10.10.1")
-	grepFile(t, p, "lxc.network.flags = up")
 	grepFile(t, p, "lxc.aa_profile = lxc-container-default-with-nesting")
 	// hostname
 	grepFile(t, p, "lxc.utsname = testhost")
@@ -329,13 +317,8 @@ func TestCustomLxcConfigMiscOverride(t *testing.T) {
 			"lxc.network.ipv4 = 172.0.0.1",
 		},
 		Network: &execdriver.Network{
-			Mtu: 1500,
-			Interface: &execdriver.NetworkInterface{
-				Gateway:     "10.10.10.1",
-				IPAddress:   "10.10.10.10",
-				IPPrefixLen: 24,
-				Bridge:      "docker0",
-			},
+			Mtu:       1500,
+			Interface: nil,
 		},
 		ProcessConfig: processConfig,
 		CapAdd:        []string{"NET_ADMIN", "SYSLOG"},
@@ -346,13 +329,6 @@ func TestCustomLxcConfigMiscOverride(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	// network
-	grepFile(t, p, "lxc.network.type = veth")
-	grepFile(t, p, "lxc.network.link = docker0")
-	grepFile(t, p, "lxc.network.name = eth0")
-	grepFile(t, p, "lxc.network.ipv4 = 172.0.0.1")
-	grepFile(t, p, "lxc.network.ipv4.gateway = 10.10.10.1")
-	grepFile(t, p, "lxc.network.flags = up")
 
 	// hostname
 	grepFile(t, p, "lxc.utsname = testhost")

+ 6 - 34
daemon/execdriver/native/create.go

@@ -89,40 +89,6 @@ func generateIfaceName() (string, error) {
 }
 
 func (d *driver) createNetwork(container *configs.Config, c *execdriver.Command) error {
-	if c.Network.HostNetworking {
-		container.Namespaces.Remove(configs.NEWNET)
-		return nil
-	}
-
-	container.Networks = []*configs.Network{
-		{
-			Type: "loopback",
-		},
-	}
-
-	iName, err := generateIfaceName()
-	if err != nil {
-		return err
-	}
-	if c.Network.Interface != nil {
-		vethNetwork := configs.Network{
-			Name:              "eth0",
-			HostInterfaceName: iName,
-			Mtu:               c.Network.Mtu,
-			Address:           fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
-			MacAddress:        c.Network.Interface.MacAddress,
-			Gateway:           c.Network.Interface.Gateway,
-			Type:              "veth",
-			Bridge:            c.Network.Interface.Bridge,
-			HairpinMode:       c.Network.Interface.HairpinMode,
-		}
-		if c.Network.Interface.GlobalIPv6Address != "" {
-			vethNetwork.IPv6Address = fmt.Sprintf("%s/%d", c.Network.Interface.GlobalIPv6Address, c.Network.Interface.GlobalIPv6PrefixLen)
-			vethNetwork.IPv6Gateway = c.Network.Interface.IPv6Gateway
-		}
-		container.Networks = append(container.Networks, &vethNetwork)
-	}
-
 	if c.Network.ContainerID != "" {
 		d.Lock()
 		active := d.activeContainers[c.Network.ContainerID]
@@ -138,8 +104,14 @@ func (d *driver) createNetwork(container *configs.Config, c *execdriver.Command)
 		}
 
 		container.Namespaces.Add(configs.NEWNET, state.NamespacePaths[configs.NEWNET])
+		return nil
+	}
+
+	if c.Network.NamespacePath == "" {
+		return fmt.Errorf("network namespace path is empty")
 	}
 
+	container.Namespaces.Add(configs.NEWNET, c.Network.NamespacePath)
 	return nil
 }
 

+ 17 - 7
daemon/network/settings.go

@@ -2,18 +2,28 @@ package network
 
 import "github.com/docker/docker/nat"
 
+type Address struct {
+	Addr      string
+	PrefixLen int
+}
+
 type Settings struct {
+	Bridge                 string
+	EndpointID             string
+	Gateway                string
+	GlobalIPv6Address      string
+	GlobalIPv6PrefixLen    int
+	HairpinMode            bool
 	IPAddress              string
 	IPPrefixLen            int
-	MacAddress             string
+	IPv6Gateway            string
 	LinkLocalIPv6Address   string
 	LinkLocalIPv6PrefixLen int
-	GlobalIPv6Address      string
-	GlobalIPv6PrefixLen    int
-	Gateway                string
-	IPv6Gateway            string
-	Bridge                 string
+	MacAddress             string
+	NetworkID              string
 	PortMapping            map[string]map[string]string // Deprecated
 	Ports                  nat.PortMap
-	HairpinMode            bool
+	SandboxKey             string
+	SecondaryIPAddresses   []Address
+	SecondaryIPv6Addresses []Address
 }

+ 1 - 1
daemon/networkdriver/portmapper/proxy.go

@@ -17,7 +17,7 @@ import (
 	"github.com/docker/docker/pkg/reexec"
 )
 
-const userlandProxyCommandName = "docker-proxy"
+const userlandProxyCommandName = "docker-proxy-deprecated"
 
 func init() {
 	reexec.Register(userlandProxyCommandName, execProxy)

+ 39 - 2
integration-cli/docker_cli_daemon_test.go

@@ -15,6 +15,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/libnetwork/iptables"
 	"github.com/docker/libtrust"
 	"github.com/go-check/check"
 )
@@ -721,13 +722,49 @@ func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) {
 	c.Assert(matched, check.Equals, true,
 		check.Commentf("iptables output should have contained %q, but was %q", regex, out))
 
-	_, err = d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567")
-	c.Assert(err, check.IsNil)
+	out, err = d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567")
+	c.Assert(err, check.IsNil, check.Commentf(out))
 
 	out, err = d.Cmd("run", "--link", "icc1:icc1", "busybox", "nc", "icc1", "4567")
 	c.Assert(err, check.IsNil, check.Commentf(out))
 }
 
+func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *check.C) {
+	bridgeName := "external-bridge"
+	bridgeIp := "192.169.1.1/24"
+
+	out, err := createInterface(c, "bridge", bridgeName, bridgeIp)
+	c.Assert(err, check.IsNil, check.Commentf(out))
+	defer deleteInterface(c, bridgeName)
+
+	args := []string{"--bridge", bridgeName, "--icc=false"}
+	err = s.d.StartWithBusybox(args...)
+	c.Assert(err, check.IsNil)
+	defer s.d.Restart()
+
+	_, err = s.d.Cmd("run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top")
+	c.Assert(err, check.IsNil)
+	_, err = s.d.Cmd("run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top")
+	c.Assert(err, check.IsNil)
+
+	childIP := s.d.findContainerIP("child")
+	parentIP := s.d.findContainerIP("parent")
+
+	sourceRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
+	destinationRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
+	if !iptables.Exists("filter", "DOCKER", sourceRule...) || !iptables.Exists("filter", "DOCKER", destinationRule...) {
+		c.Fatal("Iptables rules not found")
+	}
+
+	s.d.Cmd("rm", "--link", "parent/http")
+	if iptables.Exists("filter", "DOCKER", sourceRule...) || iptables.Exists("filter", "DOCKER", destinationRule...) {
+		c.Fatal("Iptables rules should be removed when unlink")
+	}
+
+	s.d.Cmd("kill", "child")
+	s.d.Cmd("kill", "parent")
+}
+
 func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) {
 	testRequires(c, NativeExecDriver)
 

+ 0 - 26
integration-cli/docker_cli_links_test.go

@@ -10,7 +10,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/docker/docker/pkg/iptables"
 	"github.com/go-check/check"
 )
 
@@ -110,31 +109,6 @@ func (s *DockerSuite) TestLinksPingLinkedContainersAfterRename(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestLinksIpTablesRulesWhenLinkAndUnlink(c *check.C) {
-	testRequires(c, SameHostDaemon)
-
-	dockerCmd(c, "run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top")
-	dockerCmd(c, "run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top")
-
-	childIP := findContainerIP(c, "child")
-	parentIP := findContainerIP(c, "parent")
-
-	sourceRule := []string{"-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
-	destinationRule := []string{"-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
-	if !iptables.Exists("filter", "DOCKER", sourceRule...) || !iptables.Exists("filter", "DOCKER", destinationRule...) {
-		c.Fatal("Iptables rules not found")
-	}
-
-	dockerCmd(c, "rm", "--link", "parent/http")
-	if iptables.Exists("filter", "DOCKER", sourceRule...) || iptables.Exists("filter", "DOCKER", destinationRule...) {
-		c.Fatal("Iptables rules should be removed when unlink")
-	}
-
-	dockerCmd(c, "kill", "child")
-	dockerCmd(c, "kill", "parent")
-
-}
-
 func (s *DockerSuite) TestLinksInspectLinksStarted(c *check.C) {
 	var (
 		expected = map[string]struct{}{"/container1:/testinspectlink/alias1": {}, "/container2:/testinspectlink/alias2": {}}

+ 46 - 35
integration-cli/docker_cli_run_test.go

@@ -19,7 +19,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/nat"
-	"github.com/docker/docker/pkg/resolvconf"
+	"github.com/docker/libnetwork/resolvconf"
 	"github.com/go-check/check"
 )
 
@@ -1459,14 +1459,11 @@ func (s *DockerSuite) TestRunDnsOptionsBasedOnHostResolvConf(c *check.C) {
 	}
 }
 
-// Test the file watch notifier on docker host's /etc/resolv.conf
-// A go-routine is responsible for auto-updating containers which are
-// stopped and have an unmodified copy of resolv.conf, as well as
-// marking running containers as requiring an update on next restart
-func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
-	// Because overlay doesn't support inotify properly, we need to skip
-	// this test if the docker daemon has Storage Driver == overlay
-	testRequires(c, SameHostDaemon, NotOverlay)
+// Test if container resolv.conf gets updated the next time it restarts
+// if host /etc/resolv.conf has changed. This only applies if the container
+// uses the host's /etc/resolv.conf and does not have any dns options provided.
+func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
+	testRequires(c, SameHostDaemon)
 
 	tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78")
 	tmpLocalhostResolvConf := []byte("nameserver 127.0.0.1")
@@ -1492,7 +1489,7 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
 		}
 	}()
 
-	//1. test that a non-running container gets an updated resolv.conf
+	//1. test that a restarting container gets an updated resolv.conf
 	cmd = exec.Command(dockerBinary, "run", "--name='first'", "busybox", "true")
 	if _, err := runCommand(cmd); err != nil {
 		c.Fatal(err)
@@ -1508,17 +1505,26 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
 		c.Fatal(err)
 	}
 
-	time.Sleep(time.Second / 2)
+	// start the container again to pickup changes
+	cmd = exec.Command(dockerBinary, "start", "first")
+	if out, err := runCommand(cmd); err != nil {
+		c.Fatalf("Errored out %s, \nerror: %v", string(out), err)
+	}
+
 	// check for update in container
 	containerResolv, err := readContainerFile(containerID1, "resolv.conf")
 	if err != nil {
 		c.Fatal(err)
 	}
 	if !bytes.Equal(containerResolv, bytesResolvConf) {
-		c.Fatalf("Stopped container does not have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv))
+		c.Fatalf("Restarted container does not have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv))
 	}
 
-	//2. test that a non-running container does not receive resolv.conf updates
+	/* 	//make a change to resolv.conf (in this case replacing our tmp copy with orig copy)
+	   	if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
+	   		c.Fatal(err)
+	   	} */
+	//2. test that a restarting container does not receive resolv.conf updates
 	//   if it modified the container copy of the starting point resolv.conf
 	cmd = exec.Command(dockerBinary, "run", "--name='second'", "busybox", "sh", "-c", "echo 'search mylittlepony.com' >>/etc/resolv.conf")
 	if _, err = runCommand(cmd); err != nil {
@@ -1528,24 +1534,26 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
 	if err != nil {
 		c.Fatal(err)
 	}
-	containerResolvHashBefore, err := readContainerFile(containerID2, "resolv.conf.hash")
-	if err != nil {
-		c.Fatal(err)
-	}
 
 	//make a change to resolv.conf (in this case replacing our tmp copy with orig copy)
 	if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
 		c.Fatal(err)
 	}
 
-	time.Sleep(time.Second / 2)
-	containerResolvHashAfter, err := readContainerFile(containerID2, "resolv.conf.hash")
+	// start the container again
+	cmd = exec.Command(dockerBinary, "start", "second")
+	if out, err := runCommand(cmd); err != nil {
+		c.Fatalf("Errored out %s, \nerror: %v", string(out), err)
+	}
+
+	// check for update in container
+	containerResolv, err = readContainerFile(containerID2, "resolv.conf")
 	if err != nil {
 		c.Fatal(err)
 	}
 
-	if !bytes.Equal(containerResolvHashBefore, containerResolvHashAfter) {
-		c.Fatalf("Stopped container with modified resolv.conf should not have been updated; expected hash: %v, new hash: %v", containerResolvHashBefore, containerResolvHashAfter)
+	if bytes.Equal(containerResolv, resolvConfSystem) {
+		c.Fatalf("Restarting  a container after container updated resolv.conf should not pick up host changes; expected %q, got %q", string(containerResolv), string(resolvConfSystem))
 	}
 
 	//3. test that a running container's resolv.conf is not modified while running
@@ -1556,26 +1564,19 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
 	}
 	runningContainerID := strings.TrimSpace(out)
 
-	containerResolvHashBefore, err = readContainerFile(runningContainerID, "resolv.conf.hash")
-	if err != nil {
-		c.Fatal(err)
-	}
-
 	// replace resolv.conf
 	if err := ioutil.WriteFile("/etc/resolv.conf", bytesResolvConf, 0644); err != nil {
 		c.Fatal(err)
 	}
 
-	// make sure the updater has time to run to validate we really aren't
-	// getting updated
-	time.Sleep(time.Second / 2)
-	containerResolvHashAfter, err = readContainerFile(runningContainerID, "resolv.conf.hash")
+	// check for update in container
+	containerResolv, err = readContainerFile(runningContainerID, "resolv.conf")
 	if err != nil {
 		c.Fatal(err)
 	}
 
-	if !bytes.Equal(containerResolvHashBefore, containerResolvHashAfter) {
-		c.Fatalf("Running container's resolv.conf should not be updated; expected hash: %v, new hash: %v", containerResolvHashBefore, containerResolvHashAfter)
+	if bytes.Equal(containerResolv, bytesResolvConf) {
+		c.Fatalf("Running container should not have updated resolv.conf; expected %q, got %q", string(resolvConfSystem), string(containerResolv))
 	}
 
 	//4. test that a running container's resolv.conf is updated upon restart
@@ -1591,7 +1592,7 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
 		c.Fatal(err)
 	}
 	if !bytes.Equal(containerResolv, bytesResolvConf) {
-		c.Fatalf("Restarted container should have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv))
+		c.Fatalf("Restarted container should have updated resolv.conf; expected %q, got %q", string(bytesResolvConf), string(containerResolv))
 	}
 
 	//5. test that additions of a localhost resolver are cleaned from
@@ -1603,7 +1604,12 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
 		c.Fatal(err)
 	}
 
-	time.Sleep(time.Second / 2)
+	// start the container again to pickup changes
+	cmd = exec.Command(dockerBinary, "start", "first")
+	if out, err := runCommand(cmd); err != nil {
+		c.Fatalf("Errored out %s, \nerror: %v", string(out), err)
+	}
+
 	// our first exited container ID should have been updated, but with default DNS
 	// after the cleanup of resolv.conf found only a localhost nameserver:
 	containerResolv, err = readContainerFile(containerID1, "resolv.conf")
@@ -1645,7 +1651,12 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
 		c.Fatal(err)
 	}
 
-	time.Sleep(time.Second / 2)
+	// start the container again to pickup changes
+	cmd = exec.Command(dockerBinary, "start", "third")
+	if out, err := runCommand(cmd); err != nil {
+		c.Fatalf("Errored out %s, \nerror: %v", string(out), err)
+	}
+
 	// check for update in container
 	containerResolv, err = readContainerFile(containerID3, "resolv.conf")
 	if err != nil {

+ 0 - 18
links/links.go

@@ -5,9 +5,7 @@ import (
 	"path"
 	"strings"
 
-	"github.com/docker/docker/daemon/networkdriver/bridge"
 	"github.com/docker/docker/nat"
-	"github.com/docker/docker/pkg/iptables"
 )
 
 type Link struct {
@@ -140,26 +138,10 @@ func (l *Link) getDefaultPort() *nat.Port {
 }
 
 func (l *Link) Enable() error {
-	// -A == iptables append flag
-	if err := l.toggle("-A", false); err != nil {
-		return err
-	}
-	// call this on Firewalld reload
-	iptables.OnReloaded(func() { l.toggle("-A", false) })
 	l.IsEnabled = true
 	return nil
 }
 
 func (l *Link) Disable() {
-	// We do not care about errors here because the link may not
-	// exist in iptables
-	// -D == iptables delete flag
-	l.toggle("-D", true)
-	// call this on Firewalld reload
-	iptables.OnReloaded(func() { l.toggle("-D", true) })
 	l.IsEnabled = false
 }
-
-func (l *Link) toggle(action string, ignoreErrors bool) error {
-	return bridge.LinkContainers(action, l.ParentIP, l.ChildIP, l.Ports, ignoreErrors)
-}

+ 1 - 1
runconfig/hostconfig.go

@@ -18,7 +18,7 @@ type NetworkMode string
 
 // IsPrivate indicates whether container use it's private network stack
 func (n NetworkMode) IsPrivate() bool {
-	return !(n.IsHost() || n.IsContainer() || n.IsNone())
+	return !(n.IsHost() || n.IsContainer())
 }
 
 func (n NetworkMode) IsBridge() bool {