diff --git a/netlink/netlink_linux.go b/netlink/netlink_linux.go index 6b43fdf06..5fcc817af 100644 --- a/netlink/netlink_linux.go +++ b/netlink/netlink_linux.go @@ -189,13 +189,15 @@ func newRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr { } func (a *RtAttr) Len() int { + if len(a.children) == 0 { + return (syscall.SizeofRtAttr + len(a.Data)) + } + l := 0 for _, child := range a.children { - l += child.Len() + syscall.SizeofRtAttr - } - if l == 0 { - l++ + l += child.Len() } + l += syscall.SizeofRtAttr return rtaAlignOf(l + len(a.Data)) } @@ -203,7 +205,7 @@ func (a *RtAttr) ToWireFormat() []byte { native := nativeEndian() length := a.Len() - buf := make([]byte, rtaAlignOf(length+syscall.SizeofRtAttr)) + buf := make([]byte, rtaAlignOf(length)) if a.Data != nil { copy(buf[4:], a.Data) @@ -216,11 +218,10 @@ func (a *RtAttr) ToWireFormat() []byte { } } - if l := uint16(rtaAlignOf(length)); l != 0 { - native.PutUint16(buf[0:2], l+1) + if l := uint16(length); l != 0 { + native.PutUint16(buf[0:2], l) } native.PutUint16(buf[2:4], a.Type) - return buf } @@ -700,6 +701,10 @@ func nonZeroTerminated(s string) []byte { // 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 @@ -711,15 +716,43 @@ func NetworkLinkAdd(name string, linkType string) error { msg := newIfInfomsg(syscall.AF_UNSPEC) wb.AddData(msg) - if name != "" { - nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) - wb.AddData(nameData) + 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 } - kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType)) + return s.HandleAck(wb.Seq) +} - infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat()) - wb.AddData(infoData) +// 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 diff --git a/netlink/netlink_linux_test.go b/netlink/netlink_linux_test.go index ee61d5e26..a25f28613 100644 --- a/netlink/netlink_linux_test.go +++ b/netlink/netlink_linux_test.go @@ -27,10 +27,35 @@ func TestCreateBridgeWithMac(t *testing.T) { } if _, err := net.InterfaceByName(name); err == nil { - t.Fatal("expected error getting interface because bridge was deleted") + t.Fatalf("expected error getting interface because %s bridge was deleted", name) } } +func TestCreateBridgeLink(t *testing.T) { + if testing.Short() { + return + } + + name := "mybrlink" + + if err := NetworkLinkAdd(name, "bridge"); err != nil { + t.Fatal(err) + } + + if _, err := net.InterfaceByName(name); err != nil { + t.Fatal(err) + } + + if err := NetworkLinkDel(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 TestCreateVethPair(t *testing.T) { if testing.Short() { return diff --git a/netlink/netlink_unsupported.go b/netlink/netlink_unsupported.go index f428933c1..783e68cae 100644 --- a/netlink/netlink_unsupported.go +++ b/netlink/netlink_unsupported.go @@ -19,6 +19,10 @@ func NetworkLinkAdd(name string, linkType string) error { return ErrNotImplemented } +func NetworkLinkDel(name string) error { + return ErrNotImplemented +} + func NetworkLinkUp(iface *net.Interface) error { return ErrNotImplemented }