blob: 507f013be9a575f20f81cf0a2e8d43dde270a5dc [file] [log] [blame]
David Why Use Two When One Will Do Presottof6813ec2014-07-11 16:12:54 -07001// +build darwin dragonfly freebsd netbsd openbsd
2
3package netconfig
4
5// We connect to the Route socket and parse messages to
6// look for network configuration changes. This is generic
7// to all BSD based systems (including MacOS). The net
8// library already has code to parse the messages so all
9// we need to do is look for message types.
10
11import (
David Why Use Two When One Will Do Presotto3e529b92014-09-29 10:24:01 -070012 "errors"
13 "net"
David Why Use Two When One Will Do Presottof6813ec2014-07-11 16:12:54 -070014 "sync"
15 "syscall"
16 "time"
Jungho Ahn1c59a1f2015-01-13 10:51:55 -080017
Jiri Simsa337af232015-02-27 14:36:46 -080018 "v.io/x/lib/vlog"
David Why Use Two When One Will Do Presottof6813ec2014-07-11 16:12:54 -070019)
20
21/*
22#include <sys/socket.h>
23*/
24import "C"
25
26type bsdNetConfigWatcher struct {
27 sync.Mutex
28 t *time.Timer
29 c chan struct{}
30 s int
31 stopped bool
32}
33
34func (w *bsdNetConfigWatcher) Stop() {
35 w.Lock()
36 defer w.Unlock()
37 if w.stopped {
38 return
39 }
40 w.stopped = true
41 syscall.Close(w.s)
42}
43
44func (w *bsdNetConfigWatcher) Channel() chan struct{} {
45 return w.c
46}
47
48// NewNetConfigWatcher returns a watcher that sends a message
49// on the Channel() whenever the config changes.
50func NewNetConfigWatcher() (NetConfigWatcher, error) {
51 s, err := syscall.Socket(C.PF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
52 if err != nil {
53 vlog.Infof("socket failed: %s", err)
54 return nil, err
55 }
56 w := &bsdNetConfigWatcher{c: make(chan struct{}, 1), s: s}
57 go w.watcher()
58 return w, nil
59}
60
61func (w *bsdNetConfigWatcher) ding() {
62 w.Lock()
63 defer w.Unlock()
64 w.t = nil
65 if w.stopped {
66 return
67 }
68 // Don't let us hang in the lock. The default is safe because the requirement
69 // is that the client get a message after the last config change. Since this is
70 // a queued chan, we really don't have to stuff anything in it if there's already
71 // something there.
72 select {
73 case w.c <- struct{}{}:
74 default:
75 }
76}
77
78func (w *bsdNetConfigWatcher) watcher() {
79 defer w.Stop()
80
81 // Loop waiting for messages.
82 for {
83 b := make([]byte, 4096)
84 nr, err := syscall.Read(w.s, b)
85 if err != nil {
86 return
87 }
88 msgs, err := syscall.ParseRoutingMessage(b[:nr])
89 if err != nil {
90 vlog.Infof("Couldn't parse: %s", err)
91 continue
92 }
93
94 // Decode the addresses.
95 for _, m := range msgs {
96 switch m.(type) {
97 case *syscall.InterfaceMessage:
98 case *syscall.InterfaceAddrMessage:
Jungho Ahn1c59a1f2015-01-13 10:51:55 -080099 case *syscall.RouteMessage:
David Why Use Two When One Will Do Presottof6813ec2014-07-11 16:12:54 -0700100 default:
101 continue
102 }
103 // Changing networks usually spans many seconds and involves
104 // multiple network config changes. We add histeresis by
105 // setting an alarm when the first change is detected and
106 // not informing the client till the alarm goes off.
107 // NOTE(p): I chose 3 seconds because that covers all the
108 // events involved in moving from one wifi network to another.
109 w.Lock()
110 if w.t == nil {
111 w.t = time.AfterFunc(3*time.Second, w.ding)
112 }
113 w.Unlock()
114 }
115 }
116
David Why Use Two When One Will Do Presottof6813ec2014-07-11 16:12:54 -0700117 w.Stop()
118 w.Lock()
119 close(w.c)
120 if w.t != nil {
121 w.t.Stop()
122 }
123 w.Unlock()
124}
David Why Use Two When One Will Do Presotto3e529b92014-09-29 10:24:01 -0700125
126func toIP(sa syscall.Sockaddr) (net.IP, error) {
127 switch v := sa.(type) {
128 case *syscall.SockaddrInet4:
129 return net.IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3]), nil
130 case *syscall.SockaddrInet6:
131 return net.IP(v.Addr[:]), nil
132 }
133 return net.IPv6zero, errors.New("unknown sockaddr ip")
134}
135
136func toIPNet(sa syscall.Sockaddr, msa syscall.Sockaddr) (net.IPNet, error) {
137 var x net.IPNet
138 var err error
139 x.IP, err = toIP(sa)
140 if err != nil {
141 return x, err
142 }
143 switch v := msa.(type) {
144 case *syscall.SockaddrInet4:
145 x.Mask = net.IPv4Mask(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])
146 return x, nil
147 case *syscall.SockaddrInet6:
148 x.Mask = net.IPMask(v.Addr[:])
149 return x, nil
150 }
151 return x, errors.New("unknown sockaddr ipnet")
152}
153
154// IPRoutes returns all kernel known routes. If defaultOnly is set, only default routes
155// are returned.
156func GetIPRoutes(defaultOnly bool) []*IPRoute {
157 var x []*IPRoute
158 rib, err := syscall.RouteRIB(syscall.NET_RT_DUMP, 0)
159 if err != nil {
160 vlog.Infof("Couldn't read: %s", err)
161 return x
162 }
163 msgs, err := syscall.ParseRoutingMessage(rib)
164 if err != nil {
165 vlog.Infof("Couldn't parse: %s", err)
166 return x
167 }
168 for _, m := range msgs {
169 switch v := m.(type) {
170 case *syscall.RouteMessage:
171 addrs, err := syscall.ParseRoutingSockaddr(m)
172 if err != nil {
173 return x
174 }
175 if addrs[0] == nil || addrs[1] == nil || addrs[2] == nil {
176 continue
177 }
178 r := new(IPRoute)
179 if r.Gateway, err = toIP(addrs[1]); err != nil {
180 continue
181 }
182 if r.Net, err = toIPNet(addrs[0], addrs[2]); err != nil {
183 continue
184 }
185 r.IfcIndex = int(v.Header.Index)
186 if !defaultOnly || IsDefaultIPRoute(r) {
187 x = append(x, r)
188 }
189 }
190 }
191 return x
192}