Skip to content

Commit

Permalink
Control IPv6 on container's interface
Browse files Browse the repository at this point in the history
- 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 <[email protected]>
  • Loading branch information
aboch committed Nov 22, 2016
1 parent dd0ddde commit 947eb35
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 10 deletions.
2 changes: 2 additions & 0 deletions cmd/proxy/network_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 2 additions & 0 deletions networkdb/networkdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package networkdb
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"sync/atomic"
Expand All @@ -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())
}
Expand Down
7 changes: 7 additions & 0 deletions osl/interface_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ func (i *nwIface) Remove() error {
}
n.Unlock()

n.checkLoV6()

return nil
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
}
Expand Down
98 changes: 98 additions & 0 deletions osl/namespace_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -47,6 +51,7 @@ type networkNamespace struct {
nextIfIndex int
isDefault bool
nlHandle *netlink.Handle
loV6Enabled bool
sync.Mutex
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
49 changes: 39 additions & 10 deletions osl/sandbox_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net"
"os"
"path/filepath"
"runtime"
"strings"
"syscall"
"testing"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"},
Expand Down

0 comments on commit 947eb35

Please sign in to comment.