Control IPv6 on container's interface

- Disable ipv6 on all interface by default at sandbox creation.
  Enable IPv6 per interface basis if the interface has an IPv6
  address. In case sandbox has an IPv6 interface, also enable
  IPv6 on loopback interface.

Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
Alessandro Boch 2016-02-23 13:28:14 -08:00
parent afcec80137
commit f195563a4e
5 changed files with 149 additions and 11 deletions

View File

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

View File

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

View File

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

View File

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

View File

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