-
Notifications
You must be signed in to change notification settings - Fork 2
/
client_linux.go
141 lines (118 loc) · 3.13 KB
/
client_linux.go
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
//+build linux
package devlink
import (
"github.com/mdlayher/genetlink"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
var _ osClient = &client{}
// A client is a Linux-specific devlink client.
type client struct {
c *genetlink.Conn
family genetlink.Family
}
// newClient opens a connection to the devlink family using generic netlink.
func newClient() (*client, error) {
c, err := genetlink.Dial(nil)
if err != nil {
return nil, err
}
return initClient(c)
}
// initClient is the internal client constructor used in some tests.
func initClient(c *genetlink.Conn) (*client, error) {
f, err := c.GetFamily(unix.DEVLINK_GENL_NAME)
if err != nil {
_ = c.Close()
return nil, err
}
return &client{
c: c,
family: f,
}, nil
}
// Close implements osClient.
func (c *client) Close() error { return c.c.Close() }
// Devices implements osClient.
func (c *client) Devices() ([]*Device, error) {
msgs, err := c.execute(unix.DEVLINK_CMD_GET, netlink.Dump)
if err != nil {
return nil, err
}
return parseDevices(msgs)
}
// Ports implements osClient.
func (c *client) Ports() ([]*Port, error) {
msgs, err := c.execute(unix.DEVLINK_CMD_PORT_GET, netlink.Dump)
if err != nil {
return nil, err
}
return parsePorts(msgs)
}
// execute executes the specified command with additional header flags. The
// netlink.Request header flag is automatically set.
func (c *client) execute(cmd uint8, flags netlink.HeaderFlags) ([]genetlink.Message, error) {
return c.c.Execute(
genetlink.Message{
Header: genetlink.Header{
Command: cmd,
Version: unix.DEVLINK_GENL_VERSION,
},
},
// Always pass the genetlink family ID and request flag.
c.family.ID,
netlink.Request|flags,
)
}
// parseDevices parses Devices from a slice of generic netlink messages.
func parseDevices(msgs []genetlink.Message) ([]*Device, error) {
// It appears that a Device is just a subset of the attributes found in
// a Port, so we just call the port parsing function to avoid duplication.
ports, err := parsePorts(msgs)
if err != nil {
return nil, err
}
ds := make([]*Device, 0, len(msgs))
for _, p := range ports {
ds = append(ds, &Device{
Bus: p.Bus,
Device: p.Device,
})
}
return ds, nil
}
// parsePorts parses Ports from a slice of generic netlink messages.
func parsePorts(msgs []genetlink.Message) ([]*Port, error) {
if len(msgs) == 0 {
// No devlink response found.
return nil, nil
}
ps := make([]*Port, 0, len(msgs))
for _, m := range msgs {
ad, err := netlink.NewAttributeDecoder(m.Data)
if err != nil {
return nil, err
}
var p Port
for ad.Next() {
switch ad.Type() {
case unix.DEVLINK_ATTR_BUS_NAME:
p.Bus = ad.String()
case unix.DEVLINK_ATTR_DEV_NAME:
p.Device = ad.String()
case unix.DEVLINK_ATTR_PORT_INDEX:
p.Port = int(ad.Uint32())
case unix.DEVLINK_ATTR_PORT_TYPE:
p.Type = PortType(ad.Uint16())
// Allow netdev/ibdev name to share the same "Name" field.
case unix.DEVLINK_ATTR_PORT_NETDEV_NAME, unix.DEVLINK_ATTR_PORT_IBDEV_NAME:
p.Name = ad.String()
}
}
if err := ad.Err(); err != nil {
return nil, err
}
ps = append(ps, &p)
}
return ps, nil
}