forked from jsimonetti/rtnetlink
-
Notifications
You must be signed in to change notification settings - Fork 0
/
conn.go
197 lines (166 loc) · 5.12 KB
/
conn.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
package rtnetlink
import (
"encoding"
"time"
"github.com/jsimonetti/rtnetlink/internal/unix"
"github.com/mdlayher/netlink"
)
// A Conn is a route netlink connection. A Conn can be used to send and
// receive route netlink messages to and from netlink.
type Conn struct {
c conn
Link *LinkService
Address *AddressService
Route *RouteService
Neigh *NeighService
Rule *RuleService
}
var _ conn = &netlink.Conn{}
// A conn is a netlink connection, which can be swapped for tests.
type conn interface {
Close() error
Send(m netlink.Message) (netlink.Message, error)
Receive() ([]netlink.Message, error)
Execute(m netlink.Message) ([]netlink.Message, error)
SetOption(option netlink.ConnOption, enable bool) error
SetReadDeadline(t time.Time) error
}
// Dial dials a route netlink connection. Config specifies optional
// configuration for the underlying netlink connection. If config is
// nil, a default configuration will be used.
func Dial(config *netlink.Config) (*Conn, error) {
c, err := netlink.Dial(unix.NETLINK_ROUTE, config)
if err != nil {
return nil, err
}
return newConn(c), nil
}
// newConn creates a Conn that wraps an existing *netlink.Conn for
// rtnetlink communications. It is used for testing.
func newConn(c conn) *Conn {
rtc := &Conn{
c: c,
}
rtc.Link = &LinkService{c: rtc}
rtc.Address = &AddressService{c: rtc}
rtc.Route = &RouteService{c: rtc}
rtc.Neigh = &NeighService{c: rtc}
rtc.Rule = &RuleService{c: rtc}
return rtc
}
// Close closes the connection.
func (c *Conn) Close() error {
return c.c.Close()
}
// SetOption enables or disables a netlink socket option for the Conn.
func (c *Conn) SetOption(option netlink.ConnOption, enable bool) error {
return c.c.SetOption(option, enable)
}
// SetReadDeadline sets the read deadline associated with the connection.
func (c *Conn) SetReadDeadline(t time.Time) error {
return c.c.SetReadDeadline(t)
}
// Send sends a single Message to netlink, wrapping it in a netlink.Message
// using the specified generic netlink family and flags. On success, Send
// returns a copy of the netlink.Message with all parameters populated, for
// later validation.
func (c *Conn) Send(m Message, family uint16, flags netlink.HeaderFlags) (netlink.Message, error) {
nm := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType(family),
Flags: flags,
},
}
mb, err := m.MarshalBinary()
if err != nil {
return netlink.Message{}, err
}
nm.Data = mb
reqnm, err := c.c.Send(nm)
if err != nil {
return netlink.Message{}, err
}
return reqnm, nil
}
// Receive receives one or more Messages from netlink. The netlink.Messages
// used to wrap each Message are available for later validation.
func (c *Conn) Receive() ([]Message, []netlink.Message, error) {
msgs, err := c.c.Receive()
if err != nil {
return nil, nil, err
}
rtmsgs, err := unpackMessages(msgs)
if err != nil {
return nil, nil, err
}
return rtmsgs, msgs, nil
}
// Execute sends a single Message to netlink using Send, receives one or more
// replies using Receive, and then checks the validity of the replies against
// the request using netlink.Validate.
//
// Execute acquires a lock for the duration of the function call which blocks
// concurrent calls to Send and Receive, in order to ensure consistency between
// generic netlink request/reply messages.
//
// See the documentation of Send, Receive, and netlink.Validate for details
// about each function.
func (c *Conn) Execute(m Message, family uint16, flags netlink.HeaderFlags) ([]Message, error) {
nm, err := packMessage(m, family, flags)
if err != nil {
return nil, err
}
msgs, err := c.c.Execute(nm)
if err != nil {
return nil, err
}
return unpackMessages(msgs)
}
// Message is the interface used for passing around different kinds of rtnetlink messages
type Message interface {
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
rtMessage()
}
// packMessage packs a rtnetlink Message into a netlink.Message with the
// appropriate rtnetlink family and netlink flags.
func packMessage(m Message, family uint16, flags netlink.HeaderFlags) (netlink.Message, error) {
nm := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType(family),
Flags: flags,
},
}
mb, err := m.MarshalBinary()
if err != nil {
return netlink.Message{}, err
}
nm.Data = mb
return nm, nil
}
// unpackMessages unpacks rtnetlink Messages from a slice of netlink.Messages.
func unpackMessages(msgs []netlink.Message) ([]Message, error) {
lmsgs := make([]Message, 0, len(msgs))
for _, nm := range msgs {
var m Message
switch nm.Header.Type {
case unix.RTM_GETLINK, unix.RTM_NEWLINK, unix.RTM_DELLINK:
m = &LinkMessage{}
case unix.RTM_GETADDR, unix.RTM_NEWADDR, unix.RTM_DELADDR:
m = &AddressMessage{}
case unix.RTM_GETROUTE, unix.RTM_NEWROUTE, unix.RTM_DELROUTE:
m = &RouteMessage{}
case unix.RTM_GETNEIGH, unix.RTM_NEWNEIGH, unix.RTM_DELNEIGH:
m = &NeighMessage{}
case unix.RTM_GETRULE, unix.RTM_NEWRULE, unix.RTM_DELRULE:
m = &RuleMessage{}
default:
continue
}
if err := (m).UnmarshalBinary(nm.Data); err != nil {
return nil, err
}
lmsgs = append(lmsgs, m)
}
return lmsgs, nil
}