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:
parent
afcec80137
commit
f195563a4e
5 changed files with 149 additions and 11 deletions
|
@ -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()
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"},
|
||||
|
|
Loading…
Reference in a new issue