BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file NCDInterfaceMonitor.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29  
30 #include <string.h>
31 #include <unistd.h>
32 #include <stdint.h>
33  
34 #include <sys/socket.h>
35 #include <net/if.h>
36 #include <netinet/in.h>
37 #include <linux/netlink.h>
38 #include <linux/rtnetlink.h>
39 #include <asm/types.h>
40 #include <asm/types.h>
41  
42 #include <misc/debug.h>
43 #include <misc/nonblocking.h>
44 #include <base/BLog.h>
45  
46 #include <ncd/extra/NCDInterfaceMonitor.h>
47  
48 #include <generated/blog_channel_NCDInterfaceMonitor.h>
49  
50 #define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
51  
52 static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type);
53 static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len);
54 static void report_error (NCDInterfaceMonitor *o);
55 static int send_next_dump_request (NCDInterfaceMonitor *o);
56 static void netlink_fd_handler (NCDInterfaceMonitor *o, int events);
57 static void process_buffer (NCDInterfaceMonitor *o);
58 static void more_job_handler (NCDInterfaceMonitor *o);
59  
60 static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type)
61 {
62 struct {
63 struct nlmsghdr nlh;
64 struct rtgenmsg g;
65 } req;
66  
67 memset(&req, 0, sizeof(req));
68 req.nlh.nlmsg_len = sizeof(req);
69 req.nlh.nlmsg_type = type;
70 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
71 req.nlh.nlmsg_pid = 0;
72 req.nlh.nlmsg_seq = seq;
73 req.g.rtgen_family = family;
74  
75 int res = write(fd, &req, sizeof(req));
76 if (res < 0) {
77 BLog(BLOG_ERROR, "write failed");
78 return 0;
79 }
80 if (res != sizeof(req)) {
81 BLog(BLOG_ERROR, "write short");
82 return 0;
83 }
84  
85 return 1;
86 }
87  
88 static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len)
89 {
90 for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
91 uint8_t *attr = RTA_DATA(rta);
92 int attr_len = RTA_PAYLOAD(rta);
93  
94 if (rta->rta_type == type) {
95 *out_attr = attr;
96 *out_attr_len = attr_len;
97 return 1;
98 }
99 }
100  
101 return 0;
102 }
103  
104 static void report_error (NCDInterfaceMonitor *o)
105 {
106 DEBUGERROR(&o->d_err, o->handler_error(o->user))
107 }
108  
109 static int send_next_dump_request (NCDInterfaceMonitor *o)
110 {
111 ASSERT(o->dump_queue)
112  
113 if (o->dump_queue & NCDIFMONITOR_WATCH_LINK) {
114 o->dump_queue &= ~NCDIFMONITOR_WATCH_LINK;
115 return send_wilddump_request(o, o->netlink_fd, o->dump_seq, 0, RTM_GETLINK);
116 }
117 else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV4_ADDR) {
118 o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV4_ADDR;
119 return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET, RTM_GETADDR);
120 }
121 else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV6_ADDR) {
122 o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV6_ADDR;
123 return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET6, RTM_GETADDR);
124 }
125  
126 ASSERT(0)
127 return 0;
128 }
129  
130 void netlink_fd_handler (NCDInterfaceMonitor *o, int events)
131 {
132 DebugObject_Access(&o->d_obj);
133 ASSERT(o->have_bfd)
134  
135 // handler fd error
136 if (o->buf_left >= 0) {
137 BLog(BLOG_ERROR, "file descriptor error");
138 goto fail;
139 }
140  
141 // read from netlink fd
142 int len = read(o->netlink_fd, o->buf.buf, sizeof(o->buf));
143 if (len < 0) {
144 BLog(BLOG_ERROR, "read failed");
145 goto fail;
146 }
147  
148 // stop receiving fd events
149 BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0);
150  
151 // set buffer
152 o->buf_nh = &o->buf.nlh;
153 o->buf_left = len;
154  
155 // process buffer
156 process_buffer(o);
157 return;
158  
159 fail:
160 report_error(o);
161 }
162  
163 void process_buffer (NCDInterfaceMonitor *o)
164 {
165 ASSERT(o->buf_left >= 0)
166 ASSERT(o->have_bfd)
167  
168 int done = 0;
169  
170 for (; NLMSG_OK(o->buf_nh, o->buf_left); o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left)) {
171 if (o->buf_nh->nlmsg_type == NLMSG_DONE) {
172 done = 1;
173 break;
174 }
175  
176 struct nlmsghdr *buf = o->buf_nh;
177 void *pl = NLMSG_DATA(buf);
178 int pl_len = NLMSG_PAYLOAD(buf, 0);
179  
180 struct NCDInterfaceMonitor_event ev;
181  
182 switch (buf->nlmsg_type) {
183 case RTM_NEWLINK: { // not RTM_DELLINK! who knows what these mean...
184 if (pl_len < sizeof(struct ifinfomsg)) {
185 BLog(BLOG_ERROR, "ifinfomsg too short");
186 goto fail;
187 }
188 struct ifinfomsg *msg = pl;
189  
190 if (msg->ifi_index == o->ifindex && (o->watch_events & NCDIFMONITOR_WATCH_LINK)) {
191 ev.event = (buf->nlmsg_type == RTM_NEWLINK && (msg->ifi_flags & IFF_RUNNING)) ? NCDIFMONITOR_EVENT_LINK_UP : NCDIFMONITOR_EVENT_LINK_DOWN;
192 goto dispatch;
193 }
194 } break;
195  
196 case RTM_NEWADDR:
197 case RTM_DELADDR: {
198 if (pl_len < sizeof(struct ifaddrmsg)) {
199 BLog(BLOG_ERROR, "ifaddrmsg too short");
200 goto fail;
201 }
202 struct ifaddrmsg *msg = pl;
203  
204 void *addr;
205 int addr_len;
206 if (!get_attr(IFA_ADDRESS, IFA_RTA(msg), buf->nlmsg_len - NLMSG_LENGTH(sizeof(*msg)), &addr, &addr_len)) {
207 break;
208 }
209  
210 if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET && (o->watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR)) {
211 if (addr_len != 4 || msg->ifa_prefixlen > 32) {
212 BLog(BLOG_ERROR, "bad ipv4 ifaddrmsg");
213 goto fail;
214 }
215  
216 ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED;
217 ev.u.ipv4_addr.addr.addr = ((struct in_addr *)addr)->s_addr;
218 ev.u.ipv4_addr.addr.prefix = msg->ifa_prefixlen;
219 goto dispatch;
220 }
221  
222 if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET6 && (o->watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR)) {
223 if (addr_len != 16 || msg->ifa_prefixlen > 128) {
224 BLog(BLOG_ERROR, "bad ipv6 ifaddrmsg");
225 goto fail;
226 }
227  
228 ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED;
229 memcpy(ev.u.ipv6_addr.addr.addr.bytes, ((struct in6_addr *)addr)->s6_addr, 16);
230 ev.u.ipv6_addr.addr.prefix = msg->ifa_prefixlen;
231 ev.u.ipv6_addr.addr_flags = 0;
232 ev.u.ipv6_addr.scope = msg->ifa_scope;
233 if (!(msg->ifa_flags & IFA_F_PERMANENT)) {
234 ev.u.ipv6_addr.addr_flags |= NCDIFMONITOR_ADDR_FLAG_DYNAMIC;
235 }
236 goto dispatch;
237 }
238 } break;
239 }
240  
241 continue;
242  
243 dispatch:
244 // move to next message
245 o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left);
246  
247 // schedule more job
248 BPending_Set(&o->more_job);
249  
250 // dispatch event
251 o->handler(o->user, ev);
252 return;
253 }
254  
255 if (done) {
256 if (o->dump_queue) {
257 // increment dump request sequence number
258 o->dump_seq++;
259  
260 // send next dump request
261 if (!send_next_dump_request(o)) {
262 goto fail;
263 }
264 }
265 else if (o->event_netlink_fd >= 0) {
266 // stop watching dump fd
267 BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
268 o->have_bfd = 0;
269  
270 // close dump fd, make event fd current
271 close(o->netlink_fd);
272 o->netlink_fd = o->event_netlink_fd;
273 o->event_netlink_fd = -1;
274  
275 // start watching event fd
276 BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o);
277 if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) {
278 BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
279 goto fail;
280 }
281 o->have_bfd = 1;
282 }
283 }
284  
285 // set no buffer
286 o->buf_left = -1;
287  
288 // continue receiving fd events
289 BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ);
290 return;
291  
292 fail:
293 report_error(o);
294 }
295  
296 void more_job_handler (NCDInterfaceMonitor *o)
297 {
298 DebugObject_Access(&o->d_obj);
299 ASSERT(o->buf_left >= 0)
300  
301 // process buffer
302 process_buffer(o);
303 return;
304 }
305  
306 int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, int ifindex, int watch_events, BReactor *reactor, void *user,
307 NCDInterfaceMonitor_handler handler,
308 NCDInterfaceMonitor_handler_error handler_error)
309 {
310 ASSERT(watch_events)
311 ASSERT((watch_events&~(NCDIFMONITOR_WATCH_LINK|NCDIFMONITOR_WATCH_IPV4_ADDR|NCDIFMONITOR_WATCH_IPV6_ADDR)) == 0)
312 ASSERT(handler)
313 ASSERT(handler_error)
314 BNetwork_Assert();
315  
316 // init arguments
317 o->ifindex = ifindex;
318 o->watch_events = watch_events;
319 o->reactor = reactor;
320 o->user = user;
321 o->handler = handler;
322 o->handler_error = handler_error;
323  
324 // init dump netlink fd
325 if ((o->netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
326 BLog(BLOG_ERROR, "socket failed");
327 goto fail0;
328 }
329 if (!badvpn_set_nonblocking(o->netlink_fd)) {
330 BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
331 goto fail1;
332 }
333  
334 // init event netlink fd
335 if ((o->event_netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
336 BLog(BLOG_ERROR, "socket failed");
337 goto fail1;
338 }
339 if (!badvpn_set_nonblocking(o->event_netlink_fd)) {
340 BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
341 goto fail2;
342 }
343  
344 // build bind address
345 struct sockaddr_nl sa;
346 memset(&sa, 0, sizeof(sa));
347 sa.nl_family = AF_NETLINK;
348 sa.nl_groups = 0;
349 if (watch_events & NCDIFMONITOR_WATCH_LINK) sa.nl_groups |= RTMGRP_LINK;
350 if (watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR) sa.nl_groups |= RTMGRP_IPV4_IFADDR;
351 if (watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR) sa.nl_groups |= RTMGRP_IPV6_IFADDR;
352  
353 // bind event netlink fd
354 if (bind(o->event_netlink_fd, (void *)&sa, sizeof(sa)) < 0) {
355 BLog(BLOG_ERROR, "bind failed");
356 goto fail2;
357 }
358  
359 // set dump state
360 o->dump_queue = watch_events;
361 o->dump_seq = 0;
362  
363 // init BFileDescriptor
364 BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o);
365 if (!BReactor_AddFileDescriptor(reactor, &o->bfd)) {
366 BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
367 goto fail2;
368 }
369 o->have_bfd = 1;
370  
371 // set nothing in buffer
372 o->buf_left = -1;
373  
374 // init more job
375 BPending_Init(&o->more_job, BReactor_PendingGroup(reactor), (BPending_handler)more_job_handler, o);
376  
377 // send first dump request
378 if (!send_next_dump_request(o)) {
379 goto fail3;
380 }
381  
382 // wait for reading fd
383 BReactor_SetFileDescriptorEvents(reactor, &o->bfd, BREACTOR_READ);
384  
385 DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor));
386 DebugObject_Init(&o->d_obj);
387 return 1;
388  
389 fail3:
390 BPending_Free(&o->more_job);
391 BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
392 fail2:
393 close(o->event_netlink_fd);
394 fail1:
395 close(o->netlink_fd);
396 fail0:
397 return 0;
398 }
399  
400 void NCDInterfaceMonitor_Free (NCDInterfaceMonitor *o)
401 {
402 DebugObject_Free(&o->d_obj);
403 DebugError_Free(&o->d_err);
404  
405 // free more job
406 BPending_Free(&o->more_job);
407  
408 // free BFileDescriptor
409 if (o->have_bfd) {
410 BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
411 }
412  
413 // close event fd, in case we're still dumping
414 if (o->event_netlink_fd >= 0) {
415 close(o->event_netlink_fd);
416 }
417  
418 // close fd
419 close(o->netlink_fd);
420 }
421  
422 void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o)
423 {
424 DebugObject_Access(&o->d_obj);
425 DebugError_AssertNoError(&o->d_err);
426 ASSERT(o->have_bfd)
427  
428 if (o->buf_left >= 0) {
429 BPending_Unset(&o->more_job);
430 } else {
431 BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0);
432 }
433 }
434  
435 void NCDInterfaceMonitor_Continue (NCDInterfaceMonitor *o)
436 {
437 DebugObject_Access(&o->d_obj);
438 DebugError_AssertNoError(&o->d_err);
439 ASSERT(o->have_bfd)
440  
441 if (o->buf_left >= 0) {
442 BPending_Set(&o->more_job);
443 } else {
444 BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ);
445 }
446 }