Explorar o código

Merge pull request #8133 from crosbymichael/update-libcontainer-sep7

Update libcontainer to 185328a42654f6dc9a41814e578
Tibor Vass %!s(int64=10) %!d(string=hai) anos
pai
achega
7fafe170cf

+ 1 - 1
hack/vendor.sh

@@ -62,7 +62,7 @@ if [ "$1" = '--go' ]; then
 	mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
 	mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
 fi
 fi
 
 
-clone git github.com/docker/libcontainer 84ad9386a0240acb7475429a835d826007032bf9
+clone git github.com/docker/libcontainer 185328a42654f6dc9a41814e57882f69d65f6ab7
 # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
 # 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
 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')"
 eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"

+ 19 - 9
vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go

@@ -24,6 +24,23 @@ var (
 	CgroupProcesses = "cgroup.procs"
 	CgroupProcesses = "cgroup.procs"
 )
 )
 
 
+// The absolute path to the root of the cgroup hierarchies.
+var cgroupRoot string
+
+// TODO(vmarmol): Report error here, we'll probably need to wait for the new API.
+func init() {
+	// we can pick any subsystem to find the root
+	cpuRoot, err := cgroups.FindCgroupMountpoint("cpu")
+	if err != nil {
+		return
+	}
+	cgroupRoot = filepath.Dir(cpuRoot)
+
+	if _, err := os.Stat(cgroupRoot); err != nil {
+		return
+	}
+}
+
 type subsystem interface {
 type subsystem interface {
 	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
 	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
 	GetStats(path string, stats *cgroups.Stats) error
 	GetStats(path string, stats *cgroups.Stats) error
@@ -121,15 +138,8 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) {
 }
 }
 
 
 func getCgroupData(c *cgroups.Cgroup, pid int) (*data, error) {
 func getCgroupData(c *cgroups.Cgroup, pid int) (*data, error) {
-	// we can pick any subsystem to find the root
-	cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
-	if err != nil {
-		return nil, err
-	}
-	cgroupRoot = filepath.Dir(cgroupRoot)
-
-	if _, err := os.Stat(cgroupRoot); err != nil {
-		return nil, fmt.Errorf("cgroups fs not found")
+	if cgroupRoot == "" {
+		return nil, fmt.Errorf("failed to find the cgroup root")
 	}
 	}
 
 
 	cgroup := c.Name
 	cgroup := c.Name

+ 29 - 22
vendor/src/github.com/docker/libcontainer/container.go

@@ -14,58 +14,65 @@ type Container interface {
 
 
 	// Returns the current run state of the container.
 	// Returns the current run state of the container.
 	//
 	//
-	// Errors: container no longer exists,
-	//         system error.
-	RunState() (*RunState, error)
+	// Errors:
+	// ContainerDestroyed - Container no longer exists,
+	// SystemError - System error.
+	RunState() (*RunState, Error)
 
 
 	// Returns the current config of the container.
 	// Returns the current config of the container.
 	Config() *Config
 	Config() *Config
 
 
 	// Start a process inside the container. Returns the PID of the new process (in the caller process's namespace) and a channel that will return the exit status of the process whenever it dies.
 	// Start a process inside the container. Returns the PID of the new process (in the caller process's namespace) and a channel that will return the exit status of the process whenever it dies.
 	//
 	//
-	// Errors: container no longer exists,
-	//         config is invalid,
-	//         container is paused,
-	//         system error.
-	Start(*ProcessConfig) (pid int, exitChan chan int, err error)
+	// Errors:
+	// ContainerDestroyed - Container no longer exists,
+	// ConfigInvalid - config is invalid,
+	// ContainerPaused - Container is paused,
+	// SystemError - System error.
+	Start(config *ProcessConfig) (pid int, exitChan chan int, err Error)
 
 
 	// Destroys the container after killing all running processes.
 	// Destroys the container after killing all running processes.
 	//
 	//
 	// Any event registrations are removed before the container is destroyed.
 	// Any event registrations are removed before the container is destroyed.
 	// No error is returned if the container is already destroyed.
 	// No error is returned if the container is already destroyed.
 	//
 	//
-	// Errors: system error.
-	Destroy() error
+	// Errors:
+	// SystemError - System error.
+	Destroy() Error
 
 
 	// Returns the PIDs inside this container. The PIDs are in the namespace of the calling process.
 	// Returns the PIDs inside this container. The PIDs are in the namespace of the calling process.
 	//
 	//
-	// Errors: container no longer exists,
-	//         system error.
+	// Errors:
+	// ContainerDestroyed - Container no longer exists,
+	// SystemError - System error.
 	//
 	//
 	// Some of the returned PIDs may no longer refer to processes in the Container, unless
 	// Some of the returned PIDs may no longer refer to processes in the Container, unless
 	// the Container state is PAUSED in which case every PID in the slice is valid.
 	// the Container state is PAUSED in which case every PID in the slice is valid.
-	Processes() ([]int, error)
+	Processes() ([]int, Error)
 
 
 	// Returns statistics for the container.
 	// Returns statistics for the container.
 	//
 	//
-	// Errors: container no longer exists,
-	//         system error.
-	Stats() (*ContainerStats, error)
+	// Errors:
+	// ContainerDestroyed - Container no longer exists,
+	// SystemError - System error.
+	Stats() (*ContainerStats, Error)
 
 
 	// If the Container state is RUNNING or PAUSING, sets the Container state to PAUSING and pauses
 	// If the Container state is RUNNING or PAUSING, sets the Container state to PAUSING and pauses
 	// the execution of any user processes. Asynchronously, when the container finished being paused the
 	// the execution of any user processes. Asynchronously, when the container finished being paused the
 	// state is changed to PAUSED.
 	// state is changed to PAUSED.
 	// If the Container state is PAUSED, do nothing.
 	// If the Container state is PAUSED, do nothing.
 	//
 	//
-	// Errors: container no longer exists,
-	//         system error.
-	Pause() error
+	// Errors:
+	// ContainerDestroyed - Container no longer exists,
+	// SystemError - System error.
+	Pause() Error
 
 
 	// If the Container state is PAUSED, resumes the execution of any user processes in the
 	// If the Container state is PAUSED, resumes the execution of any user processes in the
 	// Container before setting the Container state to RUNNING.
 	// Container before setting the Container state to RUNNING.
 	// If the Container state is RUNNING, do nothing.
 	// If the Container state is RUNNING, do nothing.
 	//
 	//
-	// Errors: container no longer exists,
-	//         system error.
-	Resume() error
+	// Errors:
+	// ContainerDestroyed - Container no longer exists,
+	// SystemError - System error.
+	Resume() Error
 }
 }

+ 8 - 2
vendor/src/github.com/docker/libcontainer/devices/devices.go

@@ -17,6 +17,12 @@ var (
 	ErrNotADeviceNode = errors.New("not a device node")
 	ErrNotADeviceNode = errors.New("not a device node")
 )
 )
 
 
