1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
//go:build linux
// +build linux
package genetlink
import (
"errors"
"fmt"
"math"
"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nlenc"
"golang.org/x/sys/unix"
)
// errInvalidFamilyVersion is returned when a family's version is greater
// than an 8-bit integer.
var errInvalidFamilyVersion = errors.New("invalid family version attribute")
// getFamily retrieves a generic netlink family with the specified name.
func (c *Conn) getFamily(name string) (Family, error) {
b, err := netlink.MarshalAttributes([]netlink.Attribute{{
Type: unix.CTRL_ATTR_FAMILY_NAME,
Data: nlenc.Bytes(name),
}})
if err != nil {
return Family{}, err
}
req := Message{
Header: Header{
Command: unix.CTRL_CMD_GETFAMILY,
// TODO(mdlayher): grab nlctrl version?
Version: 1,
},
Data: b,
}
msgs, err := c.Execute(req, unix.GENL_ID_CTRL, netlink.Request)
if err != nil {
return Family{}, err
}
// TODO(mdlayher): consider interpreting generic netlink header values
families, err := buildFamilies(msgs)
if err != nil {
return Family{}, err
}
if len(families) != 1 {
// If this were to ever happen, netlink must be in a state where
// its answers cannot be trusted
panic(fmt.Sprintf("netlink returned multiple families for name: %q", name))
}
return families[0], nil
}
// listFamilies retrieves all registered generic netlink families.
func (c *Conn) listFamilies() ([]Family, error) {
req := Message{
Header: Header{
Command: unix.CTRL_CMD_GETFAMILY,
// TODO(mdlayher): grab nlctrl version?
Version: 1,
},
}
msgs, err := c.Execute(req, unix.GENL_ID_CTRL, netlink.Request|netlink.Dump)
if err != nil {
return nil, err
}
return buildFamilies(msgs)
}
// buildFamilies builds a slice of Families by parsing attributes from the
// input Messages.
func buildFamilies(msgs []Message) ([]Family, error) {
families := make([]Family, 0, len(msgs))
for _, m := range msgs {
f, err := parseFamily(m.Data)
if err != nil {
return nil, err
}
families = append(families, f)
}
return families, nil
}
// parseFamily decodes netlink attributes into a Family.
func parseFamily(b []byte) (Family, error) {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return Family{}, err
}
var f Family
for ad.Next() {
switch ad.Type() {
case unix.CTRL_ATTR_FAMILY_ID:
f.ID = ad.Uint16()
case unix.CTRL_ATTR_FAMILY_NAME:
f.Name = ad.String()
case unix.CTRL_ATTR_VERSION:
v := ad.Uint32()
if v > math.MaxUint8 {
return Family{}, errInvalidFamilyVersion
}
f.Version = uint8(v)
case unix.CTRL_ATTR_MCAST_GROUPS:
ad.Nested(parseMulticastGroups(&f.Groups))
}
}
if err := ad.Err(); err != nil {
return Family{}, err
}
return f, nil
}
// parseMulticastGroups parses an array of multicast group nested attributes
// into a slice of MulticastGroups.
func parseMulticastGroups(groups *[]MulticastGroup) func(*netlink.AttributeDecoder) error {
return func(ad *netlink.AttributeDecoder) error {
*groups = make([]MulticastGroup, 0, ad.Len())
for ad.Next() {
ad.Nested(func(nad *netlink.AttributeDecoder) error {
var g MulticastGroup
for nad.Next() {
switch nad.Type() {
case unix.CTRL_ATTR_MCAST_GRP_NAME:
g.Name = nad.String()
case unix.CTRL_ATTR_MCAST_GRP_ID:
g.ID = nad.Uint32()
}
}
*groups = append(*groups, g)
return nil
})
}
return nil
}
}
|