2017-11-02 12:30:34 +01:00
|
|
|
//+build linux
|
|
|
|
|
2017-01-09 20:33:55 +01:00
|
|
|
package genetlink
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
|
|
|
|
"github.com/mdlayher/netlink"
|
|
|
|
"github.com/mdlayher/netlink/nlenc"
|
2017-11-02 12:30:34 +01:00
|
|
|
"golang.org/x/sys/unix"
|
2017-01-09 20:33:55 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// errInvalidFamilyVersion is returned when a family's version is greater
|
|
|
|
// than an 8-bit integer.
|
|
|
|
errInvalidFamilyVersion = errors.New("invalid family version attribute")
|
|
|
|
)
|
|
|
|
|
2017-11-02 12:30:34 +01:00
|
|
|
// getFamily retrieves a generic netlink family with the specified name.
|
|
|
|
func (c *Conn) getFamily(name string) (Family, error) {
|
2017-01-09 20:33:55 +01:00
|
|
|
b, err := netlink.MarshalAttributes([]netlink.Attribute{{
|
2017-11-02 12:30:34 +01:00
|
|
|
Type: unix.CTRL_ATTR_FAMILY_NAME,
|
2017-01-09 20:33:55 +01:00
|
|
|
Data: nlenc.Bytes(name),
|
|
|
|
}})
|
|
|
|
if err != nil {
|
|
|
|
return Family{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req := Message{
|
|
|
|
Header: Header{
|
2017-11-02 12:30:34 +01:00
|
|
|
Command: unix.CTRL_CMD_GETFAMILY,
|
|
|
|
// TODO(mdlayher): grab nlctrl version?
|
|
|
|
Version: 1,
|
2017-01-09 20:33:55 +01:00
|
|
|
},
|
|
|
|
Data: b,
|
|
|
|
}
|
|
|
|
|
2017-11-02 12:30:34 +01:00
|
|
|
msgs, err := c.Execute(req, unix.GENL_ID_CTRL, netlink.HeaderFlagsRequest)
|
2017-01-09 20:33:55 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-11-02 12:30:34 +01:00
|
|
|
// listFamilies retrieves all registered generic netlink families.
|
|
|
|
func (c *Conn) listFamilies() ([]Family, error) {
|
2017-01-09 20:33:55 +01:00
|
|
|
req := Message{
|
|
|
|
Header: Header{
|
2017-11-02 12:30:34 +01:00
|
|
|
Command: unix.CTRL_CMD_GETFAMILY,
|
|
|
|
// TODO(mdlayher): grab nlctrl version?
|
|
|
|
Version: 1,
|
2017-01-09 20:33:55 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
flags := netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump
|
2017-11-02 12:30:34 +01:00
|
|
|
msgs, err := c.Execute(req, unix.GENL_ID_CTRL, flags)
|
2017-01-09 20:33:55 +01:00
|
|
|
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 {
|
|
|
|
var f Family
|
2018-08-14 21:15:07 +02:00
|
|
|
if err := (&f).parseAttributes(m.Data); err != nil {
|
2017-01-09 20:33:55 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
families = append(families, f)
|
|
|
|
}
|
|
|
|
|
|
|
|
return families, nil
|
|
|
|
}
|
|
|
|
|
2018-08-14 21:15:07 +02:00
|
|
|
// parseAttributes decodes netlink attributes into a Family's fields.
|
|
|
|
func (f *Family) parseAttributes(b []byte) error {
|
|
|
|
ad, err := netlink.NewAttributeDecoder(b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for ad.Next() {
|
|
|
|
switch ad.Type() {
|
2017-11-02 12:30:34 +01:00
|
|
|
case unix.CTRL_ATTR_FAMILY_ID:
|
2018-08-14 21:15:07 +02:00
|
|
|
f.ID = ad.Uint16()
|
2017-11-02 12:30:34 +01:00
|
|
|
case unix.CTRL_ATTR_FAMILY_NAME:
|
2018-08-14 21:15:07 +02:00
|
|
|
f.Name = ad.String()
|
2017-11-02 12:30:34 +01:00
|
|
|
case unix.CTRL_ATTR_VERSION:
|
2018-08-14 21:15:07 +02:00
|
|
|
v := ad.Uint32()
|
2017-01-09 20:33:55 +01:00
|
|
|
if v > math.MaxUint8 {
|
|
|
|
return errInvalidFamilyVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
f.Version = uint8(v)
|
2017-11-02 12:30:34 +01:00
|
|
|
case unix.CTRL_ATTR_MCAST_GROUPS:
|
2018-08-14 21:15:07 +02:00
|
|
|
ad.Do(func(b []byte) error {
|
|
|
|
groups, err := parseMulticastGroups(b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
f.Groups = groups
|
|
|
|
return nil
|
|
|
|
})
|
2017-01-09 20:33:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-14 21:15:07 +02:00
|
|
|
return ad.Err()
|
2017-01-09 20:33:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// parseMulticastGroups parses an array of multicast group nested attributes
|
|
|
|
// into a slice of MulticastGroups.
|
|
|
|
func parseMulticastGroups(b []byte) ([]MulticastGroup, error) {
|
2018-08-14 21:15:07 +02:00
|
|
|
ad, err := netlink.NewAttributeDecoder(b)
|
2017-01-09 20:33:55 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-08-14 21:15:07 +02:00
|
|
|
var groups []MulticastGroup
|
|
|
|
for ad.Next() {
|
|
|
|
ad.Do(func(b []byte) error {
|
|
|
|
adi, err := netlink.NewAttributeDecoder(b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-01-09 20:33:55 +01:00
|
|
|
|
2018-08-14 21:15:07 +02:00
|
|
|
var g MulticastGroup
|
|
|
|
for adi.Next() {
|
|
|
|
switch adi.Type() {
|
|
|
|
case unix.CTRL_ATTR_MCAST_GRP_NAME:
|
|
|
|
g.Name = adi.String()
|
|
|
|
case unix.CTRL_ATTR_MCAST_GRP_ID:
|
|
|
|
g.ID = adi.Uint32()
|
|
|
|
}
|
|
|
|
}
|
2017-01-09 20:33:55 +01:00
|
|
|
|
2018-08-14 21:15:07 +02:00
|
|
|
if err := ad.Err(); err != nil {
|
|
|
|
return err
|
2017-01-09 20:33:55 +01:00
|
|
|
}
|
|
|
|
|
2018-08-14 21:15:07 +02:00
|
|
|
groups = append(groups, g)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := ad.Err(); err != nil {
|
|
|
|
return nil, err
|
2017-01-09 20:33:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return groups, nil
|
|
|
|
}
|