Centralized thread cancellation in processor_t
[strongswan.git] / src / libhydra / plugins / kernel_pfroute / kernel_pfroute_net.c
1 /*
2 * Copyright (C) 2009-2012 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <net/if.h>
19 #include <ifaddrs.h>
20 #include <net/route.h>
21 #include <unistd.h>
22 #include <errno.h>
23
24 #include "kernel_pfroute_net.h"
25
26 #include <hydra.h>
27 #include <debug.h>
28 #include <utils/host.h>
29 #include <threading/thread.h>
30 #include <threading/mutex.h>
31 #include <utils/linked_list.h>
32 #include <processing/jobs/callback_job.h>
33
34 #ifndef HAVE_STRUCT_SOCKADDR_SA_LEN
35 #error Cannot compile this plugin on systems where 'struct sockaddr' has no sa_len member.
36 #endif
37
38 /** delay before firing roam events (ms) */
39 #define ROAM_DELAY 100
40
41 /** buffer size for PF_ROUTE messages */
42 #define PFROUTE_BUFFER_SIZE 4096
43
44 typedef struct addr_entry_t addr_entry_t;
45
46 /**
47 * IP address in an inface_entry_t
48 */
49 struct addr_entry_t {
50
51 /** The ip address */
52 host_t *ip;
53
54 /** virtual IP managed by us */
55 bool virtual;
56
57 /** Number of times this IP is used, if virtual */
58 u_int refcount;
59 };
60
61 /**
62 * destroy a addr_entry_t object
63 */
64 static void addr_entry_destroy(addr_entry_t *this)
65 {
66 this->ip->destroy(this->ip);
67 free(this);
68 }
69
70 typedef struct iface_entry_t iface_entry_t;
71
72 /**
73 * A network interface on this system, containing addr_entry_t's
74 */
75 struct iface_entry_t {
76
77 /** interface index */
78 int ifindex;
79
80 /** name of the interface */
81 char ifname[IFNAMSIZ];
82
83 /** interface flags, as in netdevice(7) SIOCGIFFLAGS */
84 u_int flags;
85
86 /** list of addresses as host_t */
87 linked_list_t *addrs;
88 };
89
90 /**
91 * destroy an interface entry
92 */
93 static void iface_entry_destroy(iface_entry_t *this)
94 {
95 this->addrs->destroy_function(this->addrs, (void*)addr_entry_destroy);
96 free(this);
97 }
98
99
100 typedef struct private_kernel_pfroute_net_t private_kernel_pfroute_net_t;
101
102 /**
103 * Private variables and functions of kernel_pfroute class.
104 */
105 struct private_kernel_pfroute_net_t
106 {
107 /**
108 * Public part of the kernel_pfroute_t object.
109 */
110 kernel_pfroute_net_t public;
111
112 /**
113 * mutex to lock access to various lists
114 */
115 mutex_t *mutex;
116
117 /**
118 * Cached list of interfaces and their addresses (iface_entry_t)
119 */
120 linked_list_t *ifaces;
121
122 /**
123 * mutex to lock access to the PF_ROUTE socket
124 */
125 mutex_t *mutex_pfroute;
126
127 /**
128 * PF_ROUTE socket to communicate with the kernel
129 */
130 int socket;
131
132 /**
133 * PF_ROUTE socket to receive events
134 */
135 int socket_events;
136
137 /**
138 * sequence number for messages sent to the kernel
139 */
140 int seq;
141
142 /**
143 * time of last roam event
144 */
145 timeval_t last_roam;
146 };
147
148 /**
149 * callback function that raises the delayed roam event
150 */
151 static job_requeue_t roam_event(uintptr_t address)
152 {
153 hydra->kernel_interface->roam(hydra->kernel_interface, address != 0);
154 return JOB_REQUEUE_NONE;
155 }
156
157 /**
158 * fire a roaming event. we delay it for a bit and fire only one event
159 * for multiple calls. otherwise we would create too many events.
160 */
161 static void fire_roam_event(private_kernel_pfroute_net_t *this, bool address)
162 {
163 timeval_t now;
164 job_t *job;
165
166 time_monotonic(&now);
167 if (timercmp(&now, &this->last_roam, >))
168 {
169 now.tv_usec += ROAM_DELAY * 1000;
170 while (now.tv_usec > 1000000)
171 {
172 now.tv_sec++;
173 now.tv_usec -= 1000000;
174 }
175 this->last_roam = now;
176
177 job = (job_t*)callback_job_create((callback_job_cb_t)roam_event,
178 (void*)(uintptr_t)(address ? 1 : 0),
179 NULL, NULL);
180 lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY);
181 }
182 }
183
184 /**
185 * Process an RTM_*ADDR message from the kernel
186 */
187 static void process_addr(private_kernel_pfroute_net_t *this,
188 struct rt_msghdr *msg)
189 {
190 struct ifa_msghdr *ifa = (struct ifa_msghdr*)msg;
191 sockaddr_t *sockaddr = (sockaddr_t*)(ifa + 1);
192 host_t *host = NULL;
193 enumerator_t *ifaces, *addrs;
194 iface_entry_t *iface;
195 addr_entry_t *addr;
196 bool found = FALSE, changed = FALSE, roam = FALSE;
197 int i;
198
199 for (i = 1; i < (1 << RTAX_MAX); i <<= 1)
200 {
201 if (ifa->ifam_addrs & i)
202 {
203 if (RTA_IFA & i)
204 {
205 host = host_create_from_sockaddr(sockaddr);
206 break;
207 }
208 sockaddr = (sockaddr_t*)((char*)sockaddr + sockaddr->sa_len);
209 }
210 }
211
212 if (!host)
213 {
214 return;
215 }
216
217 this->mutex->lock(this->mutex);
218 ifaces = this->ifaces->create_enumerator(this->ifaces);
219 while (ifaces->enumerate(ifaces, &iface))
220 {
221 if (iface->ifindex == ifa->ifam_index)
222 {
223 addrs = iface->addrs->create_enumerator(iface->addrs);
224 while (addrs->enumerate(addrs, &addr))
225 {
226 if (host->ip_equals(host, addr->ip))
227 {
228 found = TRUE;
229 if (ifa->ifam_type == RTM_DELADDR)
230 {
231 iface->addrs->remove_at(iface->addrs, addrs);
232 if (!addr->virtual)
233 {
234 changed = TRUE;
235 DBG1(DBG_KNL, "%H disappeared from %s",
236 host, iface->ifname);
237 }
238 addr_entry_destroy(addr);
239 }
240 else if (ifa->ifam_type == RTM_NEWADDR && addr->virtual)
241 {
242 addr->refcount = 1;
243 }
244 }
245 }
246 addrs->destroy(addrs);
247
248 if (!found && ifa->ifam_type == RTM_NEWADDR)
249 {
250 changed = TRUE;
251 addr = malloc_thing(addr_entry_t);
252 addr->ip = host->clone(host);
253 addr->virtual = FALSE;
254 addr->refcount = 1;
255 iface->addrs->insert_last(iface->addrs, addr);
256 DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname);
257 }
258
259 if (changed && (iface->flags & IFF_UP))
260 {
261 roam = TRUE;
262 }
263 break;
264 }
265 }
266 ifaces->destroy(ifaces);
267 this->mutex->unlock(this->mutex);
268 host->destroy(host);
269
270 if (roam)
271 {
272 fire_roam_event(this, TRUE);
273 }
274 }
275
276 /**
277 * Process an RTM_IFINFO message from the kernel
278 */
279 static void process_link(private_kernel_pfroute_net_t *this,
280 struct rt_msghdr *hdr)
281 {
282 struct if_msghdr *msg = (struct if_msghdr*)hdr;
283 enumerator_t *enumerator;
284 iface_entry_t *iface;
285 bool roam = FALSE;
286
287 if (msg->ifm_flags & IFF_LOOPBACK)
288 { /* ignore loopback interfaces */
289 return;
290 }
291
292 this->mutex->lock(this->mutex);
293 enumerator = this->ifaces->create_enumerator(this->ifaces);
294 while (enumerator->enumerate(enumerator, &iface))
295 {
296 if (iface->ifindex == msg->ifm_index)
297 {
298 if (!(iface->flags & IFF_UP) && (msg->ifm_flags & IFF_UP))
299 {
300 roam = TRUE;
301 DBG1(DBG_KNL, "interface %s activated", iface->ifname);
302 }
303 else if ((iface->flags & IFF_UP) && !(msg->ifm_flags & IFF_UP))
304 {
305 roam = TRUE;
306 DBG1(DBG_KNL, "interface %s deactivated", iface->ifname);
307 }
308 iface->flags = msg->ifm_flags;
309 break;
310 }
311 }
312 enumerator->destroy(enumerator);
313 this->mutex->unlock(this->mutex);
314
315 if (roam)
316 {
317 fire_roam_event(this, TRUE);
318 }
319 }
320
321 /**
322 * Process an RTM_*ROUTE message from the kernel
323 */
324 static void process_route(private_kernel_pfroute_net_t *this,
325 struct rt_msghdr *msg)
326 {
327
328 }
329
330 /**
331 * Receives events from kernel
332 */
333 static job_requeue_t receive_events(private_kernel_pfroute_net_t *this)
334 {
335 unsigned char buf[PFROUTE_BUFFER_SIZE];
336 struct rt_msghdr *msg = (struct rt_msghdr*)buf;
337 int len;
338 bool oldstate;
339
340 oldstate = thread_cancelability(TRUE);
341 len = recvfrom(this->socket_events, buf, sizeof(buf), 0, NULL, 0);
342 thread_cancelability(oldstate);
343
344 if (len < 0)
345 {
346 switch (errno)
347 {
348 case EINTR:
349 /* interrupted, try again */
350 return JOB_REQUEUE_DIRECT;
351 case EAGAIN:
352 /* no data ready, select again */
353 return JOB_REQUEUE_DIRECT;
354 default:
355 DBG1(DBG_KNL, "unable to receive from PF_ROUTE event socket");
356 sleep(1);
357 return JOB_REQUEUE_FAIR;
358 }
359 }
360
361 if (len < sizeof(msg->rtm_msglen) || len < msg->rtm_msglen ||
362 msg->rtm_version != RTM_VERSION)
363 {
364 DBG2(DBG_KNL, "received corrupted PF_ROUTE message");
365 return JOB_REQUEUE_DIRECT;
366 }
367
368 switch (msg->rtm_type)
369 {
370 case RTM_NEWADDR:
371 case RTM_DELADDR:
372 process_addr(this, msg);
373 break;
374 case RTM_IFINFO:
375 /*case RTM_IFANNOUNCE <- what about this*/
376 process_link(this, msg);
377 break;
378 case RTM_ADD:
379 case RTM_DELETE:
380 process_route(this, msg);
381 default:
382 break;
383 }
384
385 return JOB_REQUEUE_DIRECT;
386 }
387
388
389 /** enumerator over addresses */
390 typedef struct {
391 private_kernel_pfroute_net_t* this;
392 /** whether to enumerate down interfaces */
393 bool include_down_ifaces;
394 /** whether to enumerate virtual ip addresses */
395 bool include_virtual_ips;
396 } address_enumerator_t;
397
398 /**
399 * cleanup function for address enumerator
400 */
401 static void address_enumerator_destroy(address_enumerator_t *data)
402 {
403 data->this->mutex->unlock(data->this->mutex);
404 free(data);
405 }
406
407 /**
408 * filter for addresses
409 */
410 static bool filter_addresses(address_enumerator_t *data,
411 addr_entry_t** in, host_t** out)
412 {
413 host_t *ip;
414 if (!data->include_virtual_ips && (*in)->virtual)
415 { /* skip virtual interfaces added by us */
416 return FALSE;
417 }
418 ip = (*in)->ip;
419 if (ip->get_family(ip) == AF_INET6)
420 {
421 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ip->get_sockaddr(ip);
422 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
423 { /* skip addresses with a unusable scope */
424 return FALSE;
425 }
426 }
427 *out = ip;
428 return TRUE;
429 }
430
431 /**
432 * enumerator constructor for interfaces
433 */
434 static enumerator_t *create_iface_enumerator(iface_entry_t *iface,
435 address_enumerator_t *data)
436 {
437 return enumerator_create_filter(iface->addrs->create_enumerator(iface->addrs),
438 (void*)filter_addresses, data, NULL);
439 }
440
441 /**
442 * filter for interfaces
443 */
444 static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in,
445 iface_entry_t** out)
446 {
447 if (!data->include_down_ifaces && !((*in)->flags & IFF_UP))
448 { /* skip interfaces not up */
449 return FALSE;
450 }
451 *out = *in;
452 return TRUE;
453 }
454
455 METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
456 private_kernel_pfroute_net_t *this,
457 bool include_down_ifaces, bool include_virtual_ips)
458 {
459 address_enumerator_t *data = malloc_thing(address_enumerator_t);
460 data->this = this;
461 data->include_down_ifaces = include_down_ifaces;
462 data->include_virtual_ips = include_virtual_ips;
463
464 this->mutex->lock(this->mutex);
465 return enumerator_create_nested(
466 enumerator_create_filter(
467 this->ifaces->create_enumerator(this->ifaces),
468 (void*)filter_interfaces, data, NULL),
469 (void*)create_iface_enumerator, data,
470 (void*)address_enumerator_destroy);
471 }
472
473 METHOD(kernel_net_t, get_interface_name, char*,
474 private_kernel_pfroute_net_t *this, host_t* ip)
475 {
476 enumerator_t *ifaces, *addrs;
477 iface_entry_t *iface;
478 addr_entry_t *addr;
479 char *name = NULL;
480
481 DBG2(DBG_KNL, "getting interface name for %H", ip);
482
483 this->mutex->lock(this->mutex);
484 ifaces = this->ifaces->create_enumerator(this->ifaces);
485 while (ifaces->enumerate(ifaces, &iface))
486 {
487 addrs = iface->addrs->create_enumerator(iface->addrs);
488 while (addrs->enumerate(addrs, &addr))
489 {
490 if (ip->ip_equals(ip, addr->ip))
491 {
492 name = strdup(iface->ifname);
493 break;
494 }
495 }
496 addrs->destroy(addrs);
497 if (name)
498 {
499 break;
500 }
501 }
502 ifaces->destroy(ifaces);
503 this->mutex->unlock(this->mutex);
504
505 if (name)
506 {
507 DBG2(DBG_KNL, "%H is on interface %s", ip, name);
508 }
509 else
510 {
511 DBG2(DBG_KNL, "%H is not a local address", ip);
512 }
513 return name;
514 }
515
516 METHOD(kernel_net_t, get_source_addr, host_t*,
517 private_kernel_pfroute_net_t *this, host_t *dest, host_t *src)
518 {
519 return NULL;
520 }
521
522 METHOD(kernel_net_t, get_nexthop, host_t*,
523 private_kernel_pfroute_net_t *this, host_t *dest)
524 {
525 return NULL;
526 }
527
528 METHOD(kernel_net_t, add_ip, status_t,
529 private_kernel_pfroute_net_t *this, host_t *virtual_ip, host_t *iface_ip)
530 {
531 return FAILED;
532 }
533
534 METHOD(kernel_net_t, del_ip, status_t,
535 private_kernel_pfroute_net_t *this, host_t *virtual_ip)
536 {
537 return FAILED;
538 }
539
540 METHOD(kernel_net_t, add_route, status_t,
541 private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
542 host_t *gateway, host_t *src_ip, char *if_name)
543 {
544 return FAILED;
545 }
546
547 METHOD(kernel_net_t, del_route, status_t,
548 private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
549 host_t *gateway, host_t *src_ip, char *if_name)
550 {
551 return FAILED;
552 }
553
554 /**
555 * Initialize a list of local addresses.
556 */
557 static status_t init_address_list(private_kernel_pfroute_net_t *this)
558 {
559 struct ifaddrs *ifap, *ifa;
560 iface_entry_t *iface, *current;
561 addr_entry_t *addr;
562 enumerator_t *ifaces, *addrs;
563
564 DBG1(DBG_KNL, "listening on interfaces:");
565
566 if (getifaddrs(&ifap) < 0)
567 {
568 DBG1(DBG_KNL, " failed to get interfaces!");
569 return FAILED;
570 }
571
572 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
573 {
574 if (ifa->ifa_addr == NULL)
575 {
576 continue;
577 }
578 switch(ifa->ifa_addr->sa_family)
579 {
580 case AF_LINK:
581 case AF_INET:
582 case AF_INET6:
583 {
584 if (ifa->ifa_flags & IFF_LOOPBACK)
585 { /* ignore loopback interfaces */
586 continue;
587 }
588
589 iface = NULL;
590 ifaces = this->ifaces->create_enumerator(this->ifaces);
591 while (ifaces->enumerate(ifaces, &current))
592 {
593 if (streq(current->ifname, ifa->ifa_name))
594 {
595 iface = current;
596 break;
597 }
598 }
599 ifaces->destroy(ifaces);
600
601 if (!iface)
602 {
603 iface = malloc_thing(iface_entry_t);
604 memcpy(iface->ifname, ifa->ifa_name, IFNAMSIZ);
605 iface->ifindex = if_nametoindex(ifa->ifa_name);
606 iface->flags = ifa->ifa_flags;
607 iface->addrs = linked_list_create();
608 this->ifaces->insert_last(this->ifaces, iface);
609 }
610
611 if (ifa->ifa_addr->sa_family != AF_LINK)
612 {
613 addr = malloc_thing(addr_entry_t);
614 addr->ip = host_create_from_sockaddr(ifa->ifa_addr);
615 addr->virtual = FALSE;
616 addr->refcount = 1;
617 iface->addrs->insert_last(iface->addrs, addr);
618 }
619 }
620 }
621 }
622 freeifaddrs(ifap);
623
624 ifaces = this->ifaces->create_enumerator(this->ifaces);
625 while (ifaces->enumerate(ifaces, &iface))
626 {
627 if (iface->flags & IFF_UP)
628 {
629 DBG1(DBG_KNL, " %s", iface->ifname);
630 addrs = iface->addrs->create_enumerator(iface->addrs);
631 while (addrs->enumerate(addrs, (void**)&addr))
632 {
633 DBG1(DBG_KNL, " %H", addr->ip);
634 }
635 addrs->destroy(addrs);
636 }
637 }
638 ifaces->destroy(ifaces);
639
640 return SUCCESS;
641 }
642
643 METHOD(kernel_net_t, destroy, void,
644 private_kernel_pfroute_net_t *this)
645 {
646 if (this->socket > 0)
647 {
648 close(this->socket);
649 }
650 if (this->socket_events)
651 {
652 close(this->socket_events);
653 }
654 this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
655 this->mutex->destroy(this->mutex);
656 this->mutex_pfroute->destroy(this->mutex_pfroute);
657 free(this);
658 }
659
660 /*
661 * Described in header.
662 */
663 kernel_pfroute_net_t *kernel_pfroute_net_create()
664 {
665 private_kernel_pfroute_net_t *this;
666 bool register_for_events = TRUE;
667
668 INIT(this,
669 .public = {
670 .interface = {
671 .get_interface = _get_interface_name,
672 .create_address_enumerator = _create_address_enumerator,
673 .get_source_addr = _get_source_addr,
674 .get_nexthop = _get_nexthop,
675 .add_ip = _add_ip,
676 .del_ip = _del_ip,
677 .add_route = _add_route,
678 .del_route = _del_route,
679 .destroy = _destroy,
680 },
681 },
682 .ifaces = linked_list_create(),
683 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
684 .mutex_pfroute = mutex_create(MUTEX_TYPE_DEFAULT),
685 );
686
687 if (streq(hydra->daemon, "starter"))
688 { /* starter has no threads, so we do not register for kernel events */
689 register_for_events = FALSE;
690 }
691
692 /* create a PF_ROUTE socket to communicate with the kernel */
693 this->socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
694 if (this->socket < 0)
695 {
696 DBG1(DBG_KNL, "unable to create PF_ROUTE socket");
697 destroy(this);
698 return NULL;
699 }
700
701 if (register_for_events)
702 {
703 /* create a PF_ROUTE socket to receive events */
704 this->socket_events = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
705 if (this->socket_events < 0)
706 {
707 DBG1(DBG_KNL, "unable to create PF_ROUTE event socket");
708 destroy(this);
709 return NULL;
710 }
711
712 lib->processor->queue_job(lib->processor,
713 (job_t*)callback_job_create_with_prio(
714 (callback_job_cb_t)receive_events, this, NULL,
715 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
716 }
717
718 if (init_address_list(this) != SUCCESS)
719 {
720 DBG1(DBG_KNL, "unable to get interface list");
721 destroy(this);
722 return NULL;
723 }
724
725 return &this->public;
726 }