From 392eec20751bd1da7f84fa4974042eaa263a56d2 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 29 Sep 2014 21:23:42 +0000 Subject: [PATCH 1/2] Update libcontainer to c744f6470e37be5ce1f1ae09b84 Signed-off-by: Michael Crosby --- hack/vendor.sh | 2 +- .../docker/libcontainer/cgroups/fs/cpuacct.go | 2 +- .../docker/libcontainer/cgroups/fs/memory.go | 15 +- .../docker/libcontainer/cgroups/fs/utils.go | 32 +- .../libcontainer/cgroups/fs/utils_test.go | 35 +- .../github.com/docker/libcontainer/config.go | 3 + .../docker/libcontainer/namespaces/create.go | 2 +- .../docker/libcontainer/namespaces/exec.go | 8 +- .../libcontainer/netlink/netlink_linux.go | 631 +++++++++++------- .../netlink/netlink_linux_test.go | 284 ++++++-- .../docker/libcontainer/network/netns.go | 2 + .../docker/libcontainer/network/types.go | 8 +- .../docker/libcontainer/network/veth.go | 11 + .../docker/libcontainer/nsinit/exec.go | 6 +- 14 files changed, 747 insertions(+), 294 deletions(-) diff --git a/hack/vendor.sh b/hack/vendor.sh index 23c768e96a285..e4473c2ea834e 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -62,7 +62,7 @@ if [ "$1" = '--go' ]; then mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar fi -clone git github.com/docker/libcontainer 185328a42654f6dc9a41814e57882f69d65f6ab7 +clone git github.com/docker/libcontainer c744f6470e37be5ce1f1ae09b842c15c1bee120d # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file) rm -rf src/github.com/docker/libcontainer/vendor eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')" diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go index 02cdff5a7d6ab..14b55ccd4e0db 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go @@ -40,7 +40,7 @@ func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error { return err } - totalUsage, err := getCgroupParamInt(path, "cpuacct.usage") + totalUsage, err := getCgroupParamUint(path, "cpuacct.usage") if err != nil { return err } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go index ea92934a0fe32..3f9647c2fd118 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go @@ -2,6 +2,7 @@ package fs import ( "bufio" + "fmt" "os" "path/filepath" "strconv" @@ -66,25 +67,25 @@ func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error { for sc.Scan() { t, v, err := getCgroupParamKeyValue(sc.Text()) if err != nil { - return err + return fmt.Errorf("failed to parse memory.stat (%q) - %v", sc.Text(), err) } stats.MemoryStats.Stats[t] = v } // Set memory usage and max historical usage. - value, err := getCgroupParamInt(path, "memory.usage_in_bytes") + value, err := getCgroupParamUint(path, "memory.usage_in_bytes") if err != nil { - return err + return fmt.Errorf("failed to parse memory.usage_in_bytes - %v", err) } stats.MemoryStats.Usage = value - value, err = getCgroupParamInt(path, "memory.max_usage_in_bytes") + value, err = getCgroupParamUint(path, "memory.max_usage_in_bytes") if err != nil { - return err + return fmt.Errorf("failed to parse memory.max_usage_in_bytes - %v", err) } stats.MemoryStats.MaxUsage = value - value, err = getCgroupParamInt(path, "memory.failcnt") + value, err = getCgroupParamUint(path, "memory.failcnt") if err != nil { - return err + return fmt.Errorf("failed to parse memory.failcnt - %v", err) } stats.MemoryStats.Failcnt = value diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils.go index f65622a80fdf1..f37a3a485a5dc 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils.go @@ -14,27 +14,49 @@ var ( ErrNotValidFormat = errors.New("line is not a valid key value format") ) +// Saturates negative values at zero and returns a uint64. +// Due to kernel bugs, some of the memory cgroup stats can be negative. +func parseUint(s string, base, bitSize int) (uint64, error) { + value, err := strconv.ParseUint(s, base, bitSize) + if err != nil { + intValue, intErr := strconv.ParseInt(s, base, bitSize) + // 1. Handle negative values greater than MinInt64 (and) + // 2. Handle negative values lesser than MinInt64 + if intErr == nil && intValue < 0 { + return 0, nil + } else if intErr != nil && intErr.(*strconv.NumError).Err == strconv.ErrRange && intValue < 0 { + return 0, nil + } + + return value, err + } + + return value, nil +} + // Parses a cgroup param and returns as name, value // i.e. "io_service_bytes 1234" will return as io_service_bytes, 1234 func getCgroupParamKeyValue(t string) (string, uint64, error) { parts := strings.Fields(t) switch len(parts) { case 2: - value, err := strconv.ParseUint(parts[1], 10, 64) + value, err := parseUint(parts[1], 10, 64) if err != nil { - return "", 0, fmt.Errorf("Unable to convert param value to uint64: %s", err) + return "", 0, fmt.Errorf("Unable to convert param value (%q) to uint64: %v", parts[1], err) } + return parts[0], value, nil default: return "", 0, ErrNotValidFormat } } -// Gets a single int64 value from the specified cgroup file. -func getCgroupParamInt(cgroupPath, cgroupFile string) (uint64, error) { +// Gets a single uint64 value from the specified cgroup file. +func getCgroupParamUint(cgroupPath, cgroupFile string) (uint64, error) { contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile)) if err != nil { return 0, err } - return strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64) + + return parseUint(strings.TrimSpace(string(contents)), 10, 64) } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils_test.go index 63d743f06e105..f1afd494112e1 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils_test.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils_test.go @@ -2,8 +2,10 @@ package fs import ( "io/ioutil" + "math" "os" "path/filepath" + "strconv" "testing" ) @@ -27,7 +29,7 @@ func TestGetCgroupParamsInt(t *testing.T) { if err != nil { t.Fatal(err) } - value, err := getCgroupParamInt(tempDir, cgroupFile) + value, err := getCgroupParamUint(tempDir, cgroupFile) if err != nil { t.Fatal(err) } else if value != floatValue { @@ -39,19 +41,44 @@ func TestGetCgroupParamsInt(t *testing.T) { if err != nil { t.Fatal(err) } - value, err = getCgroupParamInt(tempDir, cgroupFile) + value, err = getCgroupParamUint(tempDir, cgroupFile) if err != nil { t.Fatal(err) } else if value != floatValue { t.Fatalf("Expected %d to equal %f", value, floatValue) } + // Success with negative values + err = ioutil.WriteFile(tempFile, []byte("-12345"), 0755) + if err != nil { + t.Fatal(err) + } + value, err = getCgroupParamUint(tempDir, cgroupFile) + if err != nil { + t.Fatal(err) + } else if value != 0 { + t.Fatalf("Expected %d to equal %f", value, 0) + } + + // Success with negative values lesser than min int64 + s := strconv.FormatFloat(math.MinInt64, 'f', -1, 64) + err = ioutil.WriteFile(tempFile, []byte(s), 0755) + if err != nil { + t.Fatal(err) + } + value, err = getCgroupParamUint(tempDir, cgroupFile) + if err != nil { + t.Fatal(err) + } else if value != 0 { + t.Fatalf("Expected %d to equal %f", value, 0) + } + // Not a float. err = ioutil.WriteFile(tempFile, []byte("not-a-float"), 0755) if err != nil { t.Fatal(err) } - _, err = getCgroupParamInt(tempDir, cgroupFile) + _, err = getCgroupParamUint(tempDir, cgroupFile) if err == nil { t.Fatal("Expecting error, got none") } @@ -61,7 +88,7 @@ func TestGetCgroupParamsInt(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = getCgroupParamInt(tempDir, cgroupFile) + _, err = getCgroupParamUint(tempDir, cgroupFile) if err == nil { t.Fatal("Expecting error, got none") } diff --git a/vendor/src/github.com/docker/libcontainer/config.go b/vendor/src/github.com/docker/libcontainer/config.go index 8fe95c24f7295..1fb377dcefb70 100644 --- a/vendor/src/github.com/docker/libcontainer/config.go +++ b/vendor/src/github.com/docker/libcontainer/config.go @@ -15,6 +15,9 @@ type Config struct { // Mount specific options. MountConfig *MountConfig `json:"mount_config,omitempty"` + // Pathname to container's root filesystem + RootFs string `json:"root_fs,omitempty"` + // Hostname optionally sets the container's hostname if provided Hostname string `json:"hostname,omitempty"` diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/create.go b/vendor/src/github.com/docker/libcontainer/namespaces/create.go index 15a844bc0b29b..b6418b6e9f3b4 100644 --- a/vendor/src/github.com/docker/libcontainer/namespaces/create.go +++ b/vendor/src/github.com/docker/libcontainer/namespaces/create.go @@ -7,4 +7,4 @@ import ( "github.com/docker/libcontainer" ) -type CreateCommand func(container *libcontainer.Config, console, rootfs, dataPath, init string, childPipe *os.File, args []string) *exec.Cmd +type CreateCommand func(container *libcontainer.Config, console, dataPath, init string, childPipe *os.File, args []string) *exec.Cmd diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/exec.go b/vendor/src/github.com/docker/libcontainer/namespaces/exec.go index 382abfbcccc09..4440ccd0d55ab 100644 --- a/vendor/src/github.com/docker/libcontainer/namespaces/exec.go +++ b/vendor/src/github.com/docker/libcontainer/namespaces/exec.go @@ -21,7 +21,7 @@ import ( // Move this to libcontainer package. // Exec performs setup outside of a namespace so that a container can be // executed. Exec is a high level function for working with container namespaces. -func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Writer, console string, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { +func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Writer, console, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { var ( err error ) @@ -34,7 +34,7 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri } defer syncPipe.Close() - command := createCommand(container, console, rootfs, dataPath, os.Args[0], syncPipe.Child(), args) + command := createCommand(container, console, dataPath, os.Args[0], syncPipe.Child(), args) // Note: these are only used in non-tty mode // if there is a tty for the container it will be opened within the namespace and the // fds will be duped to stdin, stdiout, and stderr @@ -121,7 +121,7 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri // root: the path to the container json file and information // pipe: sync pipe to synchronize the parent and child processes // args: the arguments to pass to the container to run as the user's program -func DefaultCreateCommand(container *libcontainer.Config, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { +func DefaultCreateCommand(container *libcontainer.Config, console, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { // get our binary name from arg0 so we can always reexec ourself env := []string{ "console=" + console, @@ -141,7 +141,7 @@ func DefaultCreateCommand(container *libcontainer.Config, console, rootfs, dataP command := exec.Command(init, append([]string{"init", "--"}, args...)...) // make sure the process is executed inside the context of the rootfs - command.Dir = rootfs + command.Dir = container.RootFs command.Env = append(os.Environ(), env...) if command.SysProcAttr == nil { diff --git a/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go b/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go index 7517a6de9e788..738af8798aa16 100644 --- a/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go +++ b/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go @@ -12,15 +12,25 @@ import ( ) const ( - IFNAMSIZ = 16 - DEFAULT_CHANGE = 0xFFFFFFFF - IFLA_INFO_KIND = 1 - IFLA_INFO_DATA = 2 - VETH_INFO_PEER = 1 - IFLA_NET_NS_FD = 28 - SIOC_BRADDBR = 0x89a0 - SIOC_BRDELBR = 0x89a1 - SIOC_BRADDIF = 0x89a2 + IFNAMSIZ = 16 + DEFAULT_CHANGE = 0xFFFFFFFF + IFLA_INFO_KIND = 1 + IFLA_INFO_DATA = 2 + VETH_INFO_PEER = 1 + IFLA_MACVLAN_MODE = 1 + IFLA_VLAN_ID = 1 + IFLA_NET_NS_FD = 28 + IFLA_ADDRESS = 1 + SIOC_BRADDBR = 0x89a0 + SIOC_BRDELBR = 0x89a1 + SIOC_BRADDIF = 0x89a2 +) + +const ( + MACVLAN_MODE_PRIVATE = 1 << iota + MACVLAN_MODE_VEPA + MACVLAN_MODE_BRIDGE + MACVLAN_MODE_PASSTHRU ) var nextSeqNr uint32 @@ -375,10 +385,19 @@ outer: return nil } -// Add a new route table entry. -func AddRoute(destination, source, gateway, device string) error { - if destination == "" && source == "" && gateway == "" { - return fmt.Errorf("one of destination, source or gateway must not be blank") +func zeroTerminated(s string) []byte { + return []byte(s + "\000") +} + +func nonZeroTerminated(s string) []byte { + return []byte(s) +} + +// Add a new network link of a specified type. +// This is identical to running: ip link add $name type $linkType +func NetworkLinkAdd(name string, linkType string) error { + if name == "" || linkType == "" { + return fmt.Errorf("Neither link name nor link type can be empty!") } s, err := getNetlinkSocket() @@ -387,101 +406,58 @@ func AddRoute(destination, source, gateway, device string) error { } defer s.Close() - wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) - msg := newRtMsg() - currentFamily := -1 - var rtAttrs []*RtAttr + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) - if destination != "" { - destIP, destNet, err := net.ParseCIDR(destination) - if err != nil { - return fmt.Errorf("destination CIDR %s couldn't be parsed", destination) - } - destFamily := getIpFamily(destIP) - currentFamily = destFamily - destLen, bits := destNet.Mask.Size() - if destLen == 0 && bits == 0 { - return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination) - } - msg.Family = uint8(destFamily) - msg.Dst_len = uint8(destLen) - var destData []byte - if destFamily == syscall.AF_INET { - destData = destIP.To4() - } else { - destData = destIP.To16() - } - rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData)) - } + msg := newIfInfomsg(syscall.AF_UNSPEC) + wb.AddData(msg) - if source != "" { - srcIP, srcNet, err := net.ParseCIDR(source) - if err != nil { - return fmt.Errorf("source CIDR %s couldn't be parsed", source) - } - srcFamily := getIpFamily(srcIP) - if currentFamily != -1 && currentFamily != srcFamily { - return fmt.Errorf("source and destination ip were not the same IP family") - } - currentFamily = srcFamily - srcLen, bits := srcNet.Mask.Size() - if srcLen == 0 && bits == 0 { - return fmt.Errorf("source CIDR %s generated a non-canonical Mask", source) - } - msg.Family = uint8(srcFamily) - msg.Src_len = uint8(srcLen) - var srcData []byte - if srcFamily == syscall.AF_INET { - srcData = srcIP.To4() - } else { - srcData = srcIP.To16() - } - rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_SRC, srcData)) + linkInfo := newRtAttr(syscall.IFLA_LINKINFO, nil) + newRtAttrChild(linkInfo, IFLA_INFO_KIND, nonZeroTerminated(linkType)) + wb.AddData(linkInfo) + + nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) + wb.AddData(nameData) + + if err := s.Send(wb); err != nil { + return err } - if gateway != "" { - gwIP := net.ParseIP(gateway) - if gwIP == nil { - return fmt.Errorf("gateway IP %s couldn't be parsed", gateway) - } - gwFamily := getIpFamily(gwIP) - if currentFamily != -1 && currentFamily != gwFamily { - return fmt.Errorf("gateway, source, and destination ip were not the same IP family") - } - msg.Family = uint8(gwFamily) - var gwData []byte - if gwFamily == syscall.AF_INET { - gwData = gwIP.To4() - } else { - gwData = gwIP.To16() - } - rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData)) + return s.HandleAck(wb.Seq) +} + +// Delete a network link. +// This is identical to running: ip link del $name +func NetworkLinkDel(name string) error { + if name == "" { + return fmt.Errorf("Network link name can not be empty!") } - wb.AddData(msg) - for _, attr := range rtAttrs { - wb.AddData(attr) + s, err := getNetlinkSocket() + if err != nil { + return err } + defer s.Close() - iface, err := net.InterfaceByName(device) + iface, err := net.InterfaceByName(name) if err != nil { return err } - wb.AddData(uint32Attr(syscall.RTA_OIF, uint32(iface.Index))) + + wb := newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(iface.Index) + wb.AddData(msg) if err := s.Send(wb); err != nil { return err } - return s.HandleAck(wb.Seq) -} -// Add a new default gateway. Identical to: -// ip route add default via $ip -func AddDefaultGw(ip, device string) error { - return AddRoute("", "", ip, device) + return s.HandleAck(wb.Seq) } -// Bring up a particular network interface +// Bring up a particular network interface. +// This is identical to running: ip link set dev $name up func NetworkLinkUp(iface *net.Interface) error { s, err := getNetlinkSocket() if err != nil { @@ -492,9 +468,9 @@ func NetworkLinkUp(iface *net.Interface) error { wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) msg := newIfInfomsg(syscall.AF_UNSPEC) - msg.Change = syscall.IFF_UP - msg.Flags = syscall.IFF_UP msg.Index = int32(iface.Index) + msg.Flags = syscall.IFF_UP + msg.Change = syscall.IFF_UP wb.AddData(msg) if err := s.Send(wb); err != nil { @@ -504,6 +480,8 @@ func NetworkLinkUp(iface *net.Interface) error { return s.HandleAck(wb.Seq) } +// Bring down a particular network interface. +// This is identical to running: ip link set $name down func NetworkLinkDown(iface *net.Interface) error { s, err := getNetlinkSocket() if err != nil { @@ -514,9 +492,9 @@ func NetworkLinkDown(iface *net.Interface) error { wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) msg := newIfInfomsg(syscall.AF_UNSPEC) - msg.Change = syscall.IFF_UP - msg.Flags = 0 & ^syscall.IFF_UP msg.Index = int32(iface.Index) + msg.Flags = 0 & ^syscall.IFF_UP + msg.Change = DEFAULT_CHANGE wb.AddData(msg) if err := s.Send(wb); err != nil { @@ -526,22 +504,40 @@ func NetworkLinkDown(iface *net.Interface) error { return s.HandleAck(wb.Seq) } -func NetworkSetMTU(iface *net.Interface, mtu int) error { +// Set link layer address ie. MAC Address. +// This is identical to running: ip link set dev $name address $macaddress +func NetworkSetMacAddress(iface *net.Interface, macaddr string) error { s, err := getNetlinkSocket() if err != nil { return err } defer s.Close() + hwaddr, err := net.ParseMAC(macaddr) + if err != nil { + return err + } + + var ( + MULTICAST byte = 0x1 + LOCALOUI byte = 0x2 + ) + + if hwaddr[0]&0x1 == MULTICAST || hwaddr[0]&0x2 != LOCALOUI { + return fmt.Errorf("Incorrect Local MAC Address specified: %s", macaddr) + } + wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) msg := newIfInfomsg(syscall.AF_UNSPEC) - msg.Type = syscall.RTM_SETLINK - msg.Flags = syscall.NLM_F_REQUEST msg.Index = int32(iface.Index) msg.Change = DEFAULT_CHANGE wb.AddData(msg) - wb.AddData(uint32Attr(syscall.IFLA_MTU, uint32(mtu))) + + macdata := make([]byte, 6) + copy(macdata, hwaddr) + data := newRtAttr(IFLA_ADDRESS, macdata) + wb.AddData(data) if err := s.Send(wb); err != nil { return err @@ -549,8 +545,13 @@ func NetworkSetMTU(iface *net.Interface, mtu int) error { return s.HandleAck(wb.Seq) } -// same as ip link set $name master $master -func NetworkSetMaster(iface, master *net.Interface) error { +// Set link Maximum Transmission Unit +// This is identical to running: ip link set dev $name mtu $MTU +// bridge is a bitch here https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=292088 +// https://bugzilla.redhat.com/show_bug.cgi?id=697021 +// There is a discussion about how to deal with ifcs joining bridge with MTU > 1500 +// Regular network nterfaces do seem to work though! +func NetworkSetMTU(iface *net.Interface, mtu int) error { s, err := getNetlinkSocket() if err != nil { return err @@ -565,16 +566,15 @@ func NetworkSetMaster(iface, master *net.Interface) error { msg.Index = int32(iface.Index) msg.Change = DEFAULT_CHANGE wb.AddData(msg) - wb.AddData(uint32Attr(syscall.IFLA_MASTER, uint32(master.Index))) + wb.AddData(uint32Attr(syscall.IFLA_MTU, uint32(mtu))) if err := s.Send(wb); err != nil { return err } - return s.HandleAck(wb.Seq) } -func NetworkSetNsPid(iface *net.Interface, nspid int) error { +func networkMasterAction(iface *net.Interface, rtattr *RtAttr) error { s, err := getNetlinkSocket() if err != nil { return err @@ -589,7 +589,41 @@ func NetworkSetNsPid(iface *net.Interface, nspid int) error { msg.Index = int32(iface.Index) msg.Change = DEFAULT_CHANGE wb.AddData(msg) - wb.AddData(uint32Attr(syscall.IFLA_NET_NS_PID, uint32(nspid))) + wb.AddData(rtattr) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +// Add an interface to bridge. +// This is identical to running: ip link set $name master $master +func NetworkSetMaster(iface, master *net.Interface) error { + data := uint32Attr(syscall.IFLA_MASTER, uint32(master.Index)) + return networkMasterAction(iface, data) +} + +// Remove an interface from the bridge +// This is is identical to to running: ip link $name set nomaster +func NetworkSetNoMaster(iface *net.Interface) error { + data := uint32Attr(syscall.IFLA_MASTER, 0) + return networkMasterAction(iface, data) +} + +func networkSetNsAction(iface *net.Interface, rtattr *RtAttr) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(iface.Index) + wb.AddData(msg) + wb.AddData(rtattr) if err := s.Send(wb); err != nil { return err @@ -598,7 +632,29 @@ func NetworkSetNsPid(iface *net.Interface, nspid int) error { return s.HandleAck(wb.Seq) } +// Move a particular network interface to a particular network namespace +// specified by PID. This is idential to running: ip link set dev $name netns $pid +func NetworkSetNsPid(iface *net.Interface, nspid int) error { + data := uint32Attr(syscall.IFLA_NET_NS_PID, uint32(nspid)) + return networkSetNsAction(iface, data) +} + +// Move a particular network interface to a particular mounted +// network namespace specified by file descriptor. +// This is idential to running: ip link set dev $name netns $fd func NetworkSetNsFd(iface *net.Interface, fd int) error { + data := uint32Attr(IFLA_NET_NS_FD, uint32(fd)) + return networkSetNsAction(iface, data) +} + +// Rname a particular interface to a different name +// !!! Note that you can't rename an active interface. You need to bring it down before renaming it. +// This is identical to running: ip link set dev ${oldName} name ${newName} +func NetworkChangeName(iface *net.Interface, newName string) error { + if len(newName) >= IFNAMSIZ { + return fmt.Errorf("Interface name %s too long", newName) + } + s, err := getNetlinkSocket() if err != nil { return err @@ -608,12 +664,12 @@ func NetworkSetNsFd(iface *net.Interface, fd int) error { wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) msg := newIfInfomsg(syscall.AF_UNSPEC) - msg.Type = syscall.RTM_SETLINK - msg.Flags = syscall.NLM_F_REQUEST msg.Index = int32(iface.Index) msg.Change = DEFAULT_CHANGE wb.AddData(msg) - wb.AddData(uint32Attr(IFLA_NET_NS_FD, uint32(fd))) + + nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(newName)) + wb.AddData(nameData) if err := s.Send(wb); err != nil { return err @@ -622,127 +678,152 @@ func NetworkSetNsFd(iface *net.Interface, fd int) error { return s.HandleAck(wb.Seq) } -func networkLinkIpAction(action, flags int, ifa IfAddr) error { +// Add a new VETH pair link on the host +// This is identical to running: ip link add name $name type veth peer name $peername +func NetworkCreateVethPair(name1, name2 string) error { s, err := getNetlinkSocket() if err != nil { return err } defer s.Close() - family := getIpFamily(ifa.IP) - - wb := newNetlinkRequest(action, flags) + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) - msg := newIfAddrmsg(family) - msg.Index = uint32(ifa.Iface.Index) - prefixLen, _ := ifa.IPNet.Mask.Size() - msg.Prefixlen = uint8(prefixLen) + msg := newIfInfomsg(syscall.AF_UNSPEC) wb.AddData(msg) - var ipData []byte - if family == syscall.AF_INET { - ipData = ifa.IP.To4() - } else { - ipData = ifa.IP.To16() - } + nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name1)) + wb.AddData(nameData) - localData := newRtAttr(syscall.IFA_LOCAL, ipData) - wb.AddData(localData) + nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) + newRtAttrChild(nest1, IFLA_INFO_KIND, zeroTerminated("veth")) + nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) + nest3 := newRtAttrChild(nest2, VETH_INFO_PEER, nil) - addrData := newRtAttr(syscall.IFA_ADDRESS, ipData) - wb.AddData(addrData) + newIfInfomsgChild(nest3, syscall.AF_UNSPEC) + newRtAttrChild(nest3, syscall.IFLA_IFNAME, zeroTerminated(name2)) + + wb.AddData(nest1) if err := s.Send(wb); err != nil { return err } - return s.HandleAck(wb.Seq) } -// Delete an IP address from an interface. This is identical to: -// ip addr del $ip/$ipNet dev $iface -func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { - return networkLinkIpAction( - syscall.RTM_DELADDR, - syscall.NLM_F_ACK, - IfAddr{iface, ip, ipNet}, - ) -} +// Add a new VLAN interface with masterDev as its upper device +// This is identical to running: +// ip link add name $name link $masterdev type vlan id $id +func NetworkLinkAddVlan(masterDev, vlanDev string, vlanId uint16) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() -// Add an Ip address to an interface. This is identical to: -// ip addr add $ip/$ipNet dev $iface -func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { - return networkLinkIpAction( - syscall.RTM_NEWADDR, - syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK, - IfAddr{iface, ip, ipNet}, - ) -} + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) -func zeroTerminated(s string) []byte { - return []byte(s + "\000") -} + masterDevIfc, err := net.InterfaceByName(masterDev) + if err != nil { + return err + } -func nonZeroTerminated(s string) []byte { - return []byte(s) -} + msg := newIfInfomsg(syscall.AF_UNSPEC) + wb.AddData(msg) -// Add a new network link of a specified type. This is identical to -// running: ip add link $name type $linkType -func NetworkLinkAdd(name string, linkType string) error { - if name == "" || linkType == "" { - return fmt.Errorf("Neither link name nor link type can be empty!") + nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) + newRtAttrChild(nest1, IFLA_INFO_KIND, nonZeroTerminated("vlan")) + + nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) + vlanData := make([]byte, 2) + native.PutUint16(vlanData, vlanId) + newRtAttrChild(nest2, IFLA_VLAN_ID, vlanData) + wb.AddData(nest1) + + wb.AddData(uint32Attr(syscall.IFLA_LINK, uint32(masterDevIfc.Index))) + wb.AddData(newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(vlanDev))) + + if err := s.Send(wb); err != nil { + return err } + return s.HandleAck(wb.Seq) +} +// Add MAC VLAN network interface with masterDev as its upper device +// This is identical to running: +// ip link add name $name link $masterdev type macvlan mode $mode +func NetworkLinkAddMacVlan(masterDev, macVlanDev string, mode string) error { s, err := getNetlinkSocket() if err != nil { return err } defer s.Close() + macVlan := map[string]uint32{ + "private": MACVLAN_MODE_PRIVATE, + "vepa": MACVLAN_MODE_VEPA, + "bridge": MACVLAN_MODE_BRIDGE, + "passthru": MACVLAN_MODE_PASSTHRU, + } + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + masterDevIfc, err := net.InterfaceByName(masterDev) + if err != nil { + return err + } + msg := newIfInfomsg(syscall.AF_UNSPEC) wb.AddData(msg) - linkInfo := newRtAttr(syscall.IFLA_LINKINFO, nil) - newRtAttrChild(linkInfo, IFLA_INFO_KIND, nonZeroTerminated(linkType)) - wb.AddData(linkInfo) + nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) + newRtAttrChild(nest1, IFLA_INFO_KIND, nonZeroTerminated("macvlan")) - nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) - wb.AddData(nameData) + nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) + macVlanData := make([]byte, 4) + native.PutUint32(macVlanData, macVlan[mode]) + newRtAttrChild(nest2, IFLA_MACVLAN_MODE, macVlanData) + wb.AddData(nest1) + + wb.AddData(uint32Attr(syscall.IFLA_LINK, uint32(masterDevIfc.Index))) + wb.AddData(newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(macVlanDev))) if err := s.Send(wb); err != nil { return err } - return s.HandleAck(wb.Seq) } -// Delete a network link. This is identical to -// running: ip link del $name -func NetworkLinkDel(name string) error { - if name == "" { - return fmt.Errorf("Network link name can not be empty!") - } - +func networkLinkIpAction(action, flags int, ifa IfAddr) error { s, err := getNetlinkSocket() if err != nil { return err } defer s.Close() - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } + family := getIpFamily(ifa.IP) - wb := newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK) + wb := newNetlinkRequest(action, flags) - msg := newIfInfomsg(syscall.AF_UNSPEC) - msg.Index = int32(iface.Index) + msg := newIfAddrmsg(family) + msg.Index = uint32(ifa.Iface.Index) + prefixLen, _ := ifa.IPNet.Mask.Size() + msg.Prefixlen = uint8(prefixLen) wb.AddData(msg) + var ipData []byte + if family == syscall.AF_INET { + ipData = ifa.IP.To4() + } else { + ipData = ifa.IP.To16() + } + + localData := newRtAttr(syscall.IFA_LOCAL, ipData) + wb.AddData(localData) + + addrData := newRtAttr(syscall.IFA_ADDRESS, ipData) + wb.AddData(addrData) + if err := s.Send(wb); err != nil { return err } @@ -750,6 +831,26 @@ func NetworkLinkDel(name string) error { return s.HandleAck(wb.Seq) } +// Delete an IP address from an interface. This is identical to: +// ip addr del $ip/$ipNet dev $iface +func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { + return networkLinkIpAction( + syscall.RTM_DELADDR, + syscall.NLM_F_ACK, + IfAddr{iface, ip, ipNet}, + ) +} + +// Add an Ip address to an interface. This is identical to: +// ip addr add $ip/$ipNet dev $iface +func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { + return networkLinkIpAction( + syscall.RTM_NEWADDR, + syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK, + IfAddr{iface, ip, ipNet}, + ) +} + // Returns an array of IPNet for all the currently routed subnets on ipv4 // This is similar to the first column of "ip route" output func NetworkGetRoutes() ([]Route, error) { @@ -842,69 +943,99 @@ outer: return res, nil } -func getIfSocket() (fd int, err error) { - for _, socket := range []int{ - syscall.AF_INET, - syscall.AF_PACKET, - syscall.AF_INET6, - } { - if fd, err = syscall.Socket(socket, syscall.SOCK_DGRAM, 0); err == nil { - break - } - } - if err == nil { - return fd, nil - } - return -1, err -} - -func NetworkChangeName(iface *net.Interface, newName string) error { - if len(newName) >= IFNAMSIZ { - return fmt.Errorf("Interface name %s too long", newName) +// Add a new route table entry. +func AddRoute(destination, source, gateway, device string) error { + if destination == "" && source == "" && gateway == "" { + return fmt.Errorf("one of destination, source or gateway must not be blank") } - fd, err := getIfSocket() + s, err := getNetlinkSocket() if err != nil { return err } - defer syscall.Close(fd) + defer s.Close() - data := [IFNAMSIZ * 2]byte{} - // the "-1"s here are very important for ensuring we get proper null - // termination of our new C strings - copy(data[:IFNAMSIZ-1], iface.Name) - copy(data[IFNAMSIZ:IFNAMSIZ*2-1], newName) + wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + msg := newRtMsg() + currentFamily := -1 + var rtAttrs []*RtAttr - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 { - return errno + if destination != "" { + destIP, destNet, err := net.ParseCIDR(destination) + if err != nil { + return fmt.Errorf("destination CIDR %s couldn't be parsed", destination) + } + destFamily := getIpFamily(destIP) + currentFamily = destFamily + destLen, bits := destNet.Mask.Size() + if destLen == 0 && bits == 0 { + return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination) + } + msg.Family = uint8(destFamily) + msg.Dst_len = uint8(destLen) + var destData []byte + if destFamily == syscall.AF_INET { + destData = destIP.To4() + } else { + destData = destIP.To16() + } + rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData)) } - return nil -} -func NetworkCreateVethPair(name1, name2 string) error { - s, err := getNetlinkSocket() - if err != nil { - return err + if source != "" { + srcIP, srcNet, err := net.ParseCIDR(source) + if err != nil { + return fmt.Errorf("source CIDR %s couldn't be parsed", source) + } + srcFamily := getIpFamily(srcIP) + if currentFamily != -1 && currentFamily != srcFamily { + return fmt.Errorf("source and destination ip were not the same IP family") + } + currentFamily = srcFamily + srcLen, bits := srcNet.Mask.Size() + if srcLen == 0 && bits == 0 { + return fmt.Errorf("source CIDR %s generated a non-canonical Mask", source) + } + msg.Family = uint8(srcFamily) + msg.Src_len = uint8(srcLen) + var srcData []byte + if srcFamily == syscall.AF_INET { + srcData = srcIP.To4() + } else { + srcData = srcIP.To16() + } + rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_SRC, srcData)) } - defer s.Close() - wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + if gateway != "" { + gwIP := net.ParseIP(gateway) + if gwIP == nil { + return fmt.Errorf("gateway IP %s couldn't be parsed", gateway) + } + gwFamily := getIpFamily(gwIP) + if currentFamily != -1 && currentFamily != gwFamily { + return fmt.Errorf("gateway, source, and destination ip were not the same IP family") + } + msg.Family = uint8(gwFamily) + var gwData []byte + if gwFamily == syscall.AF_INET { + gwData = gwIP.To4() + } else { + gwData = gwIP.To16() + } + rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData)) + } - msg := newIfInfomsg(syscall.AF_UNSPEC) wb.AddData(msg) + for _, attr := range rtAttrs { + wb.AddData(attr) + } - nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name1)) - wb.AddData(nameData) - - nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) - newRtAttrChild(nest1, IFLA_INFO_KIND, zeroTerminated("veth")) - nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) - nest3 := newRtAttrChild(nest2, VETH_INFO_PEER, nil) - - newIfInfomsgChild(nest3, syscall.AF_UNSPEC) - newRtAttrChild(nest3, syscall.IFLA_IFNAME, zeroTerminated(name2)) - - wb.AddData(nest1) + iface, err := net.InterfaceByName(device) + if err != nil { + return err + } + wb.AddData(uint32Attr(syscall.RTA_OIF, uint32(iface.Index))) if err := s.Send(wb); err != nil { return err @@ -912,6 +1043,31 @@ func NetworkCreateVethPair(name1, name2 string) error { return s.HandleAck(wb.Seq) } +// Add a new default gateway. Identical to: +// ip route add default via $ip +func AddDefaultGw(ip, device string) error { + return AddRoute("", "", ip, device) +} + +// THIS CODE DOES NOT COMMUNICATE WITH KERNEL VIA RTNETLINK INTERFACE +// IT IS HERE FOR BACKWARDS COMPATIBILITY WITH OLDER LINUX KERNELS +// WHICH SHIP WITH OLDER NOT ENTIRELY FUNCTIONAL VERSION OF NETLINK +func getIfSocket() (fd int, err error) { + for _, socket := range []int{ + syscall.AF_INET, + syscall.AF_PACKET, + syscall.AF_INET6, + } { + if fd, err = syscall.Socket(socket, syscall.SOCK_DGRAM, 0); err == nil { + break + } + } + if err == nil { + return fd, nil + } + return -1, err +} + // Create the actual bridge device. This is more backward-compatible than // netlink.NetworkLinkAdd and works on RHEL 6. func CreateBridge(name string, setMacAddr bool) error { @@ -933,7 +1089,7 @@ func CreateBridge(name string, setMacAddr bool) error { return err } if setMacAddr { - return NetworkSetMacAddress(name, randMacAddr()) + return SetMacAddress(name, randMacAddr()) } return nil } @@ -999,7 +1155,7 @@ func randMacAddr() string { return hw.String() } -func NetworkSetMacAddress(name, addr string) error { +func SetMacAddress(name, addr string) error { if len(name) >= IFNAMSIZ { return fmt.Errorf("Interface name %s too long", name) } @@ -1028,3 +1184,26 @@ func NetworkSetMacAddress(name, addr string) error { } return nil } + +func ChangeName(iface *net.Interface, newName string) error { + if len(newName) >= IFNAMSIZ { + return fmt.Errorf("Interface name %s too long", newName) + } + + fd, err := getIfSocket() + if err != nil { + return err + } + defer syscall.Close(fd) + + data := [IFNAMSIZ * 2]byte{} + // the "-1"s here are very important for ensuring we get proper null + // termination of our new C strings + copy(data[:IFNAMSIZ-1], iface.Name) + copy(data[IFNAMSIZ:IFNAMSIZ*2-1], newName) + + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 { + return errno + } + return nil +} diff --git a/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go b/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go index 6c596ed9ff98e..b2bac2eb8df2f 100644 --- a/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go +++ b/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go @@ -3,9 +3,50 @@ package netlink import ( "net" "strings" + "syscall" "testing" ) +type testLink struct { + name string + linkType string +} + +func addLink(t *testing.T, name string, linkType string) { + if err := NetworkLinkAdd(name, linkType); err != nil { + t.Fatalf("Unable to create %s link: %s", name, err) + } +} + +func readLink(t *testing.T, name string) *net.Interface { + iface, err := net.InterfaceByName(name) + if err != nil { + t.Fatalf("Could not find %s interface: %s", name, err) + } + + return iface +} + +func deleteLink(t *testing.T, name string) { + if err := NetworkLinkDel(name); err != nil { + t.Fatalf("Unable to delete %s link: %s", name, err) + } +} + +func upLink(t *testing.T, name string) { + iface := readLink(t, name) + if err := NetworkLinkUp(iface); err != nil { + t.Fatalf("Could not bring UP %#v interface: %s", iface, err) + } +} + +func downLink(t *testing.T, name string) { + iface := readLink(t, name) + if err := NetworkLinkDown(iface); err != nil { + t.Fatalf("Could not bring DOWN %#v interface: %s", iface, err) + } +} + func ipAssigned(iface *net.Interface, ip net.IP) bool { addrs, _ := iface.Addrs() @@ -19,87 +60,224 @@ func ipAssigned(iface *net.Interface, ip net.IP) bool { return false } -func TestAddDelNetworkIp(t *testing.T) { +func TestNetworkLinkAddDel(t *testing.T) { if testing.Short() { return } - ifaceName := "lo" - ip := net.ParseIP("127.0.1.1") - mask := net.IPv4Mask(255, 255, 255, 255) - ipNet := &net.IPNet{IP: ip, Mask: mask} + testLinks := []testLink{ + {"tstEth", "dummy"}, + {"tstBr", "bridge"}, + } - iface, err := net.InterfaceByName(ifaceName) - if err != nil { - t.Skip("No 'lo' interface; skipping tests") + for _, tl := range testLinks { + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + readLink(t, tl.name) } +} - if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil { - t.Fatal(err) +func TestNetworkLinkUpDown(t *testing.T) { + if testing.Short() { + return } - if !ipAssigned(iface, ip) { - t.Fatalf("Could not locate address '%s' in lo address list.", ip.String()) + tl := testLink{name: "tstEth", linkType: "dummy"} + + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + + upLink(t, tl.name) + ifcAfterUp := readLink(t, tl.name) + + if (ifcAfterUp.Flags & syscall.IFF_UP) != syscall.IFF_UP { + t.Fatalf("Could not bring UP %#v initerface", tl) } - if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil { - t.Fatal(err) + downLink(t, tl.name) + ifcAfterDown := readLink(t, tl.name) + + if (ifcAfterDown.Flags & syscall.IFF_UP) == syscall.IFF_UP { + t.Fatalf("Could not bring DOWN %#v initerface", tl) } +} - if ipAssigned(iface, ip) { - t.Fatalf("Located address '%s' in lo address list after removal.", ip.String()) +func TestNetworkSetMacAddress(t *testing.T) { + if testing.Short() { + return + } + + tl := testLink{name: "tstEth", linkType: "dummy"} + macaddr := "22:ce:e0:99:63:6f" + + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + + ifcBeforeSet := readLink(t, tl.name) + + if err := NetworkSetMacAddress(ifcBeforeSet, macaddr); err != nil { + t.Fatalf("Could not set %s MAC address on %#v interface: err", macaddr, tl, err) + } + + ifcAfterSet := readLink(t, tl.name) + + if ifcAfterSet.HardwareAddr.String() != macaddr { + t.Fatalf("Could not set %s MAC address on %#v interface", macaddr, tl) } } -func TestCreateBridgeWithMac(t *testing.T) { +func TestNetworkSetMTU(t *testing.T) { if testing.Short() { return } - name := "testbridge" + tl := testLink{name: "tstEth", linkType: "dummy"} + mtu := 1400 - if err := CreateBridge(name, true); err != nil { - t.Fatal(err) + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + + ifcBeforeSet := readLink(t, tl.name) + + if err := NetworkSetMTU(ifcBeforeSet, mtu); err != nil { + t.Fatalf("Could not set %d MTU on %#v interface: err", mtu, tl, err) } - if _, err := net.InterfaceByName(name); err != nil { - t.Fatal(err) + ifcAfterSet := readLink(t, tl.name) + + if ifcAfterSet.MTU != mtu { + t.Fatalf("Could not set %d MTU on %#v interface", mtu, tl) } +} - // cleanup and tests +func TestNetworkSetMasterNoMaster(t *testing.T) { + if testing.Short() { + return + } - if err := DeleteBridge(name); err != nil { - t.Fatal(err) + master := testLink{"tstBr", "bridge"} + slave := testLink{"tstEth", "dummy"} + testLinks := []testLink{master, slave} + + for _, tl := range testLinks { + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + upLink(t, tl.name) } - if _, err := net.InterfaceByName(name); err == nil { - t.Fatalf("expected error getting interface because %s bridge was deleted", name) + masterIfc := readLink(t, master.name) + slaveIfc := readLink(t, slave.name) + if err := NetworkSetMaster(slaveIfc, masterIfc); err != nil { + t.Fatalf("Could not set %#v to be the master of %#v: %s", master, slave, err) + } + + // Trying to figure out a way to test which will not break on RHEL6. + // We could check for existence of /sys/class/net/tstEth/upper_tstBr + // which should point to the ../tstBr which is the UPPER device i.e. network bridge + + if err := NetworkSetNoMaster(slaveIfc); err != nil { + t.Fatalf("Could not UNset %#v master of %#v: %s", master, slave, err) } } -func TestCreateBridgeLink(t *testing.T) { +func TestNetworkChangeName(t *testing.T) { if testing.Short() { return } - name := "mybrlink" + tl := testLink{"tstEth", "dummy"} + newName := "newTst" - if err := NetworkLinkAdd(name, "bridge"); err != nil { - t.Fatal(err) + addLink(t, tl.name, tl.linkType) + + linkIfc := readLink(t, tl.name) + if err := NetworkChangeName(linkIfc, newName); err != nil { + deleteLink(t, tl.name) + t.Fatalf("Could not change %#v interface name to %s: %s", tl, newName, err) } - if _, err := net.InterfaceByName(name); err != nil { - t.Fatal(err) + readLink(t, newName) + deleteLink(t, newName) +} + +func TestNetworkLinkAddVlan(t *testing.T) { + if testing.Short() { + return } - if err := NetworkLinkDel(name); err != nil { - t.Fatal(err) + tl := struct { + name string + id uint16 + }{ + name: "tstVlan", + id: 32, } + masterLink := testLink{"tstEth", "dummy"} - if _, err := net.InterfaceByName(name); err == nil { - t.Fatalf("expected error getting interface because %s bridge was deleted", name) + addLink(t, masterLink.name, masterLink.linkType) + defer deleteLink(t, masterLink.name) + + if err := NetworkLinkAddVlan(masterLink.name, tl.name, tl.id); err != nil { + t.Fatalf("Unable to create %#v VLAN interface: %s", tl, err) } + readLink(t, tl.name) +} + +func TestNetworkLinkAddMacVlan(t *testing.T) { + if testing.Short() { + return + } + + tl := struct { + name string + mode string + }{ + name: "tstVlan", + mode: "private", + } + masterLink := testLink{"tstEth", "dummy"} + + addLink(t, masterLink.name, masterLink.linkType) + defer deleteLink(t, masterLink.name) + + if err := NetworkLinkAddMacVlan(masterLink.name, tl.name, tl.mode); err != nil { + t.Fatalf("Unable to create %#v MAC VLAN interface: %s", tl, err) + } + + readLink(t, tl.name) +} + +func TestAddDelNetworkIp(t *testing.T) { + if testing.Short() { + return + } + + ifaceName := "lo" + ip := net.ParseIP("127.0.1.1") + mask := net.IPv4Mask(255, 255, 255, 255) + ipNet := &net.IPNet{IP: ip, Mask: mask} + + iface, err := net.InterfaceByName(ifaceName) + if err != nil { + t.Skip("No 'lo' interface; skipping tests") + } + + if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil { + t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err) + } + + if !ipAssigned(iface, ip) { + t.Fatalf("Could not locate address '%s' in lo address list.", ip.String()) + } + + if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil { + t.Fatalf("Could not delete IP address %s from interface %#v: %s", ip.String(), iface, err) + } + + if ipAssigned(iface, ip) { + t.Fatalf("Located address '%s' in lo address list after removal.", ip.String()) + } } func TestCreateVethPair(t *testing.T) { @@ -113,17 +291,41 @@ func TestCreateVethPair(t *testing.T) { ) if err := NetworkCreateVethPair(name1, name2); err != nil { - t.Fatal(err) + t.Fatalf("Could not create veth pair %s %s: %s", name1, name2, err) } defer NetworkLinkDel(name1) - if _, err := net.InterfaceByName(name1); err != nil { + readLink(t, name1) + readLink(t, name2) +} + +// +// netlink package tests which do not use RTNETLINK +// +func TestCreateBridgeWithMac(t *testing.T) { + if testing.Short() { + return + } + + name := "testbridge" + + if err := CreateBridge(name, true); err != nil { t.Fatal(err) } - if _, err := net.InterfaceByName(name2); err != nil { + if _, err := net.InterfaceByName(name); err != nil { t.Fatal(err) } + + // cleanup and tests + + if err := DeleteBridge(name); err != nil { + t.Fatal(err) + } + + if _, err := net.InterfaceByName(name); err == nil { + t.Fatalf("expected error getting interface because %s bridge was deleted", name) + } } func TestSetMACAddress(t *testing.T) { @@ -139,7 +341,7 @@ func TestSetMACAddress(t *testing.T) { } defer NetworkLinkDel(name) - if err := NetworkSetMacAddress(name, mac); err != nil { + if err := SetMacAddress(name, mac); err != nil { t.Fatal(err) } diff --git a/vendor/src/github.com/docker/libcontainer/network/netns.go b/vendor/src/github.com/docker/libcontainer/network/netns.go index 1ff7506452f75..73cd8de530166 100644 --- a/vendor/src/github.com/docker/libcontainer/network/netns.go +++ b/vendor/src/github.com/docker/libcontainer/network/netns.go @@ -30,8 +30,10 @@ func (v *NetNS) Initialize(config *Network, networkState *NetworkState) error { } if err := system.Setns(f.Fd(), syscall.CLONE_NEWNET); err != nil { + f.Close() return fmt.Errorf("failed to setns current network namespace: %v", err) } + f.Close() return nil } diff --git a/vendor/src/github.com/docker/libcontainer/network/types.go b/vendor/src/github.com/docker/libcontainer/network/types.go index 3b7a4e395c805..ae098981b8f82 100644 --- a/vendor/src/github.com/docker/libcontainer/network/types.go +++ b/vendor/src/github.com/docker/libcontainer/network/types.go @@ -17,12 +17,18 @@ type Network struct { // Prefix for the veth interfaces. VethPrefix string `json:"veth_prefix,omitempty"` - // Address contains the IP and mask to set on the network interface + // Address contains the IPv4 and mask to set on the network interface Address string `json:"address,omitempty"` + // IPv6Address contains the IPv6 and mask to set on the network interface + IPv6Address string `json:"ipv6_address,omitempty"` + // Gateway sets the gateway address that is used as the default for the interface Gateway string `json:"gateway,omitempty"` + // IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface + IPv6Gateway string `json:"ipv6_gateway,omitempty"` + // Mtu sets the mtu value for the interface and will be mirrored on both the host and // container's interfaces if a pair is created, specifically in the case of type veth // Note: This does not apply to loopback interfaces. diff --git a/vendor/src/github.com/docker/libcontainer/network/veth.go b/vendor/src/github.com/docker/libcontainer/network/veth.go index fcafd85ccfee3..c38fe3d2d5eeb 100644 --- a/vendor/src/github.com/docker/libcontainer/network/veth.go +++ b/vendor/src/github.com/docker/libcontainer/network/veth.go @@ -63,6 +63,12 @@ func (v *Veth) Initialize(config *Network, networkState *NetworkState) error { if err := SetInterfaceIp(defaultDevice, config.Address); err != nil { return fmt.Errorf("set %s ip %s", defaultDevice, err) } + if config.IPv6Address != "" { + if err := SetInterfaceIp(defaultDevice, config.IPv6Address); err != nil { + return fmt.Errorf("set %s ipv6 %s", defaultDevice, err) + } + } + if err := SetMtu(defaultDevice, config.Mtu); err != nil { return fmt.Errorf("set %s mtu to %d %s", defaultDevice, config.Mtu, err) } @@ -74,6 +80,11 @@ func (v *Veth) Initialize(config *Network, networkState *NetworkState) error { return fmt.Errorf("set gateway to %s on device %s failed with %s", config.Gateway, defaultDevice, err) } } + if config.IPv6Gateway != "" { + if err := SetDefaultGateway(config.IPv6Gateway, defaultDevice); err != nil { + return fmt.Errorf("set gateway for ipv6 to %s on device %s failed with %s", config.IPv6Gateway, defaultDevice, err) + } + } return nil } diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/exec.go b/vendor/src/github.com/docker/libcontainer/nsinit/exec.go index c46b19178265b..6fc553b8f93c2 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/exec.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/exec.go @@ -135,8 +135,8 @@ func startContainer(container *libcontainer.Config, dataPath string, args []stri signal.Notify(sigc) - createCommand := func(container *libcontainer.Config, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { - cmd = namespaces.DefaultCreateCommand(container, console, rootfs, dataPath, init, pipe, args) + createCommand := func(container *libcontainer.Config, console, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { + cmd = namespaces.DefaultCreateCommand(container, console, dataPath, init, pipe, args) if logPath != "" { cmd.Env = append(cmd.Env, fmt.Sprintf("log=%s", logPath)) } @@ -189,7 +189,7 @@ func startContainer(container *libcontainer.Config, dataPath string, args []stri }() } - return namespaces.Exec(container, stdin, stdout, stderr, console, "", dataPath, args, createCommand, startCallback) + return namespaces.Exec(container, stdin, stdout, stderr, console, dataPath, args, createCommand, startCallback) } func resizeTty(master *os.File) { From 532c29ef7deea38bed68506b785f067796a1836b Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 29 Sep 2014 21:35:25 +0000 Subject: [PATCH 2/2] Update native driver to set RootFs Signed-off-by: Michael Crosby --- daemon/execdriver/native/create.go | 1 + daemon/execdriver/native/driver.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index 7aee2d6cea45f..04bdf2b2ae161 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -31,6 +31,7 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e container.Cgroups.Name = c.ID container.Cgroups.AllowedDevices = c.AllowedDevices container.MountConfig.DeviceNodes = c.AutoCreatedDevices + container.RootFs = c.Rootfs // check to see if we are running in ramdisk to disable pivot root container.MountConfig.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go index 3367fad1f2860..3628d7b5751a5 100644 --- a/daemon/execdriver/native/driver.go +++ b/daemon/execdriver/native/driver.go @@ -100,7 +100,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba return -1, err } - return namespaces.Exec(container, c.ProcessConfig.Stdin, c.ProcessConfig.Stdout, c.ProcessConfig.Stderr, c.ProcessConfig.Console, c.Rootfs, dataPath, args, func(container *libcontainer.Config, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd { + return namespaces.Exec(container, c.ProcessConfig.Stdin, c.ProcessConfig.Stdout, c.ProcessConfig.Stderr, c.ProcessConfig.Console, dataPath, args, func(container *libcontainer.Config, console, dataPath, init string, child *os.File, args []string) *exec.Cmd { c.ProcessConfig.Path = d.initPath c.ProcessConfig.Args = append([]string{ DriverName, @@ -117,7 +117,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba c.ProcessConfig.ExtraFiles = []*os.File{child} c.ProcessConfig.Env = container.Env - c.ProcessConfig.Dir = c.Rootfs + c.ProcessConfig.Dir = container.RootFs return &c.ProcessConfig.Cmd }, func() {