+// Testing dependencies
+var (
+	osLstat       = os.Lstat
+	ioutilReadDir = ioutil.ReadDir
+)
+
 type Device struct {
 type Device struct {
 	Type              rune        `json:"type,omitempty"`
 	Type              rune        `json:"type,omitempty"`
 	Path              string      `json:"path,omitempty"`               // It is fine if this is an empty string in the case that you are using Wildcards
 	Path              string      `json:"path,omitempty"`               // It is fine if this is an empty string in the case that you are using Wildcards
@@ -42,7 +48,7 @@ func (device *Device) GetCgroupAllowString() string {
 
 
 // Given the path to a device and it's cgroup_permissions(which cannot be easilly queried) look up the information about a linux device and return that information as a Device struct.
 // Given the path to a device and it's cgroup_permissions(which cannot be easilly queried) look up the information about a linux device and return that information as a Device struct.
 func GetDevice(path, cgroupPermissions string) (*Device, error) {
 func GetDevice(path, cgroupPermissions string) (*Device, error) {
-	fileInfo, err := os.Lstat(path)
+	fileInfo, err := osLstat(path)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -87,7 +93,7 @@ func GetHostDeviceNodes() ([]*Device, error) {
 }
 }
 
 
 func getDeviceNodes(path string) ([]*Device, error) {
 func getDeviceNodes(path string) ([]*Device, error) {
-	files, err := ioutil.ReadDir(path)
+	files, err := ioutilReadDir(path)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 61 - 0
vendor/src/github.com/docker/libcontainer/devices/devices_test.go

@@ -0,0 +1,61 @@
+package devices
+
+import (
+	"errors"
+	"os"
+	"testing"
+)
+
+func TestGetDeviceLstatFailure(t *testing.T) {
+	testError := errors.New("test error")
+
+	// Override os.Lstat to inject error.
+	osLstat = func(path string) (os.FileInfo, error) {
+		return nil, testError
+	}
+
+	_, err := GetDevice("", "")
+	if err != testError {
+		t.Fatalf("Unexpected error %v, expected %v", err, testError)
+	}
+}
+
+func TestGetHostDeviceNodesIoutilReadDirFailure(t *testing.T) {
+	testError := errors.New("test error")
+
+	// Override ioutil.ReadDir to inject error.
+	ioutilReadDir = func(dirname string) ([]os.FileInfo, error) {
+		return nil, testError
+	}
+
+	_, err := GetHostDeviceNodes()
+	if err != testError {
+		t.Fatalf("Unexpected error %v, expected %v", err, testError)
+	}
+}
+
+func TestGetHostDeviceNodesIoutilReadDirDeepFailure(t *testing.T) {
+	testError := errors.New("test error")
+	called := false
+
+	// Override ioutil.ReadDir to inject error after the first call.
+	ioutilReadDir = func(dirname string) ([]os.FileInfo, error) {
+		if called {
+			return nil, testError
+		}
+		called = true
+
+		// Provoke a second call.
+		fi, err := os.Lstat("/tmp")
+		if err != nil {
+			t.Fatalf("Unexpected error %v", err)
+		}
+
+		return []os.FileInfo{fi}, nil
+	}
+
+	_, err := GetHostDeviceNodes()
+	if err != testError {
+		t.Fatalf("Unexpected error %v, expected %v", err, testError)
+	}
+}

+ 37 - 0
vendor/src/github.com/docker/libcontainer/error.go

@@ -0,0 +1,37 @@
+package libcontainer
+
+// API error code type.
+type ErrorCode int
+
+// API error codes.
+const (
+	// Factory errors
+	IdInUse ErrorCode = iota
+	InvalidIdFormat
+	// TODO: add Load errors
+
+	// Container errors
+	ContainerDestroyed
+	ContainerPaused
+
+	// Common errors
+	ConfigInvalid
+	SystemError
+)
+
+// API Error type.
+type Error interface {
+	error
+
+	// Returns the stack trace, if any, which identifies the
+	// point at which the error occurred.
+	Stack() []byte
+
+	// Returns a verbose string including the error message
+	// and a representation of the stack trace suitable for
+	// printing.
+	Detail() string
+
+	// Returns the error code for this error.
+	Code() ErrorCode
+}

+ 7 - 6
vendor/src/github.com/docker/libcontainer/factory.go

@@ -12,13 +12,13 @@ type Factory interface {
 	// Returns the new container with a running process.
 	// Returns the new container with a running process.
 	//
 	//
 	// Errors:
 	// Errors:
-	// id is already in use by a container
-	// id has incorrect format
-	// config is invalid
-	// System error
+	// IdInUse - id is already in use by a container
+	// InvalidIdFormat - id has incorrect format
+	// ConfigInvalid - config is invalid
+	// SystemError - System error
 	//
 	//
 	// On error, any partially created container parts are cleaned up (the operation is atomic).
 	// On error, any partially created container parts are cleaned up (the operation is atomic).
-	Create(id string, config *Config) (Container, error)
+	Create(id string, config *Config) (Container, Error)
 
 
 	// Load takes an ID for an existing container and reconstructs the container
 	// Load takes an ID for an existing container and reconstructs the container
 	// from the state.
 	// from the state.
@@ -27,5 +27,6 @@ type Factory interface {
 	// Path does not exist
 	// Path does not exist
 	// Container is stopped
 	// Container is stopped
 	// System error
 	// System error
-	Load(id string) (Container, error)
+	// TODO: fix description
+	Load(id string) (Container, Error)
 }
 }

+ 73 - 96
vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go

@@ -3,6 +3,8 @@ package netlink
 import (
 import (
 	"encoding/binary"
 	"encoding/binary"
 	"fmt"
 	"fmt"
+	"io"
+	"math/rand"
 	"net"
 	"net"
 	"sync/atomic"
 	"sync/atomic"
 	"syscall"
 	"syscall"
@@ -38,12 +40,15 @@ type ifreqFlags struct {
 	Ifruflags uint16
 	Ifruflags uint16
 }
 }
 
 
-func nativeEndian() binary.ByteOrder {
+var native binary.ByteOrder
+
+func init() {
 	var x uint32 = 0x01020304
 	var x uint32 = 0x01020304
 	if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
 	if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
-		return binary.BigEndian
+		native = binary.BigEndian
+	} else {
+		native = binary.LittleEndian
 	}
 	}
-	return binary.LittleEndian
 }
 }
 
 
 func getIpFamily(ip net.IP) int {
 func getIpFamily(ip net.IP) int {
@@ -80,8 +85,6 @@ func newIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
 }
 }
 
 
 func (msg *IfInfomsg) ToWireFormat() []byte {
 func (msg *IfInfomsg) ToWireFormat() []byte {
-	native := nativeEndian()
-
 	length := syscall.SizeofIfInfomsg
 	length := syscall.SizeofIfInfomsg
 	b := make([]byte, length)
 	b := make([]byte, length)
 	b[0] = msg.Family
 	b[0] = msg.Family
@@ -110,8 +113,6 @@ func newIfAddrmsg(family int) *IfAddrmsg {
 }
 }
 
 
 func (msg *IfAddrmsg) ToWireFormat() []byte {
 func (msg *IfAddrmsg) ToWireFormat() []byte {
-	native := nativeEndian()
-
 	length := syscall.SizeofIfAddrmsg
 	length := syscall.SizeofIfAddrmsg
 	b := make([]byte, length)
 	b := make([]byte, length)
 	b[0] = msg.Family
 	b[0] = msg.Family
@@ -142,8 +143,6 @@ func newRtMsg() *RtMsg {
 }
 }
 
 
 func (msg *RtMsg) ToWireFormat() []byte {
 func (msg *RtMsg) ToWireFormat() []byte {
-	native := nativeEndian()
-
 	length := syscall.SizeofRtMsg
 	length := syscall.SizeofRtMsg
 	b := make([]byte, length)
 	b := make([]byte, length)
 	b[0] = msg.Family
 	b[0] = msg.Family
@@ -202,8 +201,6 @@ func (a *RtAttr) Len() int {
 }
 }
 
 
 func (a *RtAttr) ToWireFormat() []byte {
 func (a *RtAttr) ToWireFormat() []byte {
-	native := nativeEndian()
-
 	length := a.Len()
 	length := a.Len()
 	buf := make([]byte, rtaAlignOf(length))
 	buf := make([]byte, rtaAlignOf(length))
 
 
@@ -225,14 +222,18 @@ func (a *RtAttr) ToWireFormat() []byte {
 	return buf
 	return buf
 }
 }
 
 
+func uint32Attr(t int, n uint32) *RtAttr {
+	buf := make([]byte, 4)
+	native.PutUint32(buf, n)
+	return newRtAttr(t, buf)
+}
+
 type NetlinkRequest struct {
 type NetlinkRequest struct {
 	syscall.NlMsghdr
 	syscall.NlMsghdr
 	Data []NetlinkRequestData
 	Data []NetlinkRequestData
 }
 }
 
 
 func (rr *NetlinkRequest) ToWireFormat() []byte {
 func (rr *NetlinkRequest) ToWireFormat() []byte {
-	native := nativeEndian()
-
 	length := rr.Len
 	length := rr.Len
 	dataBytes := make([][]byte, len(rr.Data))
 	dataBytes := make([][]byte, len(rr.Data))
 	for i, data := range rr.Data {
 	for i, data := range rr.Data {
@@ -329,36 +330,44 @@ func (s *NetlinkSocket) GetPid() (uint32, error) {
 	return 0, ErrWrongSockType
 	return 0, ErrWrongSockType
 }
 }
 
 
-func (s *NetlinkSocket) HandleAck(seq uint32) error {
-	native := nativeEndian()
+func (s *NetlinkSocket) CheckMessage(m syscall.NetlinkMessage, seq, pid uint32) error {
+	if m.Header.Seq != seq {
+		return fmt.Errorf("netlink: invalid seq %d, expected %d", m.Header.Seq, seq)
+	}
+	if m.Header.Pid != pid {
+		return fmt.Errorf("netlink: wrong pid %d, expected %d", m.Header.Pid, pid)
+	}
+	if m.Header.Type == syscall.NLMSG_DONE {
+		return io.EOF
+	}
+	if m.Header.Type == syscall.NLMSG_ERROR {
+		e := int32(native.Uint32(m.Data[0:4]))
+		if e == 0 {
+			return io.EOF
+		}
+		return syscall.Errno(-e)
+	}
+	return nil
+}
 
 
+func (s *NetlinkSocket) HandleAck(seq uint32) error {
 	pid, err := s.GetPid()
 	pid, err := s.GetPid()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-done:
+outer:
 	for {
 	for {
 		msgs, err := s.Receive()
 		msgs, err := s.Receive()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 		for _, m := range msgs {
 		for _, m := range msgs {
-			if m.Header.Seq != seq {
-				return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
-			}
-			if m.Header.Pid != pid {
-				return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
-			}
-			if m.Header.Type == syscall.NLMSG_DONE {
-				break done
-			}
-			if m.Header.Type == syscall.NLMSG_ERROR {
-				error := int32(native.Uint32(m.Data[0:4]))
-				if error == 0 {
-					break done
+			if err := s.CheckMessage(m, seq, pid); err != nil {
+				if err == io.EOF {
+					break outer
 				}
 				}
-				return syscall.Errno(-error)
+				return err
 			}
 			}
 		}
 		}
 	}
 	}
@@ -454,17 +463,11 @@ func AddRoute(destination, source, gateway, device string) error {
 		wb.AddData(attr)
 		wb.AddData(attr)
 	}
 	}
 
 
-	var (
-		native = nativeEndian()
-		b      = make([]byte, 4)
-	)
 	iface, err := net.InterfaceByName(device)
 	iface, err := net.InterfaceByName(device)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	native.PutUint32(b, uint32(iface.Index))
-
-	wb.AddData(newRtAttr(syscall.RTA_OIF, b))
+	wb.AddData(uint32Attr(syscall.RTA_OIF, uint32(iface.Index)))
 
 
 	if err := s.Send(wb); err != nil {
 	if err := s.Send(wb); err != nil {
 		return err
 		return err
@@ -538,15 +541,7 @@ func NetworkSetMTU(iface *net.Interface, mtu int) error {
 	msg.Index = int32(iface.Index)
 	msg.Index = int32(iface.Index)
 	msg.Change = DEFAULT_CHANGE
 	msg.Change = DEFAULT_CHANGE
 	wb.AddData(msg)
 	wb.AddData(msg)
-
-	var (
-		b      = make([]byte, 4)
-		native = nativeEndian()
-	)
-	native.PutUint32(b, uint32(mtu))
-
-	data := newRtAttr(syscall.IFLA_MTU, b)
-	wb.AddData(data)
+	wb.AddData(uint32Attr(syscall.IFLA_MTU, uint32(mtu)))
 
 
 	if err := s.Send(wb); err != nil {
 	if err := s.Send(wb); err != nil {
 		return err
 		return err
@@ -570,15 +565,7 @@ func NetworkSetMaster(iface, master *net.Interface) error {
 	msg.Index = int32(iface.Index)
 	msg.Index = int32(iface.Index)
 	msg.Change = DEFAULT_CHANGE
 	msg.Change = DEFAULT_CHANGE
 	wb.AddData(msg)
 	wb.AddData(msg)
-
-	var (
-		b      = make([]byte, 4)
-		native = nativeEndian()
-	)
-	native.PutUint32(b, uint32(master.Index))
-
-	data := newRtAttr(syscall.IFLA_MASTER, b)
-	wb.AddData(data)
+	wb.AddData(uint32Attr(syscall.IFLA_MASTER, uint32(master.Index)))
 
 
 	if err := s.Send(wb); err != nil {
 	if err := s.Send(wb); err != nil {
 		return err
 		return err
@@ -602,15 +589,7 @@ func NetworkSetNsPid(iface *net.Interface, nspid int) error {
 	msg.Index = int32(iface.Index)
 	msg.Index = int32(iface.Index)
 	msg.Change = DEFAULT_CHANGE
 	msg.Change = DEFAULT_CHANGE
 	wb.AddData(msg)
 	wb.AddData(msg)
-
-	var (
-		b      = make([]byte, 4)
-		native = nativeEndian()
-	)
-	native.PutUint32(b, uint32(nspid))
-
-	data := newRtAttr(syscall.IFLA_NET_NS_PID, b)
-	wb.AddData(data)
+	wb.AddData(uint32Attr(syscall.IFLA_NET_NS_PID, uint32(nspid)))
 
 
 	if err := s.Send(wb); err != nil {
 	if err := s.Send(wb); err != nil {
 		return err
 		return err
@@ -634,15 +613,7 @@ func NetworkSetNsFd(iface *net.Interface, fd int) error {
 	msg.Index = int32(iface.Index)
 	msg.Index = int32(iface.Index)
 	msg.Change = DEFAULT_CHANGE
 	msg.Change = DEFAULT_CHANGE
 	wb.AddData(msg)
 	wb.AddData(msg)
-
-	var (
-		b      = make([]byte, 4)
-		native = nativeEndian()
-	)
-	native.PutUint32(b, uint32(fd))
-
-	data := newRtAttr(IFLA_NET_NS_FD, b)
-	wb.AddData(data)
+	wb.AddData(uint32Attr(IFLA_NET_NS_FD, uint32(fd)))
 
 
 	if err := s.Send(wb); err != nil {
 	if err := s.Send(wb); err != nil {
 		return err
 		return err
@@ -782,8 +753,6 @@ func NetworkLinkDel(name string) error {
 // Returns an array of IPNet for all the currently routed subnets on ipv4
 // Returns an array of IPNet for all the currently routed subnets on ipv4
 // This is similar to the first column of "ip route" output
 // This is similar to the first column of "ip route" output
 func NetworkGetRoutes() ([]Route, error) {
 func NetworkGetRoutes() ([]Route, error) {
-	native := nativeEndian()
-
 	s, err := getNetlinkSocket()
 	s, err := getNetlinkSocket()
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -806,28 +775,18 @@ func NetworkGetRoutes() ([]Route, error) {
 
 
 	res := make([]Route, 0)
 	res := make([]Route, 0)
 
 
-done:
+outer:
 	for {
 	for {
 		msgs, err := s.Receive()
 		msgs, err := s.Receive()
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 		for _, m := range msgs {
 		for _, m := range msgs {
-			if m.Header.Seq != wb.Seq {
-				return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
-			}
-			if m.Header.Pid != pid {
-				return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
-			}
-			if m.Header.Type == syscall.NLMSG_DONE {
-				break done
-			}
-			if m.Header.Type == syscall.NLMSG_ERROR {
-				error := int32(native.Uint32(m.Data[0:4]))
-				if error == 0 {
-					break done
+			if err := s.CheckMessage(m, wb.Seq, pid); err != nil {
+				if err == io.EOF {
+					break outer
 				}
 				}
-				return nil, syscall.Errno(-error)
+				return nil, err
 			}
 			}
 			if m.Header.Type != syscall.RTM_NEWROUTE {
 			if m.Header.Type != syscall.RTM_NEWROUTE {
 				continue
 				continue
@@ -974,7 +933,7 @@ func CreateBridge(name string, setMacAddr bool) error {
 		return err
 		return err
 	}
 	}
 	if setMacAddr {
 	if setMacAddr {
-		return setBridgeMacAddress(s, name)
+		return NetworkSetMacAddress(name, randMacAddr())
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -1030,22 +989,40 @@ func AddToBridge(iface, master *net.Interface) error {
 	return nil
 	return nil
 }
 }
 
 
-func setBridgeMacAddress(s int, name string) error {
+func randMacAddr() string {
+	hw := make(net.HardwareAddr, 6)
+	for i := 0; i < 6; i++ {
+		hw[i] = byte(rand.Intn(255))
+	}
+	hw[0] &^= 0x1 // clear multicast bit
+	hw[0] |= 0x2  // set local assignment bit (IEEE802)
+	return hw.String()
+}
+
+func NetworkSetMacAddress(name, addr string) error {
 	if len(name) >= IFNAMSIZ {
 	if len(name) >= IFNAMSIZ {
 		return fmt.Errorf("Interface name %s too long", name)
 		return fmt.Errorf("Interface name %s too long", name)
 	}
 	}
 
 
+	hw, err := net.ParseMAC(addr)
+	if err != nil {
+		return err
+	}
+
+	s, err := getIfSocket()
+	if err != nil {
+		return err
+	}
+	defer syscall.Close(s)
+
 	ifr := ifreqHwaddr{}
 	ifr := ifreqHwaddr{}
 	ifr.IfruHwaddr.Family = syscall.ARPHRD_ETHER
 	ifr.IfruHwaddr.Family = syscall.ARPHRD_ETHER
 	copy(ifr.IfrnName[:len(ifr.IfrnName)-1], name)
 	copy(ifr.IfrnName[:len(ifr.IfrnName)-1], name)
 
 
 	for i := 0; i < 6; i++ {
 	for i := 0; i < 6; i++ {
-		ifr.IfruHwaddr.Data[i] = randIfrDataByte()
+		ifr.IfruHwaddr.Data[i] = ifrDataByte(hw[i])
 	}
 	}
 
 
-	ifr.IfruHwaddr.Data[0] &^= 0x1 // clear multicast bit
-	ifr.IfruHwaddr.Data[0] |= 0x2  // set local assignment bit (IEEE802)
-
 	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), syscall.SIOCSIFHWADDR, uintptr(unsafe.Pointer(&ifr))); err != 0 {
 	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), syscall.SIOCSIFHWADDR, uintptr(unsafe.Pointer(&ifr))); err != 0 {
 		return err
 		return err
 	}
 	}

+ 2 - 6
vendor/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go

@@ -1,9 +1,5 @@
 package netlink
 package netlink
 
 
-import (
-	"math/rand"
-)
-
-func randIfrDataByte() uint8 {
-	return uint8(rand.Intn(255))
+func ifrDataByte(b byte) uint8 {
+	return uint8(b)
 }
 }

+ 2 - 6
vendor/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go

@@ -2,10 +2,6 @@
 
 
 package netlink
 package netlink
 
 
-import (
-	"math/rand"
-)
-
-func randIfrDataByte() int8 {
-	return int8(rand.Intn(255))
+func ifrDataByte(b byte) int8 {
+	return int8(b)
 }
 }

+ 28 - 0
vendor/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go

@@ -115,6 +115,7 @@ func TestCreateVethPair(t *testing.T) {
 	if err := NetworkCreateVethPair(name1, name2); err != nil {
 	if err := NetworkCreateVethPair(name1, name2); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+	defer NetworkLinkDel(name1)
 
 
 	if _, err := net.InterfaceByName(name1); err != nil {
 	if _, err := net.InterfaceByName(name1); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -124,3 +125,30 @@ func TestCreateVethPair(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 }
 }
+
+func TestSetMACAddress(t *testing.T) {
+	if testing.Short() {
+		return
+	}
+
+	name := "testmac"
+	mac := randMacAddr()
+
+	if err := NetworkLinkAdd(name, "bridge"); err != nil {
+		t.Fatal(err)
+	}
+	defer NetworkLinkDel(name)
+
+	if err := NetworkSetMacAddress(name, mac); err != nil {
+		t.Fatal(err)
+	}
+
+	iface, err := net.InterfaceByName(name)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if iface.HardwareAddr.String() != mac {
+		t.Fatalf("mac address %q does not match %q", iface.HardwareAddr, mac)
+	}
+}