diff --git a/handle_unspecified.go b/handle_unspecified.go index 915b765..7f2fd3f 100644 --- a/handle_unspecified.go +++ b/handle_unspecified.go @@ -149,6 +149,10 @@ func (h *Handle) LinkSetTxQLen(link Link, qlen int) error { return ErrNotImplemented } +func (h *Handle) LinkSetGroup(link Link, group int) error { + return ErrNotImplemented +} + func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error { return ErrNotImplemented } diff --git a/link.go b/link.go index 0afc69c..3542288 100644 --- a/link.go +++ b/link.go @@ -44,6 +44,7 @@ type LinkAttrs struct { GSOMaxSize uint32 GSOMaxSegs uint32 Vfs []VfInfo // virtual functions available on link + Group uint32 } // VfInfo represents configuration of virtual function diff --git a/link_linux.go b/link_linux.go index 63e4aeb..e4e8501 100644 --- a/link_linux.go +++ b/link_linux.go @@ -1130,6 +1130,11 @@ func (h *Handle) linkModify(link Link, flags int) error { req.AddData(gsoAttr) } + if base.Group > 0 { + groupAttr := nl.NewRtAttr(unix.IFLA_GROUP, nl.Uint32Attr(base.Group)) + req.AddData(groupAttr) + } + if base.Namespace != nil { var attr *nl.RtAttr switch ns := base.Namespace.(type) { @@ -1577,6 +1582,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) { base.NumTxQueues = int(native.Uint32(attr.Value[0:4])) case unix.IFLA_NUM_RX_QUEUES: base.NumRxQueues = int(native.Uint32(attr.Value[0:4])) + case unix.IFLA_GROUP: + base.Group = native.Uint32(attr.Value[0:4]) } } @@ -1888,6 +1895,35 @@ func (h *Handle) LinkSetTxQLen(link Link, qlen int) error { return err } +// LinkSetGroup sets the link group id which can be used to perform mass actions +// with iproute2 as well use it as a reference in nft filters. +// Equivalent to: `ip link set $link group $id` +func LinkSetGroup(link Link, group int) error { + return pkgHandle.LinkSetGroup(link, group) +} + +// LinkSetGroup sets the link group id which can be used to perform mass actions +// with iproute2 as well use it as a reference in nft filters. +// Equivalent to: `ip link set $link group $id` +func (h *Handle) LinkSetGroup(link Link, group int) error { + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) + + msg := nl.NewIfInfomsg(unix.AF_UNSPEC) + msg.Index = int32(base.Index) + req.AddData(msg) + + b := make([]byte, 4) + native.PutUint32(b, uint32(group)) + + data := nl.NewRtAttr(unix.IFLA_GROUP, b) + req.AddData(data) + + _, err := req.Execute(unix.NETLINK_ROUTE, 0) + return err +} + func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) { vlan := link.(*Vlan) for _, datum := range data { diff --git a/link_test.go b/link_test.go index 2291b15..02a5f0e 100644 --- a/link_test.go +++ b/link_test.go @@ -47,6 +47,12 @@ func testLinkAddDel(t *testing.T, link Link) { } } + if base.Group > 0 { + if base.Group != rBase.Group { + t.Fatalf("group is %d, should be %d", rBase.Group, base.Group) + } + } + if vlan, ok := link.(*Vlan); ok { other, ok := result.(*Vlan) if !ok { @@ -476,6 +482,13 @@ func TestLinkAddDelDummy(t *testing.T) { testLinkAddDel(t, &Dummy{LinkAttrs{Name: "foo"}}) } +func TestLinkAddDelDummyWithGroup(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Dummy{LinkAttrs{Name: "foo", Group: 42}}) +} + func TestLinkAddDelIfb(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() @@ -1236,6 +1249,20 @@ func TestLinkSet(t *testing.T) { if err != nil { t.Fatal(err) } + + err = LinkSetGroup(link, 42) + if err != nil { + t.Fatalf("Could not set group: %v", err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().Group != 42 { + t.Fatal("Link group not changed") + } } func TestLinkSetARP(t *testing.T) {