ソースを参照

Merge pull request #968 from aboch/ed6

Control IPv6 on container's interface
Santhosh Manohar 8 年 前
コミット
176088a742

+ 2 - 0
libnetwork/cmd/proxy/network_proxy_test.go

@@ -130,6 +130,7 @@ func TestTCP4Proxy(t *testing.T) {
 }
 
 func TestTCP6Proxy(t *testing.T) {
+	t.Skip("Need to start CI docker with --ipv6")
 	backend := NewEchoServer(t, "tcp", "[::1]:0")
 	defer backend.Close()
 	backend.Run()
@@ -173,6 +174,7 @@ func TestUDP4Proxy(t *testing.T) {
 }
 
 func TestUDP6Proxy(t *testing.T) {
+	t.Skip("Need to start CI docker with --ipv6")
 	backend := NewEchoServer(t, "udp", "[::1]:0")
 	defer backend.Close()
 	backend.Run()

+ 2 - 0
libnetwork/networkdb/networkdb_test.go

@@ -3,6 +3,7 @@ package networkdb
 import (
 	"flag"
 	"fmt"
+	"io/ioutil"
 	"log"
 	"os"
 	"sync/atomic"
@@ -21,6 +22,7 @@ var (
 )
 
 func TestMain(m *testing.M) {
+	ioutil.WriteFile("/proc/sys/net/ipv6/conf/lo/disable_ipv6", []byte{'0', '\n'}, 0644)
 	logrus.SetLevel(logrus.ErrorLevel)
 	os.Exit(m.Run())
 }

+ 7 - 0
libnetwork/osl/interface_linux.go

@@ -179,6 +179,8 @@ func (i *nwIface) Remove() error {
 	}
 	n.Unlock()
 
+	n.checkLoV6()
+
 	return nil
 }
 
@@ -318,6 +320,8 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If
 	n.iFaces = append(n.iFaces, i)
 	n.Unlock()
 
+	n.checkLoV6()
+
 	return nil
 }
 
@@ -378,6 +382,9 @@ func setInterfaceIPv6(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error
 	if err := checkRouteConflict(nlh, i.AddressIPv6(), netlink.FAMILY_V6); err != nil {
 		return err
 	}
+	if err := setIPv6(i.ns.path, i.DstName(), true); err != nil {
+		return fmt.Errorf("failed to enable ipv6: %v", err)
+	}
 	ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: "", Flags: syscall.IFA_F_NODAD}
 	return nlh.AddrAdd(iface, ipAddr)
 }

+ 98 - 0
libnetwork/osl/namespace_linux.go

