diff --git a/api/client/port.go b/api/client/port.go index a683db3f64..4c39314706 100644 --- a/api/client/port.go +++ b/api/client/port.go @@ -1,10 +1,10 @@ package client import ( + "encoding/json" "fmt" "strings" - "github.com/docker/docker/engine" "github.com/docker/docker/nat" flag "github.com/docker/docker/pkg/mflag" ) @@ -23,12 +23,13 @@ func (cli *DockerCli) CmdPort(args ...string) error { return err } - env := engine.Env{} - if err := env.Decode(stream); err != nil { - return err + var c struct { + NetworkSettings struct { + Ports nat.PortMap + } } - ports := nat.PortMap{} - if err := env.GetSubEnv("NetworkSettings").GetJson("Ports", &ports); err != nil { + + if err := json.NewDecoder(stream).Decode(&c); err != nil { return err } @@ -44,7 +45,7 @@ func (cli *DockerCli) CmdPort(args ...string) error { proto = parts[1] } natPort := port + "/" + proto - if frontends, exists := ports[nat.Port(port+"/"+proto)]; exists && frontends != nil { + if frontends, exists := c.NetworkSettings.Ports[nat.Port(port+"/"+proto)]; exists && frontends != nil { for _, frontend := range frontends { fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort) } @@ -53,7 +54,7 @@ func (cli *DockerCli) CmdPort(args ...string) error { return fmt.Errorf("Error: No public port '%s' published for %s", natPort, cmd.Arg(0)) } - for from, frontends := range ports { + for from, frontends := range c.NetworkSettings.Ports { for _, frontend := range frontends { fmt.Fprintf(cli.out, "%s -> %s:%s\n", from, frontend.HostIp, frontend.HostPort) } diff --git a/builtins/builtins.go b/builtins/builtins.go index 149d350090..8957b58332 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -6,15 +6,11 @@ import ( "github.com/docker/docker/api" apiserver "github.com/docker/docker/api/server" "github.com/docker/docker/autogen/dockerversion" - "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/engine" "github.com/docker/docker/pkg/parsers/kernel" ) func Register(eng *engine.Engine) error { - if err := daemon(eng); err != nil { - return err - } if err := remote(eng); err != nil { return err } @@ -33,25 +29,6 @@ func remote(eng *engine.Engine) error { return eng.Register("acceptconnections", apiserver.AcceptConnections) } -// daemon: a default execution and storage backend for Docker on Linux, -// with the following underlying components: -// -// * Pluggable storage drivers including aufs, vfs, lvm and btrfs. -// * Pluggable execution drivers including lxc and chroot. -// -// In practice `daemon` still includes most core Docker components, including: -// -// * The reference registry client implementation -// * Image management -// * The build facility -// * Logging -// -// These components should be broken off into plugins of their own. -// -func daemon(eng *engine.Engine) error { - return eng.Register("init_networkdriver", bridge.InitDriver) -} - // builtins jobs independent of any subsystem func dockerVersion(job *engine.Job) error { v := &engine.Env{} diff --git a/daemon/config.go b/daemon/config.go index 9b38fde4ed..b46895c58f 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -1,9 +1,8 @@ package daemon import ( - "net" - "github.com/docker/docker/daemon/networkdriver" + "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/ulimit" @@ -20,35 +19,27 @@ const ( // to the docker daemon when you launch it with say: `docker -d -e lxc` // FIXME: separate runtime configuration from http api configuration type Config struct { - Pidfile string - Root string - AutoRestart bool - Dns []string - DnsSearch []string - EnableIPv6 bool - EnableIptables bool - EnableIpForward bool - EnableIpMasq bool - DefaultIp net.IP - BridgeIface string - BridgeIP string - FixedCIDR string - FixedCIDRv6 string - InterContainerCommunication bool - GraphDriver string - GraphOptions []string - ExecDriver string - Mtu int - SocketGroup string - EnableCors bool - CorsHeaders string - DisableNetwork bool - EnableSelinuxSupport bool - Context map[string][]string - TrustKeyPath string - Labels []string - Ulimits map[string]*ulimit.Ulimit - LogConfig runconfig.LogConfig + Bridge bridge.Config + + Pidfile string + Root string + AutoRestart bool + Dns []string + DnsSearch []string + GraphDriver string + GraphOptions []string + ExecDriver string + Mtu int + SocketGroup string + EnableCors bool + CorsHeaders string + DisableNetwork bool + EnableSelinuxSupport bool + Context map[string][]string + TrustKeyPath string + Labels []string + Ulimits map[string]*ulimit.Ulimit + LogConfig runconfig.LogConfig } // InstallFlags adds command-line options to the top-level flag parser for @@ -59,15 +50,15 @@ func (config *Config) InstallFlags() { flag.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, "/var/run/docker.pid", "Path to use for daemon PID file") flag.StringVar(&config.Root, []string{"g", "-graph"}, "/var/lib/docker", "Root of the Docker runtime") 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.EnableIptables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules") - flag.BoolVar(&config.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward") - flag.BoolVar(&config.EnableIpMasq, []string{"-ip-masq"}, true, "Enable IP masquerading") - flag.BoolVar(&config.EnableIPv6, []string{"-ipv6"}, false, "Enable IPv6 networking") - flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Specify network bridge IP") - flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a network bridge") - flag.StringVar(&config.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs") - flag.StringVar(&config.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", "IPv6 subnet for fixed IPs") - flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication") + 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") + flag.StringVar(&config.Bridge.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs") + flag.StringVar(&config.Bridge.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", "IPv6 subnet for fixed IPs") + flag.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication") flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Storage driver to use") flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Exec driver to use") flag.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, "Enable selinux support") @@ -75,7 +66,7 @@ func (config *Config) InstallFlags() { flag.StringVar(&config.SocketGroup, []string{"G", "-group"}, "docker", "Group for the unix socket") 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.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") opts.ListVar(&config.GraphOptions, []string{"-storage-opt"}, "Set storage driver options") // FIXME: why the inconsistency between "hosts" and "sockets"? opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use") diff --git a/daemon/container.go b/daemon/container.go index 2d9487ea68..6f62f8b728 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -24,6 +24,8 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/jsonfilelog" "github.com/docker/docker/daemon/logger/syslog" + "github.com/docker/docker/daemon/network" + "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/engine" "github.com/docker/docker/image" "github.com/docker/docker/links" @@ -73,7 +75,7 @@ type Container struct { Config *runconfig.Config ImageID string `json:"Image"` - NetworkSettings *NetworkSettings + NetworkSettings *network.Settings ResolvConfPath string HostnamePath string @@ -571,17 +573,12 @@ func (container *Container) AllocateNetwork() error { } var ( - env *engine.Env err error eng = container.daemon.eng ) - job := eng.Job("allocate_interface", container.ID) - job.Setenv("RequestedMac", container.Config.MacAddress) - if env, err = job.Stdout.AddEnv(); err != nil { - return err - } - if err = job.Run(); err != nil { + networkSettings, err := bridge.Allocate(container.ID, container.Config.MacAddress, "", "") + if err != nil { return err } @@ -591,12 +588,12 @@ func (container *Container) AllocateNetwork() error { if container.Config.PortSpecs != nil { if err = migratePortMappings(container.Config, container.hostConfig); err != nil { - eng.Job("release_interface", container.ID).Run() + bridge.Release(container.ID) return err } container.Config.PortSpecs = nil if err = container.WriteHostConfig(); err != nil { - eng.Job("release_interface", container.ID).Run() + bridge.Release(container.ID) return err } } @@ -626,23 +623,14 @@ func (container *Container) AllocateNetwork() error { for port := range portSpecs { if err = container.allocatePort(eng, port, bindings); err != nil { - eng.Job("release_interface", container.ID).Run() + bridge.Release(container.ID) return err } } container.WriteHostConfig() - container.NetworkSettings.Ports = bindings - container.NetworkSettings.Bridge = env.Get("Bridge") - container.NetworkSettings.IPAddress = env.Get("IP") - container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen") - container.NetworkSettings.MacAddress = env.Get("MacAddress") - container.NetworkSettings.Gateway = env.Get("Gateway") - container.NetworkSettings.LinkLocalIPv6Address = env.Get("LinkLocalIPv6") - container.NetworkSettings.LinkLocalIPv6PrefixLen = 64 - container.NetworkSettings.GlobalIPv6Address = env.Get("GlobalIPv6") - container.NetworkSettings.GlobalIPv6PrefixLen = env.GetInt("GlobalIPv6PrefixLen") - container.NetworkSettings.IPv6Gateway = env.Get("IPv6Gateway") + networkSettings.Ports = bindings + container.NetworkSettings = networkSettings return nil } @@ -651,12 +639,10 @@ func (container *Container) ReleaseNetwork() { if container.Config.NetworkDisabled || !container.hostConfig.NetworkMode.IsPrivate() { return } - eng := container.daemon.eng - job := eng.Job("release_interface", container.ID) - job.SetenvBool("overrideShutdown", true) - job.Run() - container.NetworkSettings = &NetworkSettings{} + bridge.Release(container.ID) + + container.NetworkSettings = &network.Settings{} } func (container *Container) isNetworkAllocated() bool { @@ -675,10 +661,7 @@ func (container *Container) RestoreNetwork() error { eng := container.daemon.eng // Re-allocate the interface with the same IP and MAC address. - job := eng.Job("allocate_interface", container.ID) - job.Setenv("RequestedIP", container.NetworkSettings.IPAddress) - job.Setenv("RequestedMac", container.NetworkSettings.MacAddress) - if err := job.Run(); err != nil { + if _, err := bridge.Allocate(container.ID, container.NetworkSettings.MacAddress, container.NetworkSettings.IPAddress, ""); err != nil { return err } @@ -1077,7 +1060,7 @@ func (container *Container) setupContainerDns() error { 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.EnableIPv6) + 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 := utils.HashData(bytes.NewReader(updatedResolvConf)) @@ -1131,7 +1114,7 @@ func (container *Container) setupContainerDns() error { } // replace any localhost/127.*, and remove IPv6 nameservers if IPv6 disabled in daemon - resolvConf, _ = resolvconf.FilterResolvDns(resolvConf, daemon.config.EnableIPv6) + 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) @@ -1481,24 +1464,10 @@ func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bind } for i := 0; i < len(binding); i++ { - b := binding[i] - - job := eng.Job("allocate_port", container.ID) - job.Setenv("HostIP", b.HostIp) - job.Setenv("HostPort", b.HostPort) - job.Setenv("Proto", port.Proto()) - job.Setenv("ContainerPort", port.Port()) - - portEnv, err := job.Stdout.AddEnv() + b, err := bridge.AllocatePort(container.ID, port, binding[i]) if err != nil { return err } - if err := job.Run(); err != nil { - return err - } - b.HostIp = portEnv.Get("HostIP") - b.HostPort = portEnv.Get("HostPort") - binding[i] = b } bindings[port] = binding diff --git a/daemon/daemon.go b/daemon/daemon.go index 5aa6250a88..21a834e71b 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -25,7 +25,8 @@ import ( "github.com/docker/docker/daemon/execdriver/lxc" "github.com/docker/docker/daemon/graphdriver" _ "github.com/docker/docker/daemon/graphdriver/vfs" - _ "github.com/docker/docker/daemon/networkdriver/bridge" + "github.com/docker/docker/daemon/network" + "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/engine" "github.com/docker/docker/graph" "github.com/docker/docker/image" @@ -445,7 +446,7 @@ func (daemon *Daemon) setupResolvconfWatcher() error { 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.EnableIPv6) + updatedResolvConf, modified := resolvconf.FilterResolvDns(updatedResolvConf, daemon.config.Bridge.EnableIPv6) if modified { // changes have occurred during localhost cleanup: generate an updated hash newHash, err := utils.HashData(bytes.NewReader(updatedResolvConf)) @@ -653,7 +654,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID Config: config, hostConfig: &runconfig.HostConfig{}, ImageID: imgID, - NetworkSettings: &NetworkSettings{}, + NetworkSettings: &network.Settings{}, Name: name, Driver: daemon.driver.String(), ExecDriver: daemon.execDriver.Name(), @@ -807,16 +808,16 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService config.Mtu = getDefaultNetworkMtu() } // Check for mutually incompatible config options - if config.BridgeIface != "" && config.BridgeIP != "" { + if config.Bridge.Iface != "" && config.Bridge.IP != "" { return nil, fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.") } - if !config.EnableIptables && !config.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.EnableIptables && config.EnableIpMasq { - config.EnableIpMasq = false + if !config.Bridge.EnableIptables && config.Bridge.EnableIpMasq { + config.Bridge.EnableIpMasq = false } - config.DisableNetwork = config.BridgeIface == disableNetworkBridge + config.DisableNetwork = config.Bridge.Iface == disableNetworkBridge // Claim the pidfile first, to avoid any and all unexpected race conditions. // Some of the init doesn't need a pidfile lock - but let's not try to be smart. @@ -948,20 +949,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService } if !config.DisableNetwork { - job := eng.Job("init_networkdriver") - - job.SetenvBool("EnableIptables", config.EnableIptables) - job.SetenvBool("InterContainerCommunication", config.InterContainerCommunication) - job.SetenvBool("EnableIpForward", config.EnableIpForward) - job.SetenvBool("EnableIpMasq", config.EnableIpMasq) - job.SetenvBool("EnableIPv6", config.EnableIPv6) - job.Setenv("BridgeIface", config.BridgeIface) - job.Setenv("BridgeIP", config.BridgeIP) - job.Setenv("FixedCIDR", config.FixedCIDR) - job.Setenv("FixedCIDRv6", config.FixedCIDRv6) - job.Setenv("DefaultBindingIP", config.DefaultIp.String()) - - if err := job.Run(); err != nil { + if err := bridge.InitDriver(&config.Bridge); err != nil { return nil, err } } diff --git a/daemon/network_settings.go b/daemon/network/settings.go similarity index 56% rename from daemon/network_settings.go rename to daemon/network/settings.go index bf683b2f0e..f3841f09b1 100644 --- a/daemon/network_settings.go +++ b/daemon/network/settings.go @@ -1,13 +1,8 @@ -package daemon +package network -import ( - "github.com/docker/docker/nat" -) +import "github.com/docker/docker/nat" -// FIXME: move deprecated port stuff to nat to clean up the core. -type PortMapping map[string]string // Deprecated - -type NetworkSettings struct { +type Settings struct { IPAddress string IPPrefixLen int MacAddress string @@ -18,6 +13,6 @@ type NetworkSettings struct { Gateway string IPv6Gateway string Bridge string - PortMapping map[string]PortMapping // Deprecated + PortMapping map[string]map[string]string // Deprecated Ports nat.PortMap } diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index e974a9f239..dabb1165e7 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -7,14 +7,15 @@ import ( "io/ioutil" "net" "os" + "strconv" "strings" "sync" "github.com/Sirupsen/logrus" + "github.com/docker/docker/daemon/network" "github.com/docker/docker/daemon/networkdriver" "github.com/docker/docker/daemon/networkdriver/ipallocator" "github.com/docker/docker/daemon/networkdriver/portmapper" - "github.com/docker/docker/engine" "github.com/docker/docker/nat" "github.com/docker/docker/pkg/iptables" "github.com/docker/docker/pkg/parsers/kernel" @@ -91,29 +92,34 @@ func initPortMapper() { }) } -func InitDriver(job *engine.Job) error { +type Config struct { + EnableIPv6 bool + EnableIptables bool + EnableIpForward bool + EnableIpMasq bool + DefaultIp net.IP + Iface string + IP string + FixedCIDR string + FixedCIDRv6 string + InterContainerCommunication bool +} + +func InitDriver(config *Config) error { var ( - networkv4 *net.IPNet - networkv6 *net.IPNet - addrv4 net.Addr - addrsv6 []net.Addr - enableIPTables = job.GetenvBool("EnableIptables") - enableIPv6 = job.GetenvBool("EnableIPv6") - icc = job.GetenvBool("InterContainerCommunication") - ipMasq = job.GetenvBool("EnableIpMasq") - ipForward = job.GetenvBool("EnableIpForward") - bridgeIP = job.Getenv("BridgeIP") - bridgeIPv6 = "fe80::1/64" - fixedCIDR = job.Getenv("FixedCIDR") - fixedCIDRv6 = job.Getenv("FixedCIDRv6") + networkv4 *net.IPNet + networkv6 *net.IPNet + addrv4 net.Addr + addrsv6 []net.Addr + bridgeIPv6 = "fe80::1/64" ) initPortMapper() - if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" { - defaultBindingIP = net.ParseIP(defaultIP) + if config.DefaultIp != nil { + defaultBindingIP = config.DefaultIp } - bridgeIface = job.Getenv("BridgeIface") + bridgeIface = config.Iface usingDefaultBridge := false if bridgeIface == "" { usingDefaultBridge = true @@ -130,7 +136,7 @@ func InitDriver(job *engine.Job) error { } // If the iface is not found, try to create it - if err := configureBridge(bridgeIP, bridgeIPv6, enableIPv6); err != nil { + if err := configureBridge(config.IP, bridgeIPv6, config.EnableIPv6); err != nil { return err } @@ -139,19 +145,19 @@ func InitDriver(job *engine.Job) error { return err } - if fixedCIDRv6 != "" { + if config.FixedCIDRv6 != "" { // Setting route to global IPv6 subnet - logrus.Infof("Adding route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface) - if err := netlink.AddRoute(fixedCIDRv6, "", "", bridgeIface); err != nil { - logrus.Fatalf("Could not add route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface) + logrus.Infof("Adding route to IPv6 network %q via device %q", config.FixedCIDRv6, bridgeIface) + if err := netlink.AddRoute(config.FixedCIDRv6, "", "", bridgeIface); err != nil { + logrus.Fatalf("Could not add route to IPv6 network %q via device %q", config.FixedCIDRv6, bridgeIface) } } } else { // Bridge exists already, getting info... // Validate that the bridge ip matches the ip specified by BridgeIP - if bridgeIP != "" { + if config.IP != "" { networkv4 = addrv4.(*net.IPNet) - bip, _, err := net.ParseCIDR(bridgeIP) + bip, _, err := net.ParseCIDR(config.IP) if err != nil { return err } @@ -164,7 +170,7 @@ func InitDriver(job *engine.Job) error { // (for example, an existing Docker installation that has only been used // with IPv4 and docker0 already is set up) In that case, we can perform // the bridge init for IPv6 here, else we will error out below if --ipv6=true - if len(addrsv6) == 0 && enableIPv6 { + if len(addrsv6) == 0 && config.EnableIPv6 { if err := setupIPv6Bridge(bridgeIPv6); err != nil { return err } @@ -175,10 +181,10 @@ func InitDriver(job *engine.Job) error { } } - // TODO: Check if route to fixedCIDRv6 is set + // TODO: Check if route to config.FixedCIDRv6 is set } - if enableIPv6 { + if config.EnableIPv6 { bip6, _, err := net.ParseCIDR(bridgeIPv6) if err != nil { return err @@ -198,7 +204,7 @@ func InitDriver(job *engine.Job) error { networkv4 = addrv4.(*net.IPNet) - if enableIPv6 { + if config.EnableIPv6 { if len(addrsv6) == 0 { return errors.New("IPv6 enabled but no IPv6 detected") } @@ -206,20 +212,20 @@ func InitDriver(job *engine.Job) error { } // Configure iptables for link support - if enableIPTables { - if err := setupIPTables(addrv4, icc, ipMasq); err != nil { + if config.EnableIptables { + if err := setupIPTables(addrv4, config.InterContainerCommunication, config.EnableIpMasq); err != nil { return err } } - if ipForward { + if config.EnableIpForward { // Enable IPv4 forwarding if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil { logrus.Warnf("WARNING: unable to enable IPv4 forwarding: %s\n", err) } - if fixedCIDRv6 != "" { + if config.FixedCIDRv6 != "" { // Enable IPv6 forwarding if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, 0644); err != nil { logrus.Warnf("WARNING: unable to enable IPv6 default forwarding: %s\n", err) @@ -235,7 +241,7 @@ func InitDriver(job *engine.Job) error { return err } - if enableIPTables { + if config.EnableIptables { _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Nat) if err != nil { return err @@ -248,8 +254,8 @@ func InitDriver(job *engine.Job) error { } bridgeIPv4Network = networkv4 - if fixedCIDR != "" { - _, subnet, err := net.ParseCIDR(fixedCIDR) + if config.FixedCIDR != "" { + _, subnet, err := net.ParseCIDR(config.FixedCIDR) if err != nil { return err } @@ -259,8 +265,8 @@ func InitDriver(job *engine.Job) error { } } - if fixedCIDRv6 != "" { - _, subnet, err := net.ParseCIDR(fixedCIDRv6) + if config.FixedCIDRv6 != "" { + _, subnet, err := net.ParseCIDR(config.FixedCIDRv6) if err != nil { return err } @@ -274,19 +280,6 @@ func InitDriver(job *engine.Job) error { // Block BridgeIP in IP allocator ipAllocator.RequestIP(bridgeIPv4Network, bridgeIPv4Network.IP) - // https://github.com/docker/docker/issues/2768 - job.Eng.HackSetGlobalVar("httpapi.bridgeIP", bridgeIPv4Network.IP) - - for name, f := range map[string]engine.Handler{ - "allocate_interface": Allocate, - "release_interface": Release, - "allocate_port": AllocatePort, - "link": LinkContainers, - } { - if err := job.Eng.Register(name, f); err != nil { - return err - } - } return nil } @@ -513,70 +506,67 @@ func linkLocalIPv6FromMac(mac string) (string, error) { } // Allocate a network interface -func Allocate(job *engine.Job) error { +func Allocate(id, requestedMac, requestedIP, requestedIPv6 string) (*network.Settings, error) { var ( - ip net.IP - mac net.HardwareAddr - err error - id = job.Args[0] - requestedIP = net.ParseIP(job.Getenv("RequestedIP")) - requestedIPv6 = net.ParseIP(job.Getenv("RequestedIPv6")) - globalIPv6 net.IP + ip net.IP + mac net.HardwareAddr + err error + globalIPv6 net.IP ) - ip, err = ipAllocator.RequestIP(bridgeIPv4Network, requestedIP) + ip, err = ipAllocator.RequestIP(bridgeIPv4Network, net.ParseIP(requestedIP)) if err != nil { - return err + return nil, err } // If no explicit mac address was given, generate a random one. - if mac, err = net.ParseMAC(job.Getenv("RequestedMac")); err != nil { + if mac, err = net.ParseMAC(requestedMac); err != nil { mac = generateMacAddr(ip) } if globalIPv6Network != nil { // If globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address netmaskOnes, _ := globalIPv6Network.Mask.Size() - if requestedIPv6 == nil && netmaskOnes <= 80 { - requestedIPv6 = make(net.IP, len(globalIPv6Network.IP)) - copy(requestedIPv6, globalIPv6Network.IP) + ipv6 := net.ParseIP(requestedIPv6) + if ipv6 == nil && netmaskOnes <= 80 { + ipv6 = make(net.IP, len(globalIPv6Network.IP)) + copy(ipv6, globalIPv6Network.IP) for i, h := range mac { - requestedIPv6[i+10] = h + ipv6[i+10] = h } } - globalIPv6, err = ipAllocator.RequestIP(globalIPv6Network, requestedIPv6) + globalIPv6, err = ipAllocator.RequestIP(globalIPv6Network, ipv6) if err != nil { logrus.Errorf("Allocator: RequestIP v6: %v", err) - return err + return nil, err } logrus.Infof("Allocated IPv6 %s", globalIPv6) } - out := engine.Env{} - out.Set("IP", ip.String()) - out.Set("Mask", bridgeIPv4Network.Mask.String()) - out.Set("Gateway", bridgeIPv4Network.IP.String()) - out.Set("MacAddress", mac.String()) - out.Set("Bridge", bridgeIface) - - size, _ := bridgeIPv4Network.Mask.Size() - out.SetInt("IPPrefixLen", size) + maskSize, _ := bridgeIPv4Network.Mask.Size() // If linklocal IPv6 localIPv6Net, err := linkLocalIPv6FromMac(mac.String()) if err != nil { - return err + return nil, err } localIPv6, _, _ := net.ParseCIDR(localIPv6Net) - out.Set("LinkLocalIPv6", localIPv6.String()) - out.Set("MacAddress", mac.String()) + + networkSettings := &network.Settings{ + IPAddress: ip.String(), + Gateway: bridgeIPv4Network.IP.String(), + MacAddress: mac.String(), + Bridge: bridgeIface, + IPPrefixLen: maskSize, + LinkLocalIPv6Address: localIPv6.String(), + } if globalIPv6Network != nil { - out.Set("GlobalIPv6", globalIPv6.String()) - sizev6, _ := globalIPv6Network.Mask.Size() - out.SetInt("GlobalIPv6PrefixLen", sizev6) - out.Set("IPv6Gateway", bridgeIPv6Addr.String()) + networkSettings.GlobalIPv6Address = globalIPv6.String() + maskV6Size, _ := globalIPv6Network.Mask.Size() + networkSettings.GlobalIPv6PrefixLen = maskV6Size + networkSettings.IPv6Gateway = bridgeIPv6Addr.String() } currentInterfaces.Set(id, &networkInterface{ @@ -584,20 +574,15 @@ func Allocate(job *engine.Job) error { IPv6: globalIPv6, }) - out.WriteTo(job.Stdout) - - return nil + return networkSettings, nil } // Release an interface for a select ip -func Release(job *engine.Job) error { - var ( - id = job.Args[0] - containerInterface = currentInterfaces.Get(id) - ) +func Release(id string) { + var containerInterface = currentInterfaces.Get(id) if containerInterface == nil { - return fmt.Errorf("No network information to release for %s", id) + logrus.Warnf("No network information to release for %s", id) } for _, nat := range containerInterface.PortMappings { @@ -614,27 +599,21 @@ func Release(job *engine.Job) error { logrus.Infof("Unable to release IPv6 %s", err) } } - return nil } // Allocate an external port and map it to the interface -func AllocatePort(job *engine.Job) error { +func AllocatePort(id string, port nat.Port, binding nat.PortBinding) (nat.PortBinding, error) { var ( - err error - ip = defaultBindingIP - id = job.Args[0] - hostIP = job.Getenv("HostIP") - hostPort = job.GetenvInt("HostPort") - containerPort = job.GetenvInt("ContainerPort") - proto = job.Getenv("Proto") + proto = port.Proto() + containerPort = port.Int() network = currentInterfaces.Get(id) ) - if hostIP != "" { - ip = net.ParseIP(hostIP) + if binding.HostIp != "" { + ip = net.ParseIP(binding.HostIp) if ip == nil { - return fmt.Errorf("Bad parameter: invalid host ip %s", hostIP) + return nat.PortBinding{}, fmt.Errorf("Bad parameter: invalid host ip %s", binding.HostIp) } } @@ -646,7 +625,7 @@ func AllocatePort(job *engine.Job) error { case "udp": container = &net.UDPAddr{IP: network.IP, Port: containerPort} default: - return fmt.Errorf("unsupported address type %s", proto) + return nat.PortBinding{}, fmt.Errorf("unsupported address type %s", proto) } // @@ -656,7 +635,14 @@ func AllocatePort(job *engine.Job) error { // yields. // - var host net.Addr + var ( + host net.Addr + err error + ) + hostPort, err := nat.ParsePort(binding.HostPort) + if err != nil { + return nat.PortBinding{}, err + } for i := 0; i < MaxAllocatedPortAttempts; i++ { if host, err = portMapper.Map(container, ip, hostPort); err == nil { break @@ -671,36 +657,24 @@ func AllocatePort(job *engine.Job) error { } if err != nil { - return err + return nat.PortBinding{}, err } network.PortMappings = append(network.PortMappings, host) - out := engine.Env{} switch netAddr := host.(type) { case *net.TCPAddr: - out.Set("HostIP", netAddr.IP.String()) - out.SetInt("HostPort", netAddr.Port) + return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil case *net.UDPAddr: - out.Set("HostIP", netAddr.IP.String()) - out.SetInt("HostPort", netAddr.Port) + return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil + default: + return nat.PortBinding{}, fmt.Errorf("unsupported address type %T", netAddr) } - if _, err := out.WriteTo(job.Stdout); err != nil { - return err - } - - return nil } -func LinkContainers(job *engine.Job) error { - var ( - action = job.Args[0] - nfAction iptables.Action - childIP = job.Getenv("ChildIP") - parentIP = job.Getenv("ParentIP") - ignoreErrors = job.GetenvBool("IgnoreErrors") - ports = job.GetenvList("Ports") - ) +//TODO: should it return something more than just an error? +func LinkContainers(action, parentIP, childIP string, ports []nat.Port, ignoreErrors bool) error { + var nfAction iptables.Action switch action { case "-A": @@ -723,8 +697,7 @@ func LinkContainers(job *engine.Job) error { } chain := iptables.Chain{Name: "DOCKER", Bridge: bridgeIface} - for _, p := range ports { - port := nat.Port(p) + for _, port := range ports { if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil { return err } diff --git a/daemon/networkdriver/bridge/driver_test.go b/daemon/networkdriver/bridge/driver_test.go index b646dfd715..d18882e664 100644 --- a/daemon/networkdriver/bridge/driver_test.go +++ b/daemon/networkdriver/bridge/driver_test.go @@ -6,8 +6,9 @@ import ( "strconv" "testing" + "github.com/docker/docker/daemon/network" "github.com/docker/docker/daemon/networkdriver/portmapper" - "github.com/docker/docker/engine" + "github.com/docker/docker/nat" "github.com/docker/docker/pkg/iptables" ) @@ -16,7 +17,7 @@ func init() { portmapper.NewProxy = portmapper.NewMockProxyCommand } -func findFreePort(t *testing.T) int { +func findFreePort(t *testing.T) string { l, err := net.Listen("tcp", ":0") if err != nil { t.Fatal("Failed to find a free port") @@ -27,143 +28,85 @@ func findFreePort(t *testing.T) int { if err != nil { t.Fatal("Failed to resolve address to identify free port") } - return result.Port -} - -func newPortAllocationJob(eng *engine.Engine, port int) (job *engine.Job) { - strPort := strconv.Itoa(port) - - job = eng.Job("allocate_port", "container_id") - job.Setenv("HostIP", "127.0.0.1") - job.Setenv("HostPort", strPort) - job.Setenv("Proto", "tcp") - job.Setenv("ContainerPort", strPort) - return -} - -func newPortAllocationJobWithInvalidHostIP(eng *engine.Engine, port int) (job *engine.Job) { - strPort := strconv.Itoa(port) - - job = eng.Job("allocate_port", "container_id") - job.Setenv("HostIP", "localhost") - job.Setenv("HostPort", strPort) - job.Setenv("Proto", "tcp") - job.Setenv("ContainerPort", strPort) - return + return strconv.Itoa(result.Port) } func TestAllocatePortDetection(t *testing.T) { - eng := engine.New() - eng.Logging = false - freePort := findFreePort(t) - // Init driver - job := eng.Job("initdriver") - if res := InitDriver(job); res != nil { + if err := InitDriver(new(Config)); err != nil { t.Fatal("Failed to initialize network driver") } // Allocate interface - job = eng.Job("allocate_interface", "container_id") - if res := Allocate(job); res != nil { + if _, err := Allocate("container_id", "", "", ""); err != nil { t.Fatal("Failed to allocate network interface") } + port := nat.Port(freePort + "/tcp") + binding := nat.PortBinding{HostIp: "127.0.0.1", HostPort: freePort} + // Allocate same port twice, expect failure on second call - job = newPortAllocationJob(eng, freePort) - if res := AllocatePort(job); res != nil { + if _, err := AllocatePort("container_id", port, binding); err != nil { t.Fatal("Failed to find a free port to allocate") } - if res := AllocatePort(job); res == nil { + if _, err := AllocatePort("container_id", port, binding); err == nil { t.Fatal("Duplicate port allocation granted by AllocatePort") } } func TestHostnameFormatChecking(t *testing.T) { - eng := engine.New() - eng.Logging = false - freePort := findFreePort(t) - // Init driver - job := eng.Job("initdriver") - if res := InitDriver(job); res != nil { + if err := InitDriver(new(Config)); err != nil { t.Fatal("Failed to initialize network driver") } // Allocate interface - job = eng.Job("allocate_interface", "container_id") - if res := Allocate(job); res != nil { + if _, err := Allocate("container_id", "", "", ""); err != nil { t.Fatal("Failed to allocate network interface") } - // Allocate port with invalid HostIP, expect failure with Bad Request http status - job = newPortAllocationJobWithInvalidHostIP(eng, freePort) - if res := AllocatePort(job); res == nil { + port := nat.Port(freePort + "/tcp") + binding := nat.PortBinding{HostIp: "localhost", HostPort: freePort} + + if _, err := AllocatePort("container_id", port, binding); err == nil { t.Fatal("Failed to check invalid HostIP") } } -func newInterfaceAllocation(t *testing.T, input engine.Env) (output engine.Env) { - eng := engine.New() - eng.Logging = false - - done := make(chan bool) - +func newInterfaceAllocation(t *testing.T, globalIPv6 *net.IPNet, requestedMac, requestedIP, requestedIPv6 string, expectFail bool) *network.Settings { // set IPv6 global if given - if input.Exists("globalIPv6Network") { - _, globalIPv6Network, _ = net.ParseCIDR(input.Get("globalIPv6Network")) + if globalIPv6 != nil { + globalIPv6Network = globalIPv6 } - job := eng.Job("allocate_interface", "container_id") - job.Env().Init(&input) - reader, _ := job.Stdout.AddPipe() - go func() { - output.Decode(reader) - done <- true - }() + networkSettings, err := Allocate("container_id", requestedMac, requestedIP, requestedIPv6) + if err == nil && expectFail { + t.Fatal("Doesn't fail to allocate network interface") + } else if err != nil && !expectFail { + t.Fatal("Failed to allocate network interface") - res := Allocate(job) - job.Stdout.Close() - <-done - - if input.Exists("expectFail") && input.GetBool("expectFail") { - if res == nil { - t.Fatal("Doesn't fail to allocate network interface") - } - } else { - if res != nil { - t.Fatal("Failed to allocate network interface") - } } - if input.Exists("globalIPv6Network") { + if globalIPv6 != nil { // check for bug #11427 - _, subnet, _ := net.ParseCIDR(input.Get("globalIPv6Network")) - if globalIPv6Network.IP.String() != subnet.IP.String() { + if globalIPv6Network.IP.String() != globalIPv6.IP.String() { t.Fatal("globalIPv6Network was modified during allocation") } // clean up IPv6 global globalIPv6Network = nil } - return + return networkSettings } func TestIPv6InterfaceAllocationAutoNetmaskGt80(t *testing.T) { - - input := engine.Env{} - _, subnet, _ := net.ParseCIDR("2001:db8:1234:1234:1234::/81") - - // set global ipv6 - input.Set("globalIPv6Network", subnet.String()) - - output := newInterfaceAllocation(t, input) + networkSettings := newInterfaceAllocation(t, subnet, "", "", "", false) // ensure low manually assigend global ip - ip := net.ParseIP(output.Get("GlobalIPv6")) + ip := net.ParseIP(networkSettings.GlobalIPv6Address) _, subnet, _ = net.ParseCIDR(fmt.Sprintf("%s/%d", subnet.IP.String(), 120)) if !subnet.Contains(ip) { t.Fatalf("Error ip %s not in subnet %s", ip.String(), subnet.String()) @@ -171,26 +114,18 @@ func TestIPv6InterfaceAllocationAutoNetmaskGt80(t *testing.T) { } func TestIPv6InterfaceAllocationAutoNetmaskLe80(t *testing.T) { - - input := engine.Env{} - _, subnet, _ := net.ParseCIDR("2001:db8:1234:1234:1234::/80") - - // set global ipv6 - input.Set("globalIPv6Network", subnet.String()) - input.Set("RequestedMac", "ab:cd:ab:cd:ab:cd") - - output := newInterfaceAllocation(t, input) + networkSettings := newInterfaceAllocation(t, subnet, "ab:cd:ab:cd:ab:cd", "", "", false) // ensure global ip with mac - ip := net.ParseIP(output.Get("GlobalIPv6")) + ip := net.ParseIP(networkSettings.GlobalIPv6Address) expectedIP := net.ParseIP("2001:db8:1234:1234:1234:abcd:abcd:abcd") if ip.String() != expectedIP.String() { t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP.String()) } // ensure link local format - ip = net.ParseIP(output.Get("LinkLocalIPv6")) + ip = net.ParseIP(networkSettings.LinkLocalIPv6Address) expectedIP = net.ParseIP("fe80::a9cd:abff:fecd:abcd") if ip.String() != expectedIP.String() { t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP.String()) @@ -199,27 +134,19 @@ func TestIPv6InterfaceAllocationAutoNetmaskLe80(t *testing.T) { } func TestIPv6InterfaceAllocationRequest(t *testing.T) { - - input := engine.Env{} - _, subnet, _ := net.ParseCIDR("2001:db8:1234:1234:1234::/80") - expectedIP := net.ParseIP("2001:db8:1234:1234:1234::1328") + expectedIP := "2001:db8:1234:1234:1234::1328" - // set global ipv6 - input.Set("globalIPv6Network", subnet.String()) - input.Set("RequestedIPv6", expectedIP.String()) - - output := newInterfaceAllocation(t, input) + networkSettings := newInterfaceAllocation(t, subnet, "", "", expectedIP, false) // ensure global ip with mac - ip := net.ParseIP(output.Get("GlobalIPv6")) - if ip.String() != expectedIP.String() { - t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP.String()) + ip := net.ParseIP(networkSettings.GlobalIPv6Address) + if ip.String() != expectedIP { + t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP) } // retry -> fails for duplicated address - input.SetBool("expectFail", true) - output = newInterfaceAllocation(t, input) + _ = newInterfaceAllocation(t, subnet, "", "", expectedIP, true) } func TestMacAddrGeneration(t *testing.T) { @@ -239,40 +166,27 @@ func TestMacAddrGeneration(t *testing.T) { } func TestLinkContainers(t *testing.T) { - eng := engine.New() - eng.Logging = false - // Init driver - job := eng.Job("initdriver") - if res := InitDriver(job); res != nil { + if err := InitDriver(new(Config)); err != nil { t.Fatal("Failed to initialize network driver") } // Allocate interface - job = eng.Job("allocate_interface", "container_id") - if res := Allocate(job); res != nil { + if _, err := Allocate("container_id", "", "", ""); err != nil { t.Fatal("Failed to allocate network interface") } - job.Args[0] = "-I" - - job.Setenv("ChildIP", "172.17.0.2") - job.Setenv("ParentIP", "172.17.0.1") - job.SetenvBool("IgnoreErrors", false) - job.SetenvList("Ports", []string{"1234"}) - bridgeIface = "lo" - _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter) - if err != nil { + if _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter); err != nil { t.Fatal(err) } - if res := LinkContainers(job); res != nil { - t.Fatalf("LinkContainers failed") + if err := LinkContainers("-I", "172.17.0.1", "172.17.0.2", []nat.Port{nat.Port("1234")}, false); err != nil { + t.Fatal("LinkContainers failed") } // flush rules - if _, err = iptables.Raw([]string{"-F", "DOCKER"}...); err != nil { + if _, err := iptables.Raw([]string{"-F", "DOCKER"}...); err != nil { t.Fatal(err) } diff --git a/integration/utils_test.go b/integration/utils_test.go index c0e826a0f0..ee7293034a 100644 --- a/integration/utils_test.go +++ b/integration/utils_test.go @@ -18,6 +18,7 @@ import ( "github.com/docker/docker/builtins" "github.com/docker/docker/daemon" + "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/engine" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/registry" @@ -185,9 +186,11 @@ func newTestEngine(t Fataler, autorestart bool, root string) *engine.Engine { ExecDriver: "native", // Either InterContainerCommunication or EnableIptables must be set, // otherwise NewDaemon will fail because of conflicting settings. - InterContainerCommunication: true, - TrustKeyPath: filepath.Join(root, "key.json"), - LogConfig: runconfig.LogConfig{Type: "json-file"}, + Bridge: bridge.Config{ + InterContainerCommunication: true, + }, + TrustKeyPath: filepath.Join(root, "key.json"), + LogConfig: runconfig.LogConfig{Type: "json-file"}, } d, err := daemon.NewDaemon(cfg, eng, registry.NewService(nil)) if err != nil { diff --git a/links/links.go b/links/links.go index 96c18cc243..0e5e806e57 100644 --- a/links/links.go +++ b/links/links.go @@ -2,10 +2,12 @@ package links import ( "fmt" - "github.com/docker/docker/engine" - "github.com/docker/docker/nat" "path" "strings" + + "github.com/docker/docker/daemon/networkdriver/bridge" + "github.com/docker/docker/engine" + "github.com/docker/docker/nat" ) type Link struct { @@ -158,21 +160,5 @@ func (l *Link) Disable() { } func (l *Link) toggle(action string, ignoreErrors bool) error { - job := l.eng.Job("link", action) - - job.Setenv("ParentIP", l.ParentIP) - job.Setenv("ChildIP", l.ChildIP) - job.SetenvBool("IgnoreErrors", ignoreErrors) - - out := make([]string, len(l.Ports)) - for i, p := range l.Ports { - out[i] = string(p) - } - job.SetenvList("Ports", out) - - if err := job.Run(); err != nil { - // TODO: get ouput from job - return err - } - return nil + return bridge.LinkContainers(action, l.ParentIP, l.ChildIP, l.Ports, ignoreErrors) } diff --git a/nat/nat.go b/nat/nat.go index fdecf3f9d1..2cec2e86ce 100644 --- a/nat/nat.go +++ b/nat/nat.go @@ -34,6 +34,9 @@ func NewPort(proto, port string) Port { } func ParsePort(rawPort string) (int, error) { + if len(rawPort) == 0 { + return 0, nil + } port, err := strconv.ParseUint(rawPort, 10, 16) if err != nil { return 0, err