1package netlink
2
3import (
4	"fmt"
5	"syscall"
6
7	"github.com/vishvananda/netlink/nl"
8	"golang.org/x/sys/unix"
9)
10
11type GenlOp struct {
12	ID    uint32
13	Flags uint32
14}
15
16type GenlMulticastGroup struct {
17	ID   uint32
18	Name string
19}
20
21type GenlFamily struct {
22	ID      uint16
23	HdrSize uint32
24	Name    string
25	Version uint32
26	MaxAttr uint32
27	Ops     []GenlOp
28	Groups  []GenlMulticastGroup
29}
30
31func parseOps(b []byte) ([]GenlOp, error) {
32	attrs, err := nl.ParseRouteAttr(b)
33	if err != nil {
34		return nil, err
35	}
36	ops := make([]GenlOp, 0, len(attrs))
37	for _, a := range attrs {
38		nattrs, err := nl.ParseRouteAttr(a.Value)
39		if err != nil {
40			return nil, err
41		}
42		var op GenlOp
43		for _, na := range nattrs {
44			switch na.Attr.Type {
45			case nl.GENL_CTRL_ATTR_OP_ID:
46				op.ID = native.Uint32(na.Value)
47			case nl.GENL_CTRL_ATTR_OP_FLAGS:
48				op.Flags = native.Uint32(na.Value)
49			}
50		}
51		ops = append(ops, op)
52	}
53	return ops, nil
54}
55
56func parseMulticastGroups(b []byte) ([]GenlMulticastGroup, error) {
57	attrs, err := nl.ParseRouteAttr(b)
58	if err != nil {
59		return nil, err
60	}
61	groups := make([]GenlMulticastGroup, 0, len(attrs))
62	for _, a := range attrs {
63		nattrs, err := nl.ParseRouteAttr(a.Value)
64		if err != nil {
65			return nil, err
66		}
67		var g GenlMulticastGroup
68		for _, na := range nattrs {
69			switch na.Attr.Type {
70			case nl.GENL_CTRL_ATTR_MCAST_GRP_NAME:
71				g.Name = nl.BytesToString(na.Value)
72			case nl.GENL_CTRL_ATTR_MCAST_GRP_ID:
73				g.ID = native.Uint32(na.Value)
74			}
75		}
76		groups = append(groups, g)
77	}
78	return groups, nil
79}
80
81func (f *GenlFamily) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
82	for _, a := range attrs {
83		switch a.Attr.Type {
84		case nl.GENL_CTRL_ATTR_FAMILY_NAME:
85			f.Name = nl.BytesToString(a.Value)
86		case nl.GENL_CTRL_ATTR_FAMILY_ID:
87			f.ID = native.Uint16(a.Value)
88		case nl.GENL_CTRL_ATTR_VERSION:
89			f.Version = native.Uint32(a.Value)
90		case nl.GENL_CTRL_ATTR_HDRSIZE:
91			f.HdrSize = native.Uint32(a.Value)
92		case nl.GENL_CTRL_ATTR_MAXATTR:
93			f.MaxAttr = native.Uint32(a.Value)
94		case nl.GENL_CTRL_ATTR_OPS:
95			ops, err := parseOps(a.Value)
96			if err != nil {
97				return err
98			}
99			f.Ops = ops
100		case nl.GENL_CTRL_ATTR_MCAST_GROUPS:
101			groups, err := parseMulticastGroups(a.Value)
102			if err != nil {
103				return err
104			}
105			f.Groups = groups
106		}
107	}
108
109	return nil
110}
111
112func parseFamilies(msgs [][]byte) ([]*GenlFamily, error) {
113	families := make([]*GenlFamily, 0, len(msgs))
114	for _, m := range msgs {
115		attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
116		if err != nil {
117			return nil, err
118		}
119		family := &GenlFamily{}
120		if err := family.parseAttributes(attrs); err != nil {
121			return nil, err
122		}
123
124		families = append(families, family)
125	}
126	return families, nil
127}
128
129func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
130	msg := &nl.Genlmsg{
131		Command: nl.GENL_CTRL_CMD_GETFAMILY,
132		Version: nl.GENL_CTRL_VERSION,
133	}
134	req := h.newNetlinkRequest(nl.GENL_ID_CTRL, unix.NLM_F_DUMP)
135	req.AddData(msg)
136	msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
137	if err != nil {
138		return nil, err
139	}
140	return parseFamilies(msgs)
141}
142
143func GenlFamilyList() ([]*GenlFamily, error) {
144	return pkgHandle.GenlFamilyList()
145}
146
147func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) {
148	msg := &nl.Genlmsg{
149		Command: nl.GENL_CTRL_CMD_GETFAMILY,
150		Version: nl.GENL_CTRL_VERSION,
151	}
152	req := h.newNetlinkRequest(nl.GENL_ID_CTRL, 0)
153	req.AddData(msg)
154	req.AddData(nl.NewRtAttr(nl.GENL_CTRL_ATTR_FAMILY_NAME, nl.ZeroTerminated(name)))
155	msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
156	if err != nil {
157		return nil, err
158	}
159	families, err := parseFamilies(msgs)
160	if err != nil {
161		return nil, err
162	}
163	if len(families) != 1 {
164		return nil, fmt.Errorf("invalid response for GENL_CTRL_CMD_GETFAMILY")
165	}
166	return families[0], nil
167}
168
169func GenlFamilyGet(name string) (*GenlFamily, error) {
170	return pkgHandle.GenlFamilyGet(name)
171}
172