@@ -24,6 +24,10 @@ import (
 
 const defaultPrefix = "/var/run/docker"
 
+func init() {
+	reexec.Register("set-ipv6", reexecSetIPv6)
+}
+
 var (
 	once             sync.Once
 	garbagePathMap   = make(map[string]bool)
@@ -47,6 +51,7 @@ type networkNamespace struct {
 	nextIfIndex  int
 	isDefault    bool
 	nlHandle     *netlink.Handle
+	loV6Enabled  bool
 	sync.Mutex
 }
 
@@ -216,6 +221,12 @@ func NewSandbox(key string, osCreate, isRestore bool) (Sandbox, error) {
 		logrus.Warnf("Failed to set the timeout on the sandbox netlink handle sockets: %v", err)
 	}
 
+	// As starting point, disable IPv6 on all interfaces
+	err = setIPv6(n.path, "all", false)
+	if err != nil {
+		logrus.Warnf("Failed to disable IPv6 on all interfaces on network namespace %q: %v", n.path, err)
+	}
+
 	if err = n.loopbackUp(); err != nil {
 		n.nlHandle.Delete()
 		return nil, err
@@ -263,6 +274,12 @@ func GetSandboxForExternalKey(basePath string, key string) (Sandbox, error) {
 		logrus.Warnf("Failed to set the timeout on the sandbox netlink handle sockets: %v", err)
 	}
 
+	// As starting point, disable IPv6 on all interfaces
+	err = setIPv6(n.path, "all", false)
+	if err != nil {
+		logrus.Warnf("Failed to disable IPv6 on all interfaces on network namespace %q: %v", n.path, err)
+	}
+
 	if err = n.loopbackUp(); err != nil {
 		n.nlHandle.Delete()
 		return nil, err
@@ -508,3 +525,84 @@ func (n *networkNamespace) Restore(ifsopt map[string][]IfaceOption, routes []*ty
 
 	return nil
 }
+
+// Checks whether IPv6 needs to be enabled/disabled on the loopback interface
+func (n *networkNamespace) checkLoV6() {
+	var (
+		enable = false
+		action = "disable"
+	)
+
+	n.Lock()
+	for _, iface := range n.iFaces {
+		if iface.AddressIPv6() != nil {
+			enable = true
+			action = "enable"
+			break
+		}
+	}
+	n.Unlock()
+
+	if n.loV6Enabled == enable {
+		return
+	}
+
+	if err := setIPv6(n.path, "lo", enable); err != nil {
+		logrus.Warnf("Failed to %s IPv6 on loopback interface on network namespace %q: %v", action, n.path, err)
+	}
+
+	n.loV6Enabled = enable
+}
+
+func reexecSetIPv6() {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	if len(os.Args) < 3 {
+		logrus.Errorf("invalid number of arguments for %s", os.Args[0])
+		os.Exit(1)
+	}
+
+	ns, err := netns.GetFromPath(os.Args[1])
+	if err != nil {
+		logrus.Errorf("failed get network namespace %q: %v", os.Args[1], err)
+		os.Exit(2)
+	}
+	defer ns.Close()
+
+	if err = netns.Set(ns); err != nil {
+		logrus.Errorf("setting into container netns %q failed: %v", os.Args[1], err)
+		os.Exit(3)
+	}
+
+	var (
+		action = "disable"
+		value  = byte('1')
+		path   = fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/disable_ipv6", os.Args[2])
+	)
+
+	if os.Args[3] == "true" {
+		action = "enable"
+		value = byte('0')
+	}
+
+	if err = ioutil.WriteFile(path, []byte{value, '\n'}, 0644); err != nil {
+		logrus.Errorf("failed to %s IPv6 forwarding for container's interface %s: %v", action, os.Args[2], err)
+		os.Exit(4)
+	}
+
+	os.Exit(0)
+}
+
+func setIPv6(path, iface string, enable bool) error {
+	cmd := &exec.Cmd{
+		Path:   reexec.Self(),
+		Args:   append([]string{"set-ipv6"}, path, iface, strconv.FormatBool(enable)),
+		Stdout: os.Stdout,
+		Stderr: os.Stderr,
+	}
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("reexec to set IPv6 failed: %v", err)
+	}
+	return nil
+}

+ 39 - 10
libnetwork/osl/sandbox_linux_test.go

@@ -7,6 +7,7 @@ import (
 	"net"
 	"os"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"syscall"
 	"testing"
@@ -190,17 +191,32 @@ func TestDisableIPv6DAD(t *testing.T) {
 
 	defer testutils.SetupTestOSContext(t)()
 
+	key, err := newKey(t)
+	if err != nil {
+		t.Fatalf("Failed to obtain a key: %v", err)
+	}
+
+	s, err := NewSandbox(key, true, false)
+	if err != nil {
+		t.Fatalf("Failed to create a new sandbox: %v", err)
+	}
+	runtime.LockOSThread()
+	defer s.Destroy()
+
+	n, ok := s.(*networkNamespace)
+	if !ok {
+		t.Fatal(ok)
+	}
+	nlh := n.nlHandle
+
 	ipv6, _ := types.ParseCIDR("2001:db8::44/64")
-	iface := &nwIface{addressIPv6: ipv6}
+	iface := &nwIface{addressIPv6: ipv6, ns: n, dstName: "sideA"}
 
 	veth := &netlink.Veth{
 		LinkAttrs: netlink.LinkAttrs{Name: "sideA"},
 		PeerName:  "sideB",
 	}
-	nlh, err := netlink.NewHandle(syscall.NETLINK_ROUTE)
-	if err != nil {
-		t.Fatal(err)
-	}
+
 	err = nlh.LinkAdd(veth)
 	if err != nil {
 		t.Fatal(err)
@@ -229,14 +245,27 @@ func TestDisableIPv6DAD(t *testing.T) {
 func TestSetInterfaceIP(t *testing.T) {
 	defer testutils.SetupTestOSContext(t)()
 
-	ipv4, _ := types.ParseCIDR("172.30.0.33/24")
-	ipv6, _ := types.ParseCIDR("2001:db8::44/64")
-	iface := &nwIface{address: ipv4, addressIPv6: ipv6}
+	key, err := newKey(t)
+	if err != nil {
+		t.Fatalf("Failed to obtain a key: %v", err)
+	}
 
-	nlh, err := netlink.NewHandle(syscall.NETLINK_ROUTE)
+	s, err := NewSandbox(key, true, false)
 	if err != nil {
-		t.Fatal(err)
+		t.Fatalf("Failed to create a new sandbox: %v", err)
 	}
+	runtime.LockOSThread()
+	defer s.Destroy()
+
+	n, ok := s.(*networkNamespace)
+	if !ok {
+		t.Fatal(ok)
+	}
+	nlh := n.nlHandle
+
+	ipv4, _ := types.ParseCIDR("172.30.0.33/24")
+	ipv6, _ := types.ParseCIDR("2001:db8::44/64")
+	iface := &nwIface{address: ipv4, addressIPv6: ipv6, ns: n, dstName: "sideA"}
 
 	if err := nlh.LinkAdd(&netlink.Veth{
 		LinkAttrs: netlink.LinkAttrs{Name: "sideA"},