From fe2e32c2fb270b2a6e913063c1675ecba9a48926 Mon Sep 17 00:00:00 2001 From: Wataru Ishida Date: Fri, 5 May 2017 01:02:54 -0400 Subject: [PATCH] Add support for generic netlink Signed-off-by: Wataru Ishida --- genetlink_linux.go | 167 +++++++++++++++++++++++++++++++++++++++ genetlink_unspecified.go | 25 ++++++ nl/genetlink_linux.go | 64 +++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 genetlink_linux.go create mode 100644 genetlink_unspecified.go create mode 100644 nl/genetlink_linux.go diff --git a/genetlink_linux.go b/genetlink_linux.go new file mode 100644 index 0000000..a388a87 --- /dev/null +++ b/genetlink_linux.go @@ -0,0 +1,167 @@ +package netlink + +import ( + "fmt" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +type GenlOp struct { + ID uint32 + Flags uint32 +} + +type GenlMulticastGroup struct { + ID uint32 + Name string +} + +type GenlFamily struct { + ID uint16 + HdrSize uint32 + Name string + Version uint32 + MaxAttr uint32 + Ops []GenlOp + Groups []GenlMulticastGroup +} + +func parseOps(b []byte) ([]GenlOp, error) { + attrs, err := nl.ParseRouteAttr(b) + if err != nil { + return nil, err + } + ops := make([]GenlOp, 0, len(attrs)) + for _, a := range attrs { + nattrs, err := nl.ParseRouteAttr(a.Value) + if err != nil { + return nil, err + } + var op GenlOp + for _, na := range nattrs { + switch na.Attr.Type { + case nl.GENL_CTRL_ATTR_OP_ID: + op.ID = native.Uint32(na.Value) + case nl.GENL_CTRL_ATTR_OP_FLAGS: + op.Flags = native.Uint32(na.Value) + } + } + ops = append(ops, op) + } + return ops, nil +} + +func parseMulticastGroups(b []byte) ([]GenlMulticastGroup, error) { + attrs, err := nl.ParseRouteAttr(b) + if err != nil { + return nil, err + } + groups := make([]GenlMulticastGroup, 0, len(attrs)) + for _, a := range attrs { + nattrs, err := nl.ParseRouteAttr(a.Value) + if err != nil { + return nil, err + } + var g GenlMulticastGroup + for _, na := range nattrs { + switch na.Attr.Type { + case nl.GENL_CTRL_ATTR_MCAST_GRP_NAME: + g.Name = nl.BytesToString(na.Value) + case nl.GENL_CTRL_ATTR_MCAST_GRP_ID: + g.ID = native.Uint32(na.Value) + } + } + groups = append(groups, g) + } + return groups, nil +} + +func (f *GenlFamily) parseAttributes(attrs []syscall.NetlinkRouteAttr) error { + for _, a := range attrs { + switch a.Attr.Type { + case nl.GENL_CTRL_ATTR_FAMILY_NAME: + f.Name = nl.BytesToString(a.Value) + case nl.GENL_CTRL_ATTR_FAMILY_ID: + f.ID = native.Uint16(a.Value) + case nl.GENL_CTRL_ATTR_VERSION: + f.Version = native.Uint32(a.Value) + case nl.GENL_CTRL_ATTR_HDRSIZE: + f.HdrSize = native.Uint32(a.Value) + case nl.GENL_CTRL_ATTR_MAXATTR: + f.MaxAttr = native.Uint32(a.Value) + case nl.GENL_CTRL_ATTR_OPS: + ops, err := parseOps(a.Value) + if err != nil { + return err + } + f.Ops = ops + case nl.GENL_CTRL_ATTR_MCAST_GROUPS: + groups, err := parseMulticastGroups(a.Value) + if err != nil { + return err + } + f.Groups = groups + } + } + + return nil +} + +func parseFamilies(msgs [][]byte) ([]*GenlFamily, error) { + families := make([]*GenlFamily, 0, len(msgs)) + for _, m := range msgs { + attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) + if err != nil { + return nil, err + } + family := &GenlFamily{} + if err := family.parseAttributes(attrs); err != nil { + return nil, err + } + + families = append(families, family) + } + return families, nil +} + +func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) { + msg := &nl.Genlmsg{ + Command: nl.GENL_CTRL_CMD_GETFAMILY, + Version: nl.GENL_CTRL_VERSION, + } + req := h.newNetlinkRequest(nl.GENL_ID_CTRL, syscall.NLM_F_DUMP) + req.AddData(msg) + msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0) + if err != nil { + return nil, err + } + return parseFamilies(msgs) +} + +func GenlFamilyList() ([]*GenlFamily, error) { + return pkgHandle.GenlFamilyList() +} + +func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) { + msg := &nl.Genlmsg{ + Command: nl.GENL_CTRL_CMD_GETFAMILY, + Version: nl.GENL_CTRL_VERSION, + } + req := h.newNetlinkRequest(nl.GENL_ID_CTRL, 0) + req.AddData(msg) + req.AddData(nl.NewRtAttr(nl.GENL_CTRL_ATTR_FAMILY_NAME, nl.ZeroTerminated(name))) + msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0) + if err != nil { + return nil, err + } + families, err := parseFamilies(msgs) + if len(families) != 1 { + return nil, fmt.Errorf("invalid response for GENL_CTRL_CMD_GETFAMILY") + } + return families[0], nil +} + +func GenlFamilyGet(name string) (*GenlFamily, error) { + return pkgHandle.GenlFamilyGet(name) +} diff --git a/genetlink_unspecified.go b/genetlink_unspecified.go new file mode 100644 index 0000000..0192b99 --- /dev/null +++ b/genetlink_unspecified.go @@ -0,0 +1,25 @@ +// +build !linux + +package netlink + +type GenlOp struct{} + +type GenlMulticastGroup struct{} + +type GenlFamily struct{} + +func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) { + return nil, ErrNotImplemented +} + +func GenlFamilyList() ([]*GenlFamily, error) { + return nil, ErrNotImplemented +} + +func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) { + return nil, ErrNotImplemented +} + +func GenlFamilyGet(name string) (*GenlFamily, error) { + return nil, ErrNotImplemented +} diff --git a/nl/genetlink_linux.go b/nl/genetlink_linux.go new file mode 100644 index 0000000..006625c --- /dev/null +++ b/nl/genetlink_linux.go @@ -0,0 +1,64 @@ +package nl + +import ( + "unsafe" +) + +const SizeofGenlmsg = 4 + +const ( + GENL_ID_CTRL = 0x10 + GENL_CTRL_VERSION = 2 + GENL_CTRL_NAME = "nlctrl" +) + +const ( + GENL_CTRL_CMD_GETFAMILY = 3 +) + +const ( + GENL_CTRL_ATTR_UNSPEC = iota + GENL_CTRL_ATTR_FAMILY_ID + GENL_CTRL_ATTR_FAMILY_NAME + GENL_CTRL_ATTR_VERSION + GENL_CTRL_ATTR_HDRSIZE + GENL_CTRL_ATTR_MAXATTR + GENL_CTRL_ATTR_OPS + GENL_CTRL_ATTR_MCAST_GROUPS +) + +const ( + GENL_CTRL_ATTR_OP_UNSPEC = iota + GENL_CTRL_ATTR_OP_ID + GENL_CTRL_ATTR_OP_FLAGS +) + +const ( + GENL_ADMIN_PERM = 1 << iota + GENL_CMD_CAP_DO + GENL_CMD_CAP_DUMP + GENL_CMD_CAP_HASPOL +) + +const ( + GENL_CTRL_ATTR_MCAST_GRP_UNSPEC = iota + GENL_CTRL_ATTR_MCAST_GRP_NAME + GENL_CTRL_ATTR_MCAST_GRP_ID +) + +type Genlmsg struct { + Command uint8 + Version uint8 +} + +func (msg *Genlmsg) Len() int { + return SizeofGenlmsg +} + +func DeserializeGenlmsg(b []byte) *Genlmsg { + return (*Genlmsg)(unsafe.Pointer(&b[0:SizeofGenlmsg][0])) +} + +func (msg *Genlmsg) Serialize() []byte { + return (*(*[SizeofGenlmsg]byte)(unsafe.Pointer(msg)))[:] +}