mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-12-30 07:40:09 +01:00
209 lines
4.7 KiB
Go
209 lines
4.7 KiB
Go
|
package genetlink
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"math"
|
||
|
|
||
|
"github.com/mdlayher/netlink"
|
||
|
"github.com/mdlayher/netlink/nlenc"
|
||
|
)
|
||
|
|
||
|
// Constants used to request information from generic netlink controller.
|
||
|
// Reference: http://lxr.free-electrons.com/source/include/linux/genetlink.h?v=3.3#L35
|
||
|
const (
|
||
|
ctrlVersion = 1
|
||
|
|
||
|
ctrlCommandGetFamily = 3
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// errInvalidFamilyVersion is returned when a family's version is greater
|
||
|
// than an 8-bit integer.
|
||
|
errInvalidFamilyVersion = errors.New("invalid family version attribute")
|
||
|
|
||
|
// errInvalidMulticastGroupArray is returned when a multicast group array
|
||
|
// of attributes is malformed.
|
||
|
errInvalidMulticastGroupArray = errors.New("invalid multicast group attribute array")
|
||
|
)
|
||
|
|
||
|
// A Family is a generic netlink family.
|
||
|
type Family struct {
|
||
|
ID uint16
|
||
|
Version uint8
|
||
|
Name string
|
||
|
Groups []MulticastGroup
|
||
|
}
|
||
|
|
||
|
// A MulticastGroup is a generic netlink multicast group, which can be joined
|
||
|
// for notifications from generic netlink families when specific events take
|
||
|
// place.
|
||
|
type MulticastGroup struct {
|
||
|
ID uint32
|
||
|
Name string
|
||
|
}
|
||
|
|
||
|
// A FamilyService is used to retrieve generic netlink family information.
|
||
|
type FamilyService struct {
|
||
|
c *Conn
|
||
|
}
|
||
|
|
||
|
// Get retrieves a generic netlink family with the specified name. If the
|
||
|
// family does not exist, the error value can be checked using os.IsNotExist.
|
||
|
func (s *FamilyService) Get(name string) (Family, error) {
|
||
|
b, err := netlink.MarshalAttributes([]netlink.Attribute{{
|
||
|
Type: attrFamilyName,
|
||
|
Data: nlenc.Bytes(name),
|
||
|
}})
|
||
|
if err != nil {
|
||
|
return Family{}, err
|
||
|
}
|
||
|
|
||
|
req := Message{
|
||
|
Header: Header{
|
||
|
Command: ctrlCommandGetFamily,
|
||
|
Version: ctrlVersion,
|
||
|
},
|
||
|
Data: b,
|
||
|
}
|
||
|
|
||
|
msgs, err := s.c.Execute(req, Controller, netlink.HeaderFlagsRequest)
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// List retrieves all registered generic netlink families.
|
||
|
func (s *FamilyService) List() ([]Family, error) {
|
||
|
req := Message{
|
||
|
Header: Header{
|
||
|
Command: ctrlCommandGetFamily,
|
||
|
Version: ctrlVersion,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
flags := netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump
|
||
|
msgs, err := s.c.Execute(req, Controller, flags)
|
||
|
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 {
|
||
|
attrs, err := netlink.UnmarshalAttributes(m.Data)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var f Family
|
||
|
if err := (&f).parseAttributes(attrs); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
families = append(families, f)
|
||
|
}
|
||
|
|
||
|
return families, nil
|
||
|
}
|
||
|
|
||
|
// Attribute IDs mapped to specific family fields.
|
||
|
const (
|
||
|
// TODO(mdlayher): parse additional attributes
|
||
|
|
||
|
// Family attributes
|
||
|
attrUnspecified = 0
|
||
|
attrFamilyID = 1
|
||
|
attrFamilyName = 2
|
||
|
attrVersion = 3
|
||
|
attrMulticastGroups = 7
|
||
|
|
||
|
// Multicast group-specific attributes
|
||
|
attrMGName = 1
|
||
|
attrMGID = 2
|
||
|
)
|
||
|
|
||
|
// parseAttributes parses netlink attributes into a Family's fields.
|
||
|
func (f *Family) parseAttributes(attrs []netlink.Attribute) error {
|
||
|
for _, a := range attrs {
|
||
|
switch a.Type {
|
||
|
case attrFamilyID:
|
||
|
f.ID = nlenc.Uint16(a.Data)
|
||
|
case attrFamilyName:
|
||
|
f.Name = nlenc.String(a.Data)
|
||
|
case attrVersion:
|
||
|
v := nlenc.Uint32(a.Data)
|
||
|
if v > math.MaxUint8 {
|
||
|
return errInvalidFamilyVersion
|
||
|
}
|
||
|
|
||
|
f.Version = uint8(v)
|
||
|
case attrMulticastGroups:
|
||
|
groups, err := parseMulticastGroups(a.Data)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
f.Groups = groups
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// parseMulticastGroups parses an array of multicast group nested attributes
|
||
|
// into a slice of MulticastGroups.
|
||
|
func parseMulticastGroups(b []byte) ([]MulticastGroup, error) {
|
||
|
attrs, err := netlink.UnmarshalAttributes(b)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
groups := make([]MulticastGroup, 0, len(attrs))
|
||
|
for i, a := range attrs {
|
||
|
// The type attribute is essentially an array index here; it starts
|
||
|
// at 1 and should increment for each new array element
|
||
|
if int(a.Type) != i+1 {
|
||
|
return nil, errInvalidMulticastGroupArray
|
||
|
}
|
||
|
|
||
|
nattrs, err := netlink.UnmarshalAttributes(a.Data)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var g MulticastGroup
|
||
|
for _, na := range nattrs {
|
||
|
switch na.Type {
|
||
|
case attrMGName:
|
||
|
g.Name = nlenc.String(na.Data)
|
||
|
case attrMGID:
|
||
|
g.ID = nlenc.Uint32(na.Data)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
groups = append(groups, g)
|
||
|
}
|
||
|
|
||
|
return groups, nil
|
||
|
}
|