diff --git a/daemon/config.go b/daemon/config.go index 40019e5036..952fb5f743 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -58,6 +58,8 @@ func (config *Config) InstallFlags() { 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.StringVar(&config.Bridge.DefaultGatewayIPv4, []string{"-default-gateway"}, "", "Container default gateway IPv4 address") + flag.StringVar(&config.Bridge.DefaultGatewayIPv6, []string{"-default-gateway-v6"}, "", "Container default gateway IPv6 address") 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") diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index e8627363a0..8f240ef598 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -77,8 +77,10 @@ var ( bridgeIface string bridgeIPv4Network *net.IPNet + gatewayIPv4 net.IP bridgeIPv6Addr net.IP globalIPv6Network *net.IPNet + gatewayIPv6 net.IP portMapper *portmapper.PortMapper once sync.Once @@ -103,6 +105,8 @@ type Config struct { IP string FixedCIDR string FixedCIDRv6 string + DefaultGatewayIPv4 string + DefaultGatewayIPv6 string InterContainerCommunication bool } @@ -278,6 +282,12 @@ func InitDriver(config *Config) error { } } + if gateway, err := requestDefaultGateway(config.DefaultGatewayIPv4, bridgeIPv4Network); err != nil { + return err + } else { + gatewayIPv4 = gateway + } + if config.FixedCIDRv6 != "" { _, subnet, err := net.ParseCIDR(config.FixedCIDRv6) if err != nil { @@ -289,6 +299,12 @@ func InitDriver(config *Config) error { return err } globalIPv6Network = subnet + + if gateway, err := requestDefaultGateway(config.DefaultGatewayIPv6, globalIPv6Network); err != nil { + return err + } else { + gatewayIPv6 = gateway + } } // Block BridgeIP in IP allocator @@ -473,6 +489,24 @@ func setupIPv6Bridge(bridgeIPv6 string) error { return nil } +func requestDefaultGateway(requestedGateway string, network *net.IPNet) (gateway net.IP, err error) { + if requestedGateway != "" { + gateway = net.ParseIP(requestedGateway) + + if gateway == nil { + return nil, fmt.Errorf("Bad parameter: invalid gateway ip %s", requestedGateway) + } + + if !network.Contains(gateway) { + return nil, fmt.Errorf("Gateway ip %s must be part of the network %s", requestedGateway, network.String()) + } + + ipAllocator.RequestIP(network, gateway) + } + + return gateway, nil +} + func createBridgeIface(name string) error { kv, err := kernel.GetKernelVersion() // Only set the bridge's mac address if the kernel version is > 3.3 @@ -522,10 +556,12 @@ func linkLocalIPv6FromMac(mac string) (string, error) { // Allocate a network interface func Allocate(id, requestedMac, requestedIP, requestedIPv6 string) (*network.Settings, error) { var ( - ip net.IP - mac net.HardwareAddr - err error - globalIPv6 net.IP + ip net.IP + mac net.HardwareAddr + err error + globalIPv6 net.IP + defaultGWIPv4 net.IP + defaultGWIPv6 net.IP ) ip, err = ipAllocator.RequestIP(bridgeIPv4Network, net.ParseIP(requestedIP)) @@ -560,6 +596,18 @@ func Allocate(id, requestedMac, requestedIP, requestedIPv6 string) (*network.Set maskSize, _ := bridgeIPv4Network.Mask.Size() + if gatewayIPv4 != nil { + defaultGWIPv4 = gatewayIPv4 + } else { + defaultGWIPv4 = bridgeIPv4Network.IP + } + + if gatewayIPv6 != nil { + defaultGWIPv6 = gatewayIPv6 + } else { + defaultGWIPv6 = bridgeIPv6Addr + } + // If linklocal IPv6 localIPv6Net, err := linkLocalIPv6FromMac(mac.String()) if err != nil { @@ -569,7 +617,7 @@ func Allocate(id, requestedMac, requestedIP, requestedIPv6 string) (*network.Set networkSettings := &network.Settings{ IPAddress: ip.String(), - Gateway: bridgeIPv4Network.IP.String(), + Gateway: defaultGWIPv4.String(), MacAddress: mac.String(), Bridge: bridgeIface, IPPrefixLen: maskSize, @@ -580,7 +628,7 @@ func Allocate(id, requestedMac, requestedIP, requestedIPv6 string) (*network.Set networkSettings.GlobalIPv6Address = globalIPv6.String() maskV6Size, _ := globalIPv6Network.Mask.Size() networkSettings.GlobalIPv6PrefixLen = maskV6Size - networkSettings.IPv6Gateway = bridgeIPv6Addr.String() + networkSettings.IPv6Gateway = defaultGWIPv6.String() } currentInterfaces.Set(id, &networkInterface{ diff --git a/docs/man/docker.1.md b/docs/man/docker.1.md index afa14b661c..53c54f9037 100644 --- a/docs/man/docker.1.md +++ b/docs/man/docker.1.md @@ -41,6 +41,12 @@ To see the man page for a command run **man docker **. **-d**, **--daemon**=*true*|*false* Enable daemon mode. Default is false. +**--default-gateway**="" + IPv4 address of the container default gateway; this address must be part of the bridge subnet (which is defined by \-b or \--bip) + +**--default-gateway-v6**="" + IPv6 address of the container default gateway + **--dns**="" Force Docker to use specific DNS servers diff --git a/docs/sources/articles/networking.md b/docs/sources/articles/networking.md index 46a907f7e2..2ce52ce0e0 100644 --- a/docs/sources/articles/networking.md +++ b/docs/sources/articles/networking.md @@ -56,6 +56,12 @@ server when it starts up, and cannot be changed once it is running: * `--bip=CIDR` — see [Customizing docker0](#docker0) + * `--default-gateway=IP_ADDRESS` — see + [How Docker networks a container](#container-networking) + + * `--default-gateway-v6=IP_ADDRESS` — see + [IPv6](#ipv6) + * `--fixed-cidr` — see [Customizing docker0](#docker0) @@ -499,7 +505,9 @@ want to configure `eth0` via Router Advertisements you should set: ![](/article-img/ipv6_basic_host_config.svg) Every new container will get an IPv6 address from the defined subnet. Further -a default route will be added via the gateway `fe80::1` on `eth0`: +a default route will be added on `eth0` in the container via the address +specified by the daemon option `--default-gateway-v6` if present, otherwise +via `fe80::1`: docker run -it ubuntu bash -c "ip -6 addr show dev eth0; ip -6 route show" @@ -865,12 +873,13 @@ The steps with which Docker configures a container are: parameter or generate a random one. 5. Give the container's `eth0` a new IP address from within the - bridge's range of network addresses, and set its default route to - the IP address that the Docker host owns on the bridge. The MAC - address is generated from the IP address unless otherwise specified. - This prevents ARP cache invalidation problems, when a new container - comes up with an IP used in the past by another container with another - MAC. + bridge's range of network addresses. The default route is set to the + IP address passed to the Docker daemon using the `--default-gateway` + option if specified, otherwise to the IP address that the Docker host + owns on the bridge. The MAC address is generated from the IP address + unless otherwise specified. This prevents ARP cache invalidation + problems, when a new container comes up with an IP used in the past by + another container with another MAC. With these steps complete, the container now possesses an `eth0` (virtual) network card and will find itself able to communicate with diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 821de99e30..986c0d45fb 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -145,6 +145,8 @@ expect an integer, and they can only be specified once. --bip="" Specify network bridge IP -D, --debug=false Enable debug mode -d, --daemon=false Enable daemon mode + --default-gateway="" Container default gateway IPv4 address + --default-gateway-v6="" Container default gateway IPv6 address --dns=[] DNS server to use --dns-search=[] DNS search domains to use -e, --exec-driver="native" Exec driver to use