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
|
package netboot
import (
"encoding/binary"
"net"
"github.com/jsimonetti/rtnetlink"
"golang.org/x/sys/unix"
)
// RTNL is a rtnetlink object with a high-level interface.
type RTNL struct {
conn *rtnetlink.Conn
}
func (r *RTNL) init() error {
if r.conn != nil {
return nil
}
conn, err := rtnetlink.Dial(nil)
if err != nil {
return err
}
r.conn = conn
return nil
}
// Close closes the netlink connection. Must be called to avoid leaks!
func (r *RTNL) Close() {
if r.conn != nil {
r.conn.Close()
r.conn = nil
}
}
// GetLinkState returns the operational state for the given interface index.
func (r *RTNL) GetLinkState(iface int) (rtnetlink.OperationalState, error) {
if err := r.init(); err != nil {
return 0, err
}
msg, err := r.conn.Link.Get(uint32(iface))
if err != nil {
return 0, err
}
return msg.Attributes.OperationalState, nil
}
// SetLinkState sets the operational state up or down for the given interface
// index.
func (r *RTNL) SetLinkState(iface int, up bool) error {
if err := r.init(); err != nil {
return err
}
var state uint32
if up {
state = unix.IFF_UP
}
msg := rtnetlink.LinkMessage{
Family: unix.AF_UNSPEC,
Type: unix.ARPHRD_NETROM,
Index: uint32(iface),
Flags: state,
Change: unix.IFF_UP,
}
if err := r.conn.Link.Set(&msg); err != nil {
return err
}
return nil
}
func getFamily(ip net.IP) int {
if ip.To4() != nil {
return unix.AF_INET
}
return unix.AF_INET6
}
// SetAddr sets the interface address.
func (r *RTNL) SetAddr(iface int, a net.IPNet) error {
if err := r.init(); err != nil {
return err
}
ones, _ := a.Mask.Size()
msg := rtnetlink.AddressMessage{
Family: uint8(getFamily(a.IP)),
PrefixLength: uint8(ones),
// TODO detect the right scope to set, or get it as input argument
Scope: unix.RT_SCOPE_UNIVERSE,
Index: uint32(iface),
Attributes: rtnetlink.AddressAttributes{
Address: a.IP,
Local: a.IP,
},
}
if a.IP.To4() != nil {
// Broadcast is only required for IPv4
ip := make(net.IP, net.IPv4len)
binary.BigEndian.PutUint32(
ip,
binary.BigEndian.Uint32(a.IP.To4())|
^binary.BigEndian.Uint32(net.IP(a.Mask).To4()))
msg.Attributes.Broadcast = ip
}
if err := r.conn.Address.New(&msg); err != nil {
return err
}
return nil
}
// RouteDel deletes a route to the given destination
func (r *RTNL) RouteDel(dst net.IP) error {
if err := r.init(); err != nil {
return err
}
msg := rtnetlink.RouteMessage{
Family: uint8(getFamily(dst)),
Table: unix.RT_TABLE_MAIN,
// TODO make this configurable?
Protocol: unix.RTPROT_UNSPEC,
// TODO make this configurable?
Scope: unix.RT_SCOPE_NOWHERE,
Type: unix.RTN_UNSPEC,
Attributes: rtnetlink.RouteAttributes{
Dst: dst,
},
}
if err := r.conn.Route.Delete(&msg); err != nil {
return err
}
return nil
}
// RouteAdd adds a route to dst, from src (if set), via gw.
func (r *RTNL) RouteAdd(iface int, dst, src net.IPNet, gw net.IP) error {
if err := r.init(); err != nil {
return err
}
dstLen, _ := dst.Mask.Size()
srcLen, _ := src.Mask.Size()
msg := rtnetlink.RouteMessage{
Family: uint8(getFamily(dst.IP)),
Table: unix.RT_TABLE_MAIN,
// TODO make this configurable?
Protocol: unix.RTPROT_BOOT,
// TODO make this configurable?
Scope: unix.RT_SCOPE_UNIVERSE,
Type: unix.RTN_UNICAST,
DstLength: uint8(dstLen),
SrcLength: uint8(srcLen),
Attributes: rtnetlink.RouteAttributes{
Dst: dst.IP,
Src: src.IP,
Gateway: gw,
OutIface: uint32(iface),
},
}
if err := r.conn.Route.Add(&msg); err != nil {
return err
}
return nil
}
|