Merge pull request #968 from aboch/ed6

Control IPv6 on container's interface
This commit is contained in:
Santhosh Manohar 2016-12-22 18:15:15 -08:00 committed by GitHub
commit 176088a742
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"},