blob: 40030016b73de84c320f4e9b8926a832e978de50 [file] [log] [blame]
Benjamin Prosnitza375b912014-10-07 14:53:42 -07001// +build linux
2
David Why Use Two When One Will Do Presottof6813ec2014-07-11 16:12:54 -07003package netconfig
4
5// We connect to the Netlink Route socket and parse messages to
6// look for network configuration changes. This is very Linux
7// specific, hence the file name.
8
9import (
10 "errors"
11 "fmt"
12 "net"
13 "sync"
14 "syscall"
15 "time"
16 "unsafe"
Jiri Simsa519c5072014-09-17 21:37:57 -070017 "veyron.io/veyron/veyron2/vlog"
David Why Use Two When One Will Do Presottof6813ec2014-07-11 16:12:54 -070018)
19
20/*
21#include <linux/rtnetlink.h>
22*/
23import "C"
24
25// All rtnetlink attributes start with this header.
26type rtAttrHdr C.struct_rtattr
27
28const rtAttrHdrLen = C.sizeof_struct_rtattr
29
30// The address change messages (RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR).
31type ifaddrMsgHdr C.struct_ifaddrmsg
32
33const ifaddrMsgHdrLen = C.sizeof_struct_ifaddrmsg
34
35type rtAttribute fmt.Stringer
36
37type rtAddressMessage struct {
38 name string
39 hdr ifaddrMsgHdr
40 attributes []rtAttribute
41}
42
43// Attribute types (see rtnetlink(7))
44type ifaAddress net.IP
45type ifaLocal net.IP
46type ifaBroadcast net.IP
47type ifaAnycast net.IP
48type ifaMulticast net.IP
49type ifaLabel string
50type ifaCacheInfo C.struct_ifa_cacheinfo
51
52const ifaCacheInfoLen = C.sizeof_struct_ifa_cacheinfo
53
54// String routines to make debugging easier.
55func (a ifaAddress) String() string { return "Address=" + net.IP(a).String() }
56func (a ifaLocal) String() string { return "Local=" + net.IP(a).String() }
57func (a ifaBroadcast) String() string { return "Braodcast=" + net.IP(a).String() }
58func (a ifaAnycast) String() string { return "Anycast=" + net.IP(a).String() }
59func (a ifaMulticast) String() string { return "Anycast=" + net.IP(a).String() }
60func (a ifaLabel) String() string { return "Label=" + string(a) }
61func (a ifaCacheInfo) String() string {
62 return fmt.Sprintf("CacheInfo[preferred %d valid %d cstamp %d tstamp %d]", a.ifa_prefered, a.ifa_valid, a.cstamp, a.tstamp)
63}
64func (m *rtAddressMessage) String() string {
65 return fmt.Sprintf("%s: index %d %v", m.name, m.hdr.ifa_index, m.attributes)
66}
67
68// Address looks for the address attribute in an rtAddressMessage. If it isn't there, just assume the null address.
69func (m *rtAddressMessage) Address() net.IP {
70 for _, a := range m.attributes {
71 switch a.(type) {
72 case ifaAddress:
73 return net.IP(a.(ifaAddress))
74 }
75 }
76 return net.IPv4zero
77}
78
79func parsertAddressAttribute(b []byte) (rtAttribute, []byte, error) {
80 if len(b) == 0 {
81 return nil, nil, nil
82 }
83 if len(b) < rtAttrHdrLen {
84 return nil, nil, errors.New("attribute too short")
85 }
86 ahdr := (*rtAttrHdr)(unsafe.Pointer(&b[0]))
87 rounded := ((ahdr.rta_len + 3) / 4) * 4
88 if len(b) < int(rounded) {
89 return nil, nil, errors.New("attribute too short")
90 }
91 remaining := b[rounded:]
92 b = b[rtAttrHdrLen:ahdr.rta_len]
93 switch ahdr.rta_type {
94 case C.IFA_ADDRESS:
95 return rtAttribute(ifaAddress(net.IP(b))), remaining, nil
96 case C.IFA_LOCAL:
97 return rtAttribute(ifaLocal(b)), remaining, nil
98 case C.IFA_LABEL:
99 return rtAttribute(ifaLabel(b)), remaining, nil
100 case C.IFA_BROADCAST:
101 return rtAttribute(ifaBroadcast(b)), remaining, nil
102 case C.IFA_ANYCAST:
103 return rtAttribute(ifaAnycast(b)), remaining, nil
104 case C.IFA_CACHEINFO:
105 if len(b) < ifaCacheInfoLen {
106 return nil, nil, errors.New("attribute too short")
107 }
108 return rtAttribute(ifaCacheInfo(*(*C.struct_ifa_cacheinfo)(unsafe.Pointer(&b[0])))), remaining, nil
109 case C.IFA_MULTICAST:
110 return rtAttribute(ifaMulticast(b)), remaining, nil
111 }
112 return nil, remaining, errors.New("unknown attribute")
113}
114
115func parsertAddressMessage(nlm syscall.NetlinkMessage) (*rtAddressMessage, error) {
116 var name string
117 switch nlm.Header.Type {
118 case C.RTM_NEWADDR:
119 name = "RTM_NEWADDR"
120 case C.RTM_DELADDR:
121 name = "RTM_DELADDR"
122 case C.RTM_GETADDR:
123 name = "RTM_GETADDR"
124 default:
125 return nil, fmt.Errorf("unknown message type")
126 }
127 if len(nlm.Data) < ifaddrMsgHdrLen {
128 return nil, errors.New("bad length")
129 }
130 m := &rtAddressMessage{name: name, hdr: *(*ifaddrMsgHdr)(unsafe.Pointer(&nlm.Data[0]))}
131 b := nlm.Data[ifaddrMsgHdrLen:]
132 for {
133 var a rtAttribute
134 var err error
135 a, b, err = parsertAddressAttribute(b)
136 if b == nil {
137 break
138 }
139 if err == nil {
140 m.attributes = append(m.attributes, a)
141 }
142 }
143 return m, nil
144}
145
146// The link change messages (RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK).
147type ifInfoMsgHdr C.struct_ifinfomsg
148
149const ifInfoMsgHdrLen = C.sizeof_struct_ifinfomsg
150
151type rtLinkMessage struct {
152 name string
153 hdr ifInfoMsgHdr
154 attributes []rtAttribute
155}
156
157// Attribute types (see rtnetlink(7))
158type iflaAddress []byte
159type iflaBroadcast []byte
160type iflaIFName string
161type iflaMTU uint32
162type iflaLink int
163type iflaQDisc string
164type iflaOperstate int
165type iflaStats C.struct_rtnl_link_stats
166
167const iflaStatsLen = C.sizeof_struct_rtnl_link_stats
168
169// String routines to make debugging easier.
170func (a iflaAddress) String() string { return fmt.Sprintf("HWAddress=%v", []byte(a)) }
171func (a iflaBroadcast) String() string { return fmt.Sprintf("HWBroadcast=%v", []byte(a)) }
172func (a iflaIFName) String() string { return "Name=" + string(a) }
173func (a iflaMTU) String() string { return fmt.Sprintf("MTU=%d", uint32(a)) }
174func (a iflaLink) String() string { return fmt.Sprintf("Type=%d", int(a)) }
175func (a iflaQDisc) String() string { return "Qdisc=" + string(a) }
176func (a iflaStats) String() string {
177 return fmt.Sprintf("Stats[rx %d tx %d ...]", a.rx_packets, a.tx_packets)
178}
179func (a iflaOperstate) String() string { return fmt.Sprintf("Operstate=%d", int(a)) }
180func (m *rtLinkMessage) String() string {
181 return fmt.Sprintf("%s: index %d %v", m.name, m.hdr.ifi_index, m.attributes)
182}
183
184func parseRTLinkAttribute(b []byte) (rtAttribute, []byte, error) {
185 if len(b) == 0 {
186 return nil, nil, nil
187 }
188 if len(b) < rtAttrHdrLen {
189 return nil, nil, errors.New("attribute too short")
190 }
191 ahdr := (*rtAttrHdr)(unsafe.Pointer(&b[0]))
192 rounded := ((ahdr.rta_len + 3) / 4) * 4
193 if len(b) < int(rounded) {
194 return nil, nil, errors.New("attribute too short")
195 }
196 remaining := b[rounded:]
197 b = b[rtAttrHdrLen:ahdr.rta_len]
198 switch ahdr.rta_type {
199 case C.IFLA_ADDRESS:
200 return rtAttribute(iflaAddress(b)), remaining, nil
201 case C.IFLA_BROADCAST:
202 return rtAttribute(iflaBroadcast(b)), remaining, nil
203 case C.IFLA_IFNAME:
204 return rtAttribute(iflaIFName(string(b))), remaining, nil
205 case C.IFLA_MTU:
206 return rtAttribute(iflaMTU(*(*C.uint)(unsafe.Pointer(&b[0])))), remaining, nil
207 case C.IFLA_LINK:
208 return rtAttribute(iflaMTU(*(*C.int)(unsafe.Pointer(&b[0])))), remaining, nil
209 case C.IFLA_QDISC:
210 return rtAttribute(iflaQDisc(string(b))), remaining, nil
211 case C.IFLA_STATS:
212 if len(b) < iflaStatsLen {
213 return nil, remaining, errors.New("attribute too short")
214 }
215 return rtAttribute(iflaStats(*(*C.struct_rtnl_link_stats)(unsafe.Pointer(&b[0])))), remaining, nil
216 case C.IFLA_OPERSTATE:
217 return rtAttribute(iflaOperstate(*(*C.int)(unsafe.Pointer(&b[0])))), remaining, nil
218 }
219 return nil, remaining, errors.New("unknown attribute")
220}
221
222func parsertLinkMessage(nlm syscall.NetlinkMessage) (*rtLinkMessage, error) {
223 var name string
224 switch nlm.Header.Type {
225 case C.RTM_NEWLINK:
226 name = "RTM_NEWLINK"
227 case C.RTM_DELLINK:
228 name = "RTM_DELLINK"
229 case C.RTM_GETLINK:
230 name = "RTM_GETLINK"
231 default:
232 return nil, fmt.Errorf("unknown message type")
233 }
234 if len(nlm.Data) < ifInfoMsgHdrLen {
235 return nil, errors.New("bad length")
236 }
237 m := &rtLinkMessage{name: name, hdr: *(*ifInfoMsgHdr)(unsafe.Pointer(&nlm.Data[0]))}
238 b := nlm.Data[ifInfoMsgHdrLen:]
239 for {
240 var a rtAttribute
241 var err error
242 a, b, err = parseRTLinkAttribute(b)
243 if b == nil {
244 break
245 }
246 if err == nil {
247 m.attributes = append(m.attributes, a)
248 }
249 }
250 return m, nil
251}
252
253type rtnetlinkWatcher struct {
254 sync.Mutex
255 t *time.Timer
256 c chan struct{}
257 s int
258 stopped bool
259}
260
261func (w *rtnetlinkWatcher) Stop() {
262 w.Lock()
263 defer w.Unlock()
264 if w.stopped {
265 return
266 }
267 w.stopped = true
268 syscall.Close(w.s)
269}
270
271func (w *rtnetlinkWatcher) Channel() chan struct{} {
272 return w.c
273}
274
275const (
276 GROUPS = C.RTMGRP_LINK | C.RTMGRP_IPV4_IFADDR | C.RTMGRP_IPV6_IFADDR | C.RTMGRP_NOTIFY
277)
278
279// NewNetConfigWatcher returns a watcher that wakes up anyone
280// calling the Wait routine whenever the configuration changes.
281func NewNetConfigWatcher() (NetConfigWatcher, error) {
282 s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE)
283 if err != nil {
284 vlog.Infof("netconfig socket failed: %s", err)
285 return nil, err
286 }
287
288 lsa := &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK, Groups: GROUPS}
289 if err := syscall.Bind(s, lsa); err != nil {
290 vlog.Infof("netconfig bind failed: %s", err)
291 return nil, err
292 }
293
294 w := &rtnetlinkWatcher{c: make(chan struct{}, 1), s: s}
295 go w.watcher()
296 return w, nil
297}
298
299func (w *rtnetlinkWatcher) ding() {
300 w.Lock()
301 defer w.Unlock()
302 w.t = nil
303 if w.stopped {
304 return
305 }
306 // Don't let us hang in the lock. The default is safe because the requirement
307 // is that the client get a message after the last config change. Since this is
308 // a queued chan, we really don't have to stuff anything in it if there's already
309 // something there.
310 select {
311 case w.c <- struct{}{}:
312 default:
313 }
314}
315
316func (w *rtnetlinkWatcher) watcher() {
317 var newAddrs []net.IP
318 for {
319 rb := make([]byte, 4096)
320 nr, _, err := syscall.Recvfrom(w.s, rb, 0)
321 if err != nil {
322 break
323 }
324 rb = rb[:nr]
325 msgs, err := syscall.ParseNetlinkMessage(rb)
326 if err != nil {
327 vlog.Infof("ParseNetlinkMessage failed: %s", err)
328 continue
329 }
330 L:
331 for _, m := range msgs {
332 if am, err := parsertAddressMessage(m); err == nil {
333 // NOTE(p): We get continuous NEWADDR messages about some
334 // IPv6 addresses in Google corp. Just ignore duplicate back
335 // to back NEWADDRs about the same addresses.
336 if am.name == "RTM_NEWADDR" {
337 addr := am.Address()
338 for _, a := range newAddrs {
339 if addr.Equal(a) {
340 break L
341 }
342 }
343 newAddrs = append(newAddrs, addr)
344 } else {
345 newAddrs = nil
346 }
347 } else if _, err := parsertLinkMessage(m); err == nil {
348 newAddrs = nil
349 } else {
350 continue
351 }
352 // Changing networks usually spans many seconds and involves
353 // multiple network config changes. We add histeresis by
354 // setting an alarm when the first change is detected and
355 // not informing the client till the alarm goes off.
356 // NOTE(p): I chose 3 seconds because that covers all the
357 // events involved in moving from one wifi network to another.
358 w.Lock()
359 if w.t == nil {
360 w.t = time.AfterFunc(3*time.Second, w.ding)
361 }
362 w.Unlock()
363 }
364 }
365
366 w.Stop()
367 w.Lock()
368 close(w.c)
369 if w.t != nil {
370 w.t.Stop()
371 }
372 w.Unlock()
373}
David Why Use Two When One Will Do Presotto3e529b92014-09-29 10:24:01 -0700374func toIP(a []byte) (net.IP, error) {
375 switch len(a) {
376 case 4:
377 return net.IPv4(a[0], a[1], a[2], a[3]), nil
378 case 16:
379 return net.IP(a), nil
380 }
381 return net.IPv6unspecified, errors.New("unknown ip address len")
382}
383
384// IPRoutes returns all kernel known routes. If defaultOnly is set, only default routes
385// are returned.
386func GetIPRoutes(defaultOnly bool) []*IPRoute {
387 var x []*IPRoute
388 rib, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC)
389 if err != nil {
390 vlog.Infof("Couldn't read: %s", err)
391 return x
392 }
393 msgs, err := syscall.ParseNetlinkMessage(rib)
394 if err != nil {
395 vlog.Infof("Couldn't parse: %s", err)
396 return x
397 }
398L:
399 for _, m := range msgs {
400 if m.Header.Type != syscall.RTM_NEWROUTE {
401 continue
402 }
403 attrs, err := syscall.ParseNetlinkRouteAttr(&m)
404 if err != nil {
405 continue
406 }
407 r := new(IPRoute)
408 r.Net.IP = net.IPv4zero
409 for _, a := range attrs {
410 switch a.Attr.Type {
411 case syscall.RTA_DST:
412 if r.Net.IP, err = toIP(a.Value[:]); err != nil {
413 continue L
414 }
415 case syscall.RTA_GATEWAY:
416 if r.Gateway, err = toIP(a.Value[:]); err != nil {
417 continue L
418 }
419 case syscall.RTA_OIF:
420 r.IfcIndex = int(a.Value[0])
421 case syscall.RTA_PREFSRC:
422 if r.PreferredSource, err = toIP(a.Value[:]); err != nil {
423 continue L
424 }
425 }
426 }
427 b := m.Data[:syscall.SizeofRtMsg]
428 a := (*syscall.RtMsg)(unsafe.Pointer(&b[0]))
429 len := 128
430 if r.Net.IP.To4() != nil {
431 len = 32
432 }
433 if int(a.Dst_len) > len {
434 continue
435 }
436 r.Net.Mask = net.CIDRMask(int(a.Dst_len), len)
437 if !defaultOnly || IsDefaultIPRoute(r) {
438 x = append(x, r)
439 }
440 }
441 return x
442}