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