소스 검색

Merge pull request #8947 from MalteJ/ipv6-daemon

Adding IPv6 network support to docker daemon
Alexander Morozov 10 년 전
부모
커밋
3fbf723e81

+ 6 - 2
daemon/config.go

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

+ 15 - 5
daemon/container.go

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

+ 2 - 0
daemon/daemon.go

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

+ 9 - 5
daemon/execdriver/driver.go

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

+ 4 - 0
daemon/execdriver/native/create.go

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

+ 12 - 7
daemon/network_settings.go

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

+ 179 - 30
daemon/networkdriver/bridge/driver.go

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

+ 15 - 10
daemon/networkdriver/utils.go

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

+ 10 - 2
docs/man/docker.1.md

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
docs/sources/article-img/ipv6_basic_host_config.gliffy


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
docs/sources/article-img/ipv6_basic_host_config.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
docs/sources/article-img/ipv6_routed_network_example.gliffy


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
docs/sources/article-img/ipv6_routed_network_example.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
docs/sources/article-img/ipv6_slash64_subnet_config.gliffy


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
docs/sources/article-img/ipv6_slash64_subnet_config.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
docs/sources/article-img/ipv6_switched_network_example.gliffy


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
docs/sources/article-img/ipv6_switched_network_example.svg


+ 184 - 2
docs/sources/articles/networking.md

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

+ 4 - 2
docs/sources/reference/commandline/cli.md

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

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.