1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Adding IPv6 network support to docker

Signed-off-by: Malte Janduda <mail@janduda.net>
This commit is contained in:
Malte Janduda 2015-01-09 00:03:19 +01:00
parent 273472a5c2
commit 813ff7f19d
19 changed files with 448 additions and 65 deletions

View file

@ -23,6 +23,7 @@ type Config struct {
AutoRestart bool
Dns []string
DnsSearch []string
EnableIPv6 bool
EnableIptables bool
EnableIpForward bool
EnableIpMasq bool
@ -30,6 +31,7 @@ type Config struct {
BridgeIface string
BridgeIP string
FixedCIDR string
FixedCIDRv6 string
InterContainerCommunication bool
GraphDriver string
GraphOptions []string
@ -51,11 +53,13 @@ func (config *Config) InstallFlags() {
flag.StringVar(&config.Root, []string{"g", "-graph"}, "/var/lib/docker", "Path to use as the 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 Docker's addition of iptables rules")
flag.BoolVar(&config.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
flag.BoolVar(&config.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward and IPv6 forwarding if --fixed-cidr-v6 is defined. IPv6 forwarding may interfere with your existing IPv6 configuration when using Router Advertisement.")
flag.BoolVar(&config.EnableIpMasq, []string{"-ip-masq"}, true, "Enable IP masquerading for bridge's IP range")
flag.BoolVar(&config.EnableIPv6, []string{"-ipv6"}, false, "Enable IPv6 networking")
flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
flag.StringVar(&config.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)\nthis subnet must be nested in the bridge subnet (which is defined by -b or --bip)")
flag.StringVar(&config.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs (e.g. 10.20.0.0/16)\nthis subnet must be nested in the bridge subnet (which is defined by -b or --bip)")
flag.StringVar(&config.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", "IPv6 subnet for fixed IPs (e.g.: 2001:a02b/48)")
flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Allow unrestricted inter-container and Docker daemon host communication")
flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver")
flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Force the Docker runtime to use a specific exec driver")

View file

@ -217,11 +217,15 @@ func populateCommand(c *Container, env []string) error {
if !c.Config.NetworkDisabled {
network := c.NetworkSettings
en.Interface = &execdriver.NetworkInterface{
Gateway: network.Gateway,
Bridge: network.Bridge,
IPAddress: network.IPAddress,
IPPrefixLen: network.IPPrefixLen,
MacAddress: network.MacAddress,
Gateway: network.Gateway,
Bridge: network.Bridge,
IPAddress: network.IPAddress,
IPPrefixLen: network.IPPrefixLen,
MacAddress: network.MacAddress,
LinkLocalIPv6Address: network.LinkLocalIPv6Address,
GlobalIPv6Address: network.GlobalIPv6Address,
GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen,
IPv6Gateway: network.IPv6Gateway,
}
}
case "container":
@ -540,6 +544,12 @@ func (container *Container) AllocateNetwork() error {
container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen")
container.NetworkSettings.MacAddress = env.Get("MacAddress")
container.NetworkSettings.Gateway = env.Get("Gateway")
container.NetworkSettings.MacAddress = env.Get("MacAddress")
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")
return nil
}

View file

@ -919,9 +919,11 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error)
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 {

View file

@ -78,11 +78,15 @@ type Ipc struct {
}
type NetworkInterface struct {
Gateway string `json:"gateway"`
IPAddress string `json:"ip"`
IPPrefixLen int `json:"ip_prefix_len"`
MacAddress string `json:"mac_address"`
Bridge string `json:"bridge"`
Gateway string `json:"gateway"`
IPAddress string `json:"ip"`
IPPrefixLen int `json:"ip_prefix_len"`
MacAddress string `json:"mac"`
Bridge string `json:"bridge"`
GlobalIPv6Address string `json:"global_ipv6"`
LinkLocalIPv6Address string `json:"link_local_ipv6"`
GlobalIPv6PrefixLen int `json:"global_ipv6_prefix_len"`
IPv6Gateway string `json:"ipv6_gateway"`
}
type Resources struct {

View file

@ -105,6 +105,10 @@ func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Com
Bridge: c.Network.Interface.Bridge,
VethPrefix: "veth",
}
if c.Network.Interface.GlobalIPv6Address != "" {
vethNetwork.IPv6Address = fmt.Sprintf("%s/%d", c.Network.Interface.GlobalIPv6Address, c.Network.Interface.GlobalIPv6PrefixLen)
vethNetwork.IPv6Gateway = c.Network.Interface.IPv6Gateway
}
container.Networks = append(container.Networks, &vethNetwork)
}

View file

@ -9,13 +9,18 @@ import (
type PortMapping map[string]string // Deprecated
type NetworkSettings struct {
IPAddress string
IPPrefixLen int
MacAddress string
Gateway string
Bridge string
PortMapping map[string]PortMapping // Deprecated
Ports nat.PortMap
IPAddress string
IPPrefixLen int
MacAddress string
LinkLocalIPv6Address string
LinkLocalIPv6PrefixLen int
GlobalIPv6Address string
GlobalIPv6PrefixLen int
Gateway string
IPv6Gateway string
Bridge string
PortMapping map[string]PortMapping // Deprecated
Ports nat.PortMap
}
func (settings *NetworkSettings) PortMappingAPI() *engine.Table {

View file

@ -1,10 +1,13 @@
package bridge
import (
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"strings"
"sync"
log "github.com/Sirupsen/logrus"
@ -27,6 +30,7 @@ const (
// Network interface represents the networking stack of a container
type networkInterface struct {
IP net.IP
IPv6 net.IP
PortMappings []net.Addr // there are mappings to the host interfaces
}
@ -69,8 +73,10 @@ var (
"192.168.44.1/24",
}
bridgeIface string
bridgeNetwork *net.IPNet
bridgeIface string
bridgeIPv4Network *net.IPNet
bridgeIPv6Addr net.IP
globalIPv6Network *net.IPNet
defaultBindingIP = net.ParseIP("0.0.0.0")
currentInterfaces = ifaces{c: make(map[string]*networkInterface)}
@ -78,13 +84,19 @@ var (
func InitDriver(job *engine.Job) engine.Status {
var (
network *net.IPNet
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")
)
if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
@ -98,41 +110,83 @@ func InitDriver(job *engine.Job) engine.Status {
bridgeIface = DefaultNetworkBridge
}
addr, err := networkdriver.GetIfaceAddr(bridgeIface)
addrv4, addrsv6, err := networkdriver.GetIfaceAddr(bridgeIface)
if err != nil {
// No Bridge existent. Create one
// If we're not using the default bridge, fail without trying to create it
if !usingDefaultBridge {
return job.Error(err)
}
// If the bridge interface is not found (or has no address), try to create it and/or add an address
if err := configureBridge(bridgeIP); err != nil {
// If the iface is not found, try to create it
if err := configureBridge(bridgeIP, bridgeIPv6, enableIPv6); err != nil {
return job.Error(err)
}
addr, err = networkdriver.GetIfaceAddr(bridgeIface)
addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface)
if err != nil {
return job.Error(err)
}
network = addr.(*net.IPNet)
if fixedCIDRv6 != "" {
// Setting route to global IPv6 subnet
log.Infof("Adding route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface)
if err := netlink.AddRoute(fixedCIDRv6, "", "", bridgeIface); err != nil {
log.Fatalf("Could not add route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface)
}
}
} else {
network = addr.(*net.IPNet)
// Bridge exists already. Getting info...
// validate that the bridge ip matches the ip specified by BridgeIP
if bridgeIP != "" {
networkv4 = addrv4.(*net.IPNet)
bip, _, err := net.ParseCIDR(bridgeIP)
if err != nil {
return job.Error(err)
}
if !network.IP.Equal(bip) {
return job.Errorf("bridge ip (%s) does not match existing bridge configuration %s", network.IP, bip)
if !networkv4.IP.Equal(bip) {
return job.Errorf("bridge ip (%s) does not match existing bridge configuration %s", networkv4.IP, bip)
}
}
// TODO: Check if route to fixedCIDRv6 is set
}
if enableIPv6 {
bip6, _, err := net.ParseCIDR(bridgeIPv6)
if err != nil {
return job.Error(err)
}
found := false
for _, addrv6 := range addrsv6 {
networkv6 = addrv6.(*net.IPNet)
if networkv6.IP.Equal(bip6) {
found = true
break
}
}
if !found {
return job.Errorf("bridge IPv6 does not match existing bridge configuration %s", bip6)
}
}
networkv4 = addrv4.(*net.IPNet)
log.Infof("enableIPv6 = %t", enableIPv6)
if enableIPv6 {
if len(addrsv6) == 0 {
return job.Error(errors.New("IPv6 enabled but no IPv6 detected"))
}
bridgeIPv6Addr = networkv6.IP
}
// Configure iptables for link support
if enableIPTables {
if err := setupIPTables(addr, icc, ipMasq); err != nil {
if err := setupIPTables(addrv4, icc, ipMasq); err != nil {
return job.Error(err)
}
}
if ipForward {
@ -140,6 +194,16 @@ func InitDriver(job *engine.Job) engine.Status {
if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil {
job.Logf("WARNING: unable to enable IPv4 forwarding: %s\n", err)
}
if fixedCIDRv6 != "" {
// Enable IPv6 forwarding
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, 0644); err != nil {
job.Logf("WARNING: unable to enable IPv6 default forwarding: %s\n", err)
}
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, 0644); err != nil {
job.Logf("WARNING: unable to enable IPv6 all forwarding: %s\n", err)
}
}
}
// We can always try removing the iptables
@ -159,23 +223,35 @@ func InitDriver(job *engine.Job) engine.Status {
portmapper.SetIptablesChain(chain)
}
bridgeNetwork = network
bridgeIPv4Network = networkv4
if fixedCIDR != "" {
_, subnet, err := net.ParseCIDR(fixedCIDR)
if err != nil {
return job.Error(err)
}
log.Debugf("Subnet: %v", subnet)
if err := ipallocator.RegisterSubnet(bridgeNetwork, subnet); err != nil {
if err := ipallocator.RegisterSubnet(bridgeIPv4Network, subnet); err != nil {
return job.Error(err)
}
}
if fixedCIDRv6 != "" {
_, subnet, err := net.ParseCIDR(fixedCIDRv6)
if err != nil {
return job.Error(err)
}
log.Debugf("Subnet: %v", subnet)
if err := ipallocator.RegisterSubnet(subnet, subnet); err != nil {
return job.Error(err)
}
globalIPv6Network = subnet
}
// Block BridgeIP in IP allocator
ipallocator.RequestIP(bridgeNetwork, bridgeNetwork.IP)
ipallocator.RequestIP(bridgeIPv4Network, bridgeIPv4Network.IP)
// https://github.com/docker/docker/issues/2768
job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeNetwork.IP)
job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeIPv4Network.IP)
for name, f := range map[string]engine.Handler{
"allocate_interface": Allocate,
@ -263,7 +339,7 @@ func setupIPTables(addr net.Addr, icc, ipmasq bool) error {
// If the bridge `bridgeIface` already exists, it will only perform the IP address association with the existing
// bridge (fixes issue #8444)
// If an address which doesn't conflict with existing interfaces can't be found, an error is returned.
func configureBridge(bridgeIP string) error {
func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error {
nameservers := []string{}
resolvConf, _ := resolvconf.Get()
// we don't check for an error here, because we don't really care
@ -323,6 +399,25 @@ func configureBridge(bridgeIP string) error {
if netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {
return fmt.Errorf("Unable to add private network: %s", err)
}
if enableIPv6 {
// Enable IPv6 on the bridge
procFile := "/proc/sys/net/ipv6/conf/" + iface.Name + "/disable_ipv6"
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
return fmt.Errorf("unable to enable IPv6 addresses on bridge: %s\n", err)
}
ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6)
if err != nil {
log.Errorf("BridgeIPv6 parsing failed")
return err
}
if netlink.NetworkLinkAddIp(iface, ipAddr6, ipNet6); err != nil {
return fmt.Errorf("Unable to add private IPv6 network: %s", err)
}
}
if err := netlink.NetworkLinkUp(iface); err != nil {
return fmt.Errorf("Unable to start network bridge: %s", err)
}
@ -363,20 +458,34 @@ func generateMacAddr(ip net.IP) net.HardwareAddr {
return hw
}
func linkLocalIPv6FromMac(mac string) (string, error) {
hx := strings.Replace(mac, ":", "", -1)
hw, err := hex.DecodeString(hx)
if err != nil {
return "", errors.New("Could not parse MAC address " + mac)
}
hw[0] ^= 0x2
return fmt.Sprintf("fe80::%x%x:%xff:fe%x:%x%x/64", hw[0], hw[1], hw[2], hw[3], hw[4], hw[5]), nil
}
// Allocate a network interface
func Allocate(job *engine.Job) engine.Status {
var (
ip net.IP
mac net.HardwareAddr
err error
id = job.Args[0]
requestedIP = net.ParseIP(job.Getenv("RequestedIP"))
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
)
if requestedIP != nil {
ip, err = ipallocator.RequestIP(bridgeNetwork, requestedIP)
ip, err = ipallocator.RequestIP(bridgeIPv4Network, requestedIP)
} else {
ip, err = ipallocator.RequestIP(bridgeNetwork, nil)
ip, err = ipallocator.RequestIP(bridgeIPv4Network, nil)
}
if err != nil {
return job.Error(err)
@ -387,18 +496,53 @@ func Allocate(job *engine.Job) engine.Status {
mac = generateMacAddr(ip)
}
if globalIPv6Network != nil {
// if globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address
netmask_ones, _ := globalIPv6Network.Mask.Size()
if requestedIPv6 == nil && netmask_ones <= 80 {
requestedIPv6 = globalIPv6Network.IP
for i, h := range mac {
requestedIPv6[i+10] = h
}
}
globalIPv6, err = ipallocator.RequestIP(globalIPv6Network, requestedIPv6)
if err != nil {
log.Errorf("Allocator: RequestIP v6: %s", err.Error())
return job.Error(err)
}
log.Infof("Allocated IPv6 %s", globalIPv6)
}
out := engine.Env{}
out.Set("IP", ip.String())
out.Set("Mask", bridgeNetwork.Mask.String())
out.Set("Gateway", bridgeNetwork.IP.String())
out.Set("Mask", bridgeIPv4Network.Mask.String())
out.Set("Gateway", bridgeIPv4Network.IP.String())
out.Set("MacAddress", mac.String())
out.Set("Bridge", bridgeIface)
size, _ := bridgeNetwork.Mask.Size()
size, _ := bridgeIPv4Network.Mask.Size()
out.SetInt("IPPrefixLen", size)
// if linklocal IPv6
localIPv6Net, err := linkLocalIPv6FromMac(mac.String())
if err != nil {
return job.Error(err)
}
localIPv6, _, _ := net.ParseCIDR(localIPv6Net)
out.Set("LinkLocalIPv6", localIPv6.String())
out.Set("MacAddress", mac.String())
if globalIPv6Network != nil {
out.Set("GlobalIPv6", globalIPv6.String())
sizev6, _ := globalIPv6Network.Mask.Size()
out.SetInt("GlobalIPv6PrefixLen", sizev6)
out.Set("IPv6Gateway", bridgeIPv6Addr.String())
}
currentInterfaces.Set(id, &networkInterface{
IP: ip,
IP: ip,
IPv6: globalIPv6,
})
out.WriteTo(job.Stdout)
@ -423,8 +567,13 @@ func Release(job *engine.Job) engine.Status {
}
}
if err := ipallocator.ReleaseIP(bridgeNetwork, containerInterface.IP); err != nil {
log.Infof("Unable to release ip %s", err)
if err := ipallocator.ReleaseIP(bridgeIPv4Network, containerInterface.IP); err != nil {
log.Infof("Unable to release IPv4 %s", err)
}
if globalIPv6Network != nil {
if err := ipallocator.ReleaseIP(globalIPv6Network, containerInterface.IPv6); err != nil {
log.Infof("Unable to release IPv6 %s", err)
}
}
return engine.StatusOK
}

View file

@ -44,11 +44,13 @@ func CheckRouteOverlaps(toCheck *net.IPNet) error {
// Detects overlap between one IPNet and another
func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool {
if firstIP, _ := NetworkRange(netX); netY.Contains(firstIP) {
return true
}
if firstIP, _ := NetworkRange(netY); netX.Contains(firstIP) {
return true
if len(netX.IP) == len(netY.IP) {
if firstIP, _ := NetworkRange(netX); netY.Contains(firstIP) {
return true
}
if firstIP, _ := NetworkRange(netY); netX.Contains(firstIP) {
return true
}
}
return false
}
@ -73,30 +75,33 @@ func NetworkRange(network *net.IPNet) (net.IP, net.IP) {
}
// Return the IPv4 address of a network interface
func GetIfaceAddr(name string) (net.Addr, error) {
func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) {
iface, err := net.InterfaceByName(name)
if err != nil {
return nil, err
return nil, nil, err
}
addrs, err := iface.Addrs()
if err != nil {
return nil, err
return nil, nil, err
}
var addrs4 []net.Addr
var addrs6 []net.Addr
for _, addr := range addrs {
ip := (addr.(*net.IPNet)).IP
if ip4 := ip.To4(); ip4 != nil {
addrs4 = append(addrs4, addr)
} else if ip6 := ip.To16(); len(ip6) == net.IPv6len {
addrs6 = append(addrs6, addr)
}
}
switch {
case len(addrs4) == 0:
return nil, fmt.Errorf("Interface %v has no IP addresses", name)
return nil, nil, fmt.Errorf("Interface %v has no IPv4 addresses", name)
case len(addrs4) > 1:
fmt.Printf("Interface %v has more than 1 IPv4 address. Defaulting to using %v\n",
name, (addrs4[0].(*net.IPNet)).IP)
}
return addrs4[0], nil
return addrs4[0], addrs6, nil
}
func GetDefaultRouteIface() (*net.Interface, error) {

View file

@ -52,9 +52,11 @@ unix://[/path/to/socket] to use.
**-g**=""
Path to use as the root of the Docker runtime. Default is `/var/lib/docker`.
**--fixed-cidr**=""
IPv4 subnet for fixed IPs (ex: 10.20.0.0/16); this subnet must be nested in the bridge subnet (which is defined by \-b or \-\-bip)
IPv4 subnet for fixed IPs (e.g., 10.20.0.0/16); this subnet must be nested in the bridge subnet (which is defined by \-b or \-\-bip)
**--fixed-cidr-v6**=""
IPv6 subnet for global IPv6 addresses (e.g., 2a00:1450::/64)
**--icc**=*true*|*false*
Allow unrestricted inter\-container and Docker daemon host communication. If disabled, containers can still be linked together using **--link** option (see **docker-run(1)**). Default is true.
@ -62,12 +64,18 @@ unix://[/path/to/socket] to use.
**--ip**=""
Default IP address to use when binding container ports. Default is `0.0.0.0`.
**--ip-forward**=*true*|*false*
Docker will enable IP forwarding. Default is true. If `--fixed-cidr-v6` is set. IPv6 forwarding will be activated, too. This may reject Router Advertisements and interfere with the host's existing IPv6 configuration. For more information please consult the documentation about "Advanced Networking - IPv6".
**--ip-masq**=*true*|*false*
Enable IP masquerading for bridge's IP range. Default is true.
**--iptables**=*true*|*false*
Disable Docker's addition of iptables rules. Default is true.
**--ipv6**=*true*|*false*
Enable IPv6 support. Default is false. Docker will create an IPv6-enabled bridge with address fe80::1 which will allow you to create IPv6-enabled containers. Use together with `--fixed-cidr-v6` to provide globally routable IPv6 addresses. IPv6 forwarding will be enabled if not used with `--ip-forward=false`. This may collide with your host's current IPv6 settings. For more information please consult the documentation about "Advanced Networking - IPv6".
**-l**, **--log-level**="*debug*|*info*|*error*|*fatal*""
Set the logging level. Default is `info`.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 80 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -57,6 +57,9 @@ server when it starts up, and cannot be changed once it is running:
* `--fixed-cidr` — see
[Customizing docker0](#docker0)
* `--fixed-cidr-v6` — see
[IPv6](#ipv6)
* `-H SOCKET...` or `--host=SOCKET...`
This might sound like it would affect container networking,
but it actually faces in the other direction:
@ -70,8 +73,11 @@ server when it starts up, and cannot be changed once it is running:
* `--ip=IP_ADDRESS` — see
[Binding container ports](#binding-ports)
* `--ipv6=true|false` — see
[IPv6](#ipv6)
* `--ip-forward=true|false` — see
[Communication between containers](#between-containers)
[Communication between containers and the wider world](#the-world)
* `--iptables=true|false` — see
[Communication between containers](#between-containers)
@ -204,7 +210,7 @@ Whether a container can talk to the world is governed by two factors.
containers if this parameter is `1`. Usually you will simply leave
the Docker server at its default setting `--ip-forward=true` and
Docker will go set `ip_forward` to `1` for you when the server
starts up. To check the setting or turn it on manually:
starts up. To check the setting or turn it on manually:
```
$ cat /proc/sys/net/ipv4/ip_forward
@ -397,6 +403,182 @@ Again, this topic is covered without all of these low-level networking
details in the [Docker User Guide](/userguide/dockerlinks/) document if you
would like to use that as your port redirection reference instead.
## IPv6
<a name="ipv6"></a>
As we are [running out of IPv4 addresses](http://en.wikipedia.org/wiki/IPv4_address_exhaustion)
the IETF has standardized an IPv4 successor, [Internet Protocol Version 6](http://en.wikipedia.org/wiki/IPv6)
, in [RFC 2460](https://www.ietf.org/rfc/rfc2460.txt). Both protocols, IPv4 and
IPv6, reside on layer 3 of the [OSI model](http://en.wikipedia.org/wiki/OSI_model).
### IPv6 with Docker
By default, the Docker server configures the container network for IPv4 only.
You can enable IPv4/IPv6 dualstack support by running the Docker daemon with the
`--ipv6` flag. Docker will set up the bridge `docker0` with the IPv6
[link-local address](http://en.wikipedia.org/wiki/Link-local_address) `fe80::1`.
By default, containers that are created will only get a link-local IPv6 address.
To assign globally routable IPv6 addresses to your containers you have to
specify an IPv6 subnet to pick the addresses from. Set the IPv6 subnet via the
`--fixed-cidr-v6` parameter when starting Docker daemon:
docker -d --ipv6 --fixed-cidr-v6="2001:db8:0:2:/64"
The subnet for Docker containers should at least have a size of `/80`. This way
an IPv6 address can end with the container's MAC address and you prevent NDP
neighbor cache invalidation issues in the Docker layer.
With the `--fixed-cidr-v6` parameter set Docker will add a new route to the
routing table. Further IPv6 routing will be enabled (you may prevent this by
starting Docker daemon with `--ip-forward=false`):
$ route -A inet6 add 2001:db8:0:2/64 dev docker0
$ echo 1 > /proc/sys/net/ipv6/conf/default/forwarding
$ echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
All traffic to the subnet `2001:db8:0:2/64` will now be routed
via the `docker0` interface.
Be aware that IPv6 forwarding may interfere with your existing IPv6
configuration: If you are using Router Advertisements to get IPv6 settings for
your host's interfaces you should set `accept_ra` to `2`. Otherwise IPv6
enabled forwarding will result in rejecting Router Advertisements. E.g., if you
want to configure `eth0` via Router Advertisements you should set:
```
$ echo 2 > /proc/sys/net/ipv6/conf/eth0/accept_ra
```
![](/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`:
docker run -it ubuntu bash -c "ifconfig eth0; route -A inet6"
eth0 Link encap:Ethernet HWaddr 02:42:ac:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: 2001:db8:0:2::1/64 Scope:Global
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST MTU:1500 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:110 (110.0 B) TX bytes:110 (110.0 B)
Kernel IPv6 routing table
Destination Next Hop Flag Met Ref Use If
2001:db8:0:2::/64 :: U 256 0 0 eth0
fe80::/64 :: U 256 0 0 eth0
::/0 fe80::1 UG 1024 0 0 eth0
::/0 :: !n -1 1 1 lo
::1/128 :: Un 0 1 0 lo
ff00::/8 :: U 256 1 0 eth0
::/0 :: !n -1 1 1 lo
In this example the Docker container is assigned a link-local address with the
network suffix `/64` (here: `fe80::42:acff:fe11:2/64`) and a globally routable
IPv6 address (here: `2001:db8:0:2::1/64`). The container will create connections
to addresses outside of the `2001:db8:0:2::/64` network via the link-local
gateway at `fe80::1` on `eth0`.
Often servers or virtual machines get a `/64` IPv6 subnet assigned. In this case
you can split it up further and provide Docker a `/80` subnet while using a
separate `/80` subnet for other applications on the host:
![](/article-img/ipv6_slash64_subnet_config.svg)
In this setup the subnet `2001:db8::/80` with a range from `2001:db8::0:0:0:0`
to `2001:db8::0:ffff:ffff:ffff` is attached to `eth0`, with the host listening
at `2001:db8::1`. The subnet `2001:db8:0:0:0:1::/80` with an address range from
`2001:db8::1:0:0:0` to `2001:db8::1:ffff:ffff:ffff` is attached to `docker0` and
will be used by containers.
### Docker IPv6 Cluster
#### Switched Network Environment
Using routable IPv6 addresses allows you to realize communication between
containers on different hosts. Let's have a look at a simple Docker IPv6 cluster
example:
![](/article-img/ipv6_switched_network_example.svg)
The Docker hosts are in the `2000::/64` subnet. Host1 is configured
to provide addresses from the `2001::/64` subnet to its containers. It has three
routes configured:
- Route all traffic to `2000::/64` via `eth0`
- Route all traffic to `2001::/64` via `docker0`
- Route all traffic to `2002::/64` via Host2 with IP `2000::2`
Host1 also acts as a router on OSI layer 3. When one of the network clients
tries to contact a target that is specified in Host1's routing table Host1 will
forward the traffic accordingly. It acts as a router for all networks it knows:
`2000:/64`, `2001:/64` and `2002::/64`.
On Host2 we have nearly the same configuration. Host2's containers will get IPv6
addresses from `2002::/64`. Host2 has three routes configured:
- Route all traffic to `2000::/64` via `eth0`
- Route all traffic to `2002::/64` via `docker0`
- Route all traffic to `2001::/64` via Host1 with IP `2000::1`
The difference to Host1 is that the network `2002::/64` is directly attached to
the host via its `docker0` interface whereas it reaches `2001::/64` via Host1's
IPv6 address `2000::1`.
This way every container is able to contact every other container. The
containers `Container1-*` share the same subnet and contact each other directly.
The traffic between `Container1-*` and `Container2-*` will be routed via Host1
and Host2 because those containers do not share the same subnet.
In a switched environment every host has to know all routes to every subnet. You
always have to update the hosts' routing tables once you add or remove a host
to the cluster.
Every configuration in the diagram that is shown below the dashed line is
handled by Docker: The `docker0` bridge IP address configuration, the route to
the Docker subnet on the host, the container IP addresses and the routes on the
containers. The configuration above the line is up to the user and can be
adapted to the individual environment.
#### Routed Network Environment
In a routed network environment you replace the level 2 switch with a level 3
router. Now the hosts just have to know their default gateway (the router) and
the route to their own containers (managed by Docker). The router holds all
routing information about the Docker subnets. When you add or remove a host to
this environment you just have to update the routing table in the router - not
on every host.
![](/article-img/ipv6_routed_network_example.svg)
In this scenario containers of the same host can communicate directly with each
other. The traffic between containers on different hosts will be routed via
their hosts and the router. For example packet from `Container1-1` to
`Container2-1` will be routed through `Host1`, `Router` and `Host2` until it
arrives at `Container2-1`.
To keep the IPv6 addresses short in this example a `/48` network is assigned to
every host. The hosts use a `/64` subnet of this for its own services and one
for Docker. When adding a third host you would add a route for the subnet
`2001:db8:3::/48` in the router and configure Docker on Host3 with
`--fixed-cidr-v6=2001:db8:3:1::/64`.
Remember the subnet for Docker containers should at least have a size of `/80`.
This way an IPv6 address can end with the container's MAC address and you
prevent ARP cache invalidation issues in the Docker layer. So if you have a
`/64` for your whole environment use `/68` subnets for the hosts and `/80` for
the containers. This way you can use 4096 hosts with 16 `/80` subnets each.
Every configuration in the diagram that is visualized below the dashed line is
handled by Docker: The `docker0` bridge IP address configuration, the route to
the Docker subnet on the host, the container IP addresses and the routes on the
containers. The configuration above the line is up to the user and can be
adapted to the individual environment.
## Customizing docker0
<a name="docker0"></a>

View file

@ -76,8 +76,9 @@ expect an integer, and they can only be specified once.
--dns=[] Force Docker to use specific DNS servers
--dns-search=[] Force Docker to use specific DNS search domains
-e, --exec-driver="native" Force the Docker runtime to use a specific exec driver
--fixed-cidr="" IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)
--fixed-cidr="" IPv4 subnet for fixed IPs (e.g.: 10.20.0.0/16)
this subnet must be nested in the bridge subnet (which is defined by -b or --bip)
--fixed-cidr-v6="" IPv6 subnet for global IPs (e.g.: 2a00:1450::/64)
-G, --group="docker" Group to assign the unix socket specified by -H when running in daemon mode
use '' (the empty string) to disable setting of a group
-g, --graph="/var/lib/docker" Path to use as the root of the Docker runtime
@ -85,9 +86,10 @@ expect an integer, and they can only be specified once.
--icc=true Allow unrestricted inter-container and Docker daemon host communication
--insecure-registry=[] Enable insecure communication with specified registries (disables certificate verification for HTTPS and enables HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16)
--ip=0.0.0.0 Default IP address to use when binding container ports
--ip-forward=true Enable net.ipv4.ip_forward
--ip-forward=true Enable net.ipv4.ip_forward and IPv6 forwarding if --fixed-cidr-v6 is defined. IPv6 forwarding may interfere with your existing IPv6 configuration when using Router Advertisement.
--ip-masq=true Enable IP masquerading for bridge's IP range
--iptables=true Enable Docker's addition of iptables rules
--ipv6=false Enable Docker IPv6 support
-l, --log-level="info" Set the logging level
--label=[] Set key=value labels to the daemon (displayed in `docker info`)
--mtu=0 Set the containers network MTU