diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 0b391d0a..0d813926 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -45,8 +45,8 @@ }, { "ImportPath": "github.com/docker/libcontainer", - "Comment": "v1.2.0-26-g7f9256c", - "Rev": "7f9256cdc93e36fd7bc3dfec0c3737099e3c053f" + "Comment": "v1.2.0-46-g0da391f", + "Rev": "0da391f51c1586544f02c1cc8bd634444e740747" }, { "ImportPath": "github.com/fsouza/go-dockerclient", diff --git a/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go b/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go index 02cdff5a..14b55ccd 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go +++ b/Godeps/_workspace/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/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/memory.go b/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/memory.go index ea92934a..3f9647c2 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/memory.go +++ b/Godeps/_workspace/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/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/utils.go b/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/utils.go index f65622a8..f37a3a48 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/utils.go +++ b/Godeps/_workspace/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/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/utils_test.go b/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/utils_test.go index 63d743f0..f1afd494 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/utils_test.go +++ b/Godeps/_workspace/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/Godeps/_workspace/src/github.com/docker/libcontainer/config.go b/Godeps/_workspace/src/github.com/docker/libcontainer/config.go index 8fe95c24..1fb377dc 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/config.go +++ b/Godeps/_workspace/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/Godeps/_workspace/src/github.com/docker/libcontainer/container.go b/Godeps/_workspace/src/github.com/docker/libcontainer/container.go index ace997b7..307e8cbc 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/container.go +++ b/Godeps/_workspace/src/github.com/docker/libcontainer/container.go @@ -14,58 +14,65 @@ type Container interface { // 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. 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. // - // 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. // // Any event registrations are removed before the container is 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. // - // 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 // 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. // - // 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 // the execution of any user processes. Asynchronously, when the container finished being paused the // state is changed to PAUSED. // 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 // Container before setting the Container state to RUNNING. // 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 } diff --git a/Godeps/_workspace/src/github.com/docker/libcontainer/error.go b/Godeps/_workspace/src/github.com/docker/libcontainer/error.go new file mode 100644 index 00000000..5ff56d80 --- /dev/null +++ b/Godeps/_workspace/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 +} diff --git a/Godeps/_workspace/src/github.com/docker/libcontainer/factory.go b/Godeps/_workspace/src/github.com/docker/libcontainer/factory.go index f4c31160..e37773b2 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/factory.go +++ b/Godeps/_workspace/src/github.com/docker/libcontainer/factory.go @@ -12,13 +12,13 @@ type Factory interface { // Returns the new container with a running process. // // 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). - 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 // from the state. @@ -27,5 +27,6 @@ type Factory interface { // Path does not exist // Container is stopped // System error - Load(id string) (Container, error) + // TODO: fix description + Load(id string) (Container, Error) } diff --git a/Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/create.go b/Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/create.go index 15a844bc..b6418b6e 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/create.go +++ b/Godeps/_workspace/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/Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/exec.go b/Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/exec.go index 382abfbc..4440ccd0 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/exec.go +++ b/Godeps/_workspace/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/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go b/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go index 215fb178..bc130783 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go +++ b/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go @@ -3,6 +3,8 @@ package netlink import ( "encoding/binary" "fmt" + "io" + "math/rand" "net" "sync/atomic" "syscall" @@ -16,6 +18,7 @@ const ( IFLA_INFO_DATA = 2 VETH_INFO_PEER = 1 IFLA_NET_NS_FD = 28 + IFLA_ADDRESS = 1 SIOC_BRADDBR = 0x89a0 SIOC_BRDELBR = 0x89a1 SIOC_BRADDIF = 0x89a2 @@ -38,12 +41,15 @@ type ifreqFlags struct { Ifruflags uint16 } -func nativeEndian() binary.ByteOrder { +var native binary.ByteOrder + +func init() { var x uint32 = 0x01020304 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 { @@ -80,8 +86,6 @@ func newIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg { } func (msg *IfInfomsg) ToWireFormat() []byte { - native := nativeEndian() - length := syscall.SizeofIfInfomsg b := make([]byte, length) b[0] = msg.Family @@ -110,8 +114,6 @@ func newIfAddrmsg(family int) *IfAddrmsg { } func (msg *IfAddrmsg) ToWireFormat() []byte { - native := nativeEndian() - length := syscall.SizeofIfAddrmsg b := make([]byte, length) b[0] = msg.Family @@ -142,8 +144,6 @@ func newRtMsg() *RtMsg { } func (msg *RtMsg) ToWireFormat() []byte { - native := nativeEndian() - length := syscall.SizeofRtMsg b := make([]byte, length) b[0] = msg.Family @@ -202,8 +202,6 @@ func (a *RtAttr) Len() int { } func (a *RtAttr) ToWireFormat() []byte { - native := nativeEndian() - length := a.Len() buf := make([]byte, rtaAlignOf(length)) @@ -225,14 +223,18 @@ func (a *RtAttr) ToWireFormat() []byte { return buf } +func uint32Attr(t int, n uint32) *RtAttr { + buf := make([]byte, 4) + native.PutUint32(buf, n) + return newRtAttr(t, buf) +} + type NetlinkRequest struct { syscall.NlMsghdr Data []NetlinkRequestData } func (rr *NetlinkRequest) ToWireFormat() []byte { - native := nativeEndian() - length := rr.Len dataBytes := make([][]byte, len(rr.Data)) for i, data := range rr.Data { @@ -329,36 +331,44 @@ func (s *NetlinkSocket) GetPid() (uint32, error) { 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() if err != nil { return err } -done: +outer: for { msgs, err := s.Receive() if err != nil { return err } 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 } } } @@ -366,6 +376,430 @@ done: return nil } +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 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!") + } + + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + wb.AddData(msg) + + 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 + } + + 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!") + } + + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + iface, err := net.InterfaceByName(name) + if err != nil { + return err + } + + 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) +} + +// 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 { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + 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 { + return err + } + + 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 { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + 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 { + return err + } + + return s.HandleAck(wb.Seq) +} + +// 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.Index = int32(iface.Index) + msg.Change = DEFAULT_CHANGE + wb.AddData(msg) + + macdata := make([]byte, 6) + copy(macdata, hwaddr) + data := newRtAttr(IFLA_ADDRESS, macdata) + wb.AddData(data) + + if err := s.Send(wb); err != nil { + return err + } + return s.HandleAck(wb.Seq) +} + +// 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 + } + defer s.Close() + + 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))) + + if err := s.Send(wb); err != nil { + return err + } + return s.HandleAck(wb.Seq) +} + +func networkMasterAction(iface *net.Interface, rtattr *RtAttr) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + 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(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 NetworkSetNsPid(iface *net.Interface, nspid int) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + 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_NET_NS_PID, uint32(nspid))) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +func NetworkSetNsFd(iface *net.Interface, fd int) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + 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))) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +func networkLinkIpAction(action, flags int, ifa IfAddr) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + family := getIpFamily(ifa.IP) + + wb := newNetlinkRequest(action, flags) + + 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 + } + + 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) { + s, err := getNetlinkSocket() + if err != nil { + return nil, err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + wb.AddData(msg) + + if err := s.Send(wb); err != nil { + return nil, err + } + + pid, err := s.GetPid() + if err != nil { + return nil, err + } + + res := make([]Route, 0) + +outer: + for { + msgs, err := s.Receive() + if err != nil { + return nil, err + } + for _, m := range msgs { + if err := s.CheckMessage(m, wb.Seq, pid); err != nil { + if err == io.EOF { + break outer + } + return nil, err + } + if m.Header.Type != syscall.RTM_NEWROUTE { + continue + } + + var r Route + + msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0])) + + if msg.Flags&syscall.RTM_F_CLONED != 0 { + // Ignore cloned routes + continue + } + + if msg.Table != syscall.RT_TABLE_MAIN { + // Ignore non-main tables + continue + } + + if msg.Family != syscall.AF_INET { + // Ignore non-ipv4 routes + continue + } + + if msg.Dst_len == 0 { + // Default routes + r.Default = true + } + + attrs, err := syscall.ParseNetlinkRouteAttr(&m) + if err != nil { + return nil, err + } + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.RTA_DST: + ip := attr.Value + r.IPNet = &net.IPNet{ + IP: ip, + Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)), + } + case syscall.RTA_OIF: + index := int(native.Uint32(attr.Value[0:4])) + r.Iface, _ = net.InterfaceByIndex(index) + } + } + if r.Default || r.IPNet != nil { + res = append(res, r) + } + } + } + + return res, nil +} + // Add a new route table entry. func AddRoute(destination, source, gateway, device string) error { if destination == "" && source == "" && gateway == "" { @@ -454,17 +888,11 @@ func AddRoute(destination, source, gateway, device string) error { wb.AddData(attr) } - var ( - native = nativeEndian() - b = make([]byte, 4) - ) iface, err := net.InterfaceByName(device) if err != nil { 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 { return err @@ -478,427 +906,6 @@ func AddDefaultGw(ip, device string) error { return AddRoute("", "", ip, device) } -// Bring up a particular network interface -func NetworkLinkUp(iface *net.Interface) 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.Change = syscall.IFF_UP - msg.Flags = syscall.IFF_UP - msg.Index = int32(iface.Index) - wb.AddData(msg) - - if err := s.Send(wb); err != nil { - return err - } - - return s.HandleAck(wb.Seq) -} - -func NetworkLinkDown(iface *net.Interface) 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.Change = syscall.IFF_UP - msg.Flags = 0 & ^syscall.IFF_UP - msg.Index = int32(iface.Index) - wb.AddData(msg) - - if err := s.Send(wb); err != nil { - return err - } - - return s.HandleAck(wb.Seq) -} - -func NetworkSetMTU(iface *net.Interface, mtu int) error { - s, err := getNetlinkSocket() - if err != nil { - return err - } - defer s.Close() - - 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) - - var ( - b = make([]byte, 4) - native = nativeEndian() - ) - native.PutUint32(b, uint32(mtu)) - - data := newRtAttr(syscall.IFLA_MTU, b) - wb.AddData(data) - - if err := s.Send(wb); err != nil { - return err - } - return s.HandleAck(wb.Seq) -} - -// same as ip link set $name master $master -func NetworkSetMaster(iface, master *net.Interface) error { - s, err := getNetlinkSocket() - if err != nil { - return err - } - defer s.Close() - - 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) - - var ( - b = make([]byte, 4) - native = nativeEndian() - ) - native.PutUint32(b, uint32(master.Index)) - - data := newRtAttr(syscall.IFLA_MASTER, b) - wb.AddData(data) - - if err := s.Send(wb); err != nil { - return err - } - - return s.HandleAck(wb.Seq) -} - -func NetworkSetNsPid(iface *net.Interface, nspid int) error { - s, err := getNetlinkSocket() - if err != nil { - return err - } - defer s.Close() - - 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) - - var ( - b = make([]byte, 4) - native = nativeEndian() - ) - native.PutUint32(b, uint32(nspid)) - - data := newRtAttr(syscall.IFLA_NET_NS_PID, b) - wb.AddData(data) - - if err := s.Send(wb); err != nil { - return err - } - - return s.HandleAck(wb.Seq) -} - -func NetworkSetNsFd(iface *net.Interface, fd int) error { - s, err := getNetlinkSocket() - if err != nil { - return err - } - defer s.Close() - - 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) - - var ( - b = make([]byte, 4) - native = nativeEndian() - ) - native.PutUint32(b, uint32(fd)) - - data := newRtAttr(IFLA_NET_NS_FD, b) - wb.AddData(data) - - if err := s.Send(wb); err != nil { - return err - } - - return s.HandleAck(wb.Seq) -} - -func networkLinkIpAction(action, flags int, ifa IfAddr) error { - s, err := getNetlinkSocket() - if err != nil { - return err - } - defer s.Close() - - family := getIpFamily(ifa.IP) - - wb := newNetlinkRequest(action, flags) - - 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 - } - - 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}, - ) -} - -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 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!") - } - - s, err := getNetlinkSocket() - if err != nil { - return err - } - defer s.Close() - - wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) - - msg := newIfInfomsg(syscall.AF_UNSPEC) - wb.AddData(msg) - - 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 - } - - 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!") - } - - s, err := getNetlinkSocket() - if err != nil { - return err - } - defer s.Close() - - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - - 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) -} - -// 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) { - native := nativeEndian() - - s, err := getNetlinkSocket() - if err != nil { - return nil, err - } - defer s.Close() - - wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) - - msg := newIfInfomsg(syscall.AF_UNSPEC) - wb.AddData(msg) - - if err := s.Send(wb); err != nil { - return nil, err - } - - pid, err := s.GetPid() - if err != nil { - return nil, err - } - - res := make([]Route, 0) - -done: - for { - msgs, err := s.Receive() - if err != nil { - return nil, err - } - 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 - } - return nil, syscall.Errno(-error) - } - if m.Header.Type != syscall.RTM_NEWROUTE { - continue - } - - var r Route - - msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0])) - - if msg.Flags&syscall.RTM_F_CLONED != 0 { - // Ignore cloned routes - continue - } - - if msg.Table != syscall.RT_TABLE_MAIN { - // Ignore non-main tables - continue - } - - if msg.Family != syscall.AF_INET { - // Ignore non-ipv4 routes - continue - } - - if msg.Dst_len == 0 { - // Default routes - r.Default = true - } - - attrs, err := syscall.ParseNetlinkRouteAttr(&m) - if err != nil { - return nil, err - } - for _, attr := range attrs { - switch attr.Attr.Type { - case syscall.RTA_DST: - ip := attr.Value - r.IPNet = &net.IPNet{ - IP: ip, - Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)), - } - case syscall.RTA_OIF: - index := int(native.Uint32(attr.Value[0:4])) - r.Iface, _ = net.InterfaceByIndex(index) - } - } - if r.Default || r.IPNet != nil { - res = append(res, r) - } - } - } - - 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) @@ -953,6 +960,25 @@ func NetworkCreateVethPair(name1, name2 string) error { return s.HandleAck(wb.Seq) } +// 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 { @@ -974,7 +1000,7 @@ func CreateBridge(name string, setMacAddr bool) error { return err } if setMacAddr { - return setBridgeMacAddress(s, name) + return SetMacAddress(name, randMacAddr()) } return nil } @@ -1030,22 +1056,40 @@ func AddToBridge(iface, master *net.Interface) error { 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 SetMacAddress(name, addr string) error { if len(name) >= IFNAMSIZ { 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.IfruHwaddr.Family = syscall.ARPHRD_ETHER copy(ifr.IfrnName[:len(ifr.IfrnName)-1], name) 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 { return err } diff --git a/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go b/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go index 7789ae27..779e58a7 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go +++ b/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go @@ -1,9 +1,5 @@ package netlink -import ( - "math/rand" -) - -func randIfrDataByte() uint8 { - return uint8(rand.Intn(255)) +func ifrDataByte(b byte) uint8 { + return uint8(b) } diff --git a/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go b/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go index 23c4a927..f151722a 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go +++ b/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go @@ -2,10 +2,6 @@ package netlink -import ( - "math/rand" -) - -func randIfrDataByte() int8 { - return int8(rand.Intn(255)) +func ifrDataByte(b byte) int8 { + return int8(b) } diff --git a/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go b/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go index 086aee7f..642df0f6 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go +++ b/Godeps/_workspace/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,6 +60,126 @@ func ipAssigned(iface *net.Interface, ip net.IP) bool { return false } +func TestNetworkLinkAddDel(t *testing.T) { + if testing.Short() { + return + } + + testLinks := []testLink{ + {"tstEth", "dummy"}, + {"tstBr", "bridge"}, + } + + for _, tl := range testLinks { + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + readLink(t, tl.name) + } +} + +func TestNetworkLinkUpDown(t *testing.T) { + if testing.Short() { + return + } + + 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) + } + + 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) + } +} + +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 TestNetworkSetMTU(t *testing.T) { + if testing.Short() { + return + } + + tl := testLink{name: "tstEth", linkType: "dummy"} + mtu := 1400 + + 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) + } + + ifcAfterSet := readLink(t, tl.name) + + if ifcAfterSet.MTU != mtu { + t.Fatalf("Could not set %d MTU on %#v interface", mtu, tl) + } +} + +func TestNetworkSetMasterNoMaster(t *testing.T) { + if testing.Short() { + return + } + + 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) + } + + 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 TestAddDelNetworkIp(t *testing.T) { if testing.Short() { return @@ -35,7 +196,7 @@ func TestAddDelNetworkIp(t *testing.T) { } if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil { - t.Fatal(err) + t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err) } if !ipAssigned(iface, ip) { @@ -43,7 +204,7 @@ func TestAddDelNetworkIp(t *testing.T) { } if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil { - t.Fatal(err) + t.Fatalf("Could not delete IP address %s from interface %#v: %s", ip.String(), iface, err) } if ipAssigned(iface, ip) { @@ -51,6 +212,28 @@ func TestAddDelNetworkIp(t *testing.T) { } } +func TestCreateVethPair(t *testing.T) { + if testing.Short() { + return + } + + var ( + name1 = "veth1" + name2 = "veth2" + ) + + if err := NetworkCreateVethPair(name1, name2); err != nil { + t.Fatalf("Could not create veth pair %s %s: %s", name1, name2, err) + } + defer NetworkLinkDel(name1) + + readLink(t, name1) + readLink(t, name2) +} + +// +// netlink package test which do not use RTNETLINK +// func TestCreateBridgeWithMac(t *testing.T) { if testing.Short() { return @@ -77,50 +260,29 @@ func TestCreateBridgeWithMac(t *testing.T) { } } -func TestCreateBridgeLink(t *testing.T) { +func TestSetMACAddress(t *testing.T) { if testing.Short() { return } - name := "mybrlink" + name := "testmac" + mac := randMacAddr() if err := NetworkLinkAdd(name, "bridge"); err != nil { t.Fatal(err) } + defer NetworkLinkDel(name) - if _, err := net.InterfaceByName(name); err != nil { + if err := SetMacAddress(name, mac); err != nil { t.Fatal(err) } - if err := NetworkLinkDel(name); err != nil { + iface, err := net.InterfaceByName(name) + if 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 TestCreateVethPair(t *testing.T) { - if testing.Short() { - return - } - - var ( - name1 = "veth1" - name2 = "veth2" - ) - - if err := NetworkCreateVethPair(name1, name2); err != nil { - t.Fatal(err) - } - - if _, err := net.InterfaceByName(name1); err != nil { - t.Fatal(err) - } - - if _, err := net.InterfaceByName(name2); err != nil { - t.Fatal(err) + if iface.HardwareAddr.String() != mac { + t.Fatalf("mac address %q does not match %q", iface.HardwareAddr, mac) } } diff --git a/Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/exec.go b/Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/exec.go index c46b1917..6fc553b8 100644 --- a/Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/exec.go +++ b/Godeps/_workspace/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) {