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/v2/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{filtered: (nm.Header.Flags&netlink.DumpFiltered != 0)}
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
}
|