Remove jobs from daemon/networkdriver/bridge

Signed-off-by: Tibor Vass <tibor@docker.com>
This commit is contained in:
Tibor Vass 2015-04-04 00:06:48 -04:00
parent 7233bd223d
commit 53582321ee
11 changed files with 233 additions and 433 deletions

View file

@ -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)
}

View file

@ -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{}

View file

@ -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")

View file

@ -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

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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