diff --git a/libnetwork/cmd/proxy/network_proxy_test.go b/libnetwork/cmd/proxy/network_proxy_test.go index b3379dabb3..7f036b537f 100644 --- a/libnetwork/cmd/proxy/network_proxy_test.go +++ b/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() diff --git a/libnetwork/networkdb/networkdb_test.go b/libnetwork/networkdb/networkdb_test.go index bdcb6c51a5..c9bd6fb125 100644 --- a/libnetwork/networkdb/networkdb_test.go +++ b/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()) } diff --git a/libnetwork/osl/interface_linux.go b/libnetwork/osl/interface_linux.go index 2be99d72ea..d76966200a 100644 --- a/libnetwork/osl/interface_linux.go +++ b/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) } diff --git a/libnetwork/osl/namespace_linux.go b/libnetwork/osl/namespace_linux.go index fe8065a31e..b714ccf9fa 100644 --- a/libnetwork/osl/namespace_linux.go +++ b/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 +} diff --git a/libnetwork/osl/sandbox_linux_test.go b/libnetwork/osl/sandbox_linux_test.go index d71e24a21d..45345aa9f0 100644 --- a/libnetwork/osl/sandbox_linux_test.go +++ b/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)() + 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 + ipv4, _ := types.ParseCIDR("172.30.0.33/24") ipv6, _ := types.ParseCIDR("2001:db8::44/64") - iface := &nwIface{address: ipv4, addressIPv6: ipv6} - - nlh, err := netlink.NewHandle(syscall.NETLINK_ROUTE) - if err != nil { - t.Fatal(err) - } + iface := &nwIface{address: ipv4, addressIPv6: ipv6, ns: n, dstName: "sideA"} if err := nlh.LinkAdd(&netlink.Veth{ LinkAttrs: netlink.LinkAttrs{Name: "sideA"},