]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c
kernel-pfroute: Make time that is waited for VIPs to appear configurable
[people/ms/strongswan.git] / src / libhydra / plugins / kernel_pfroute / kernel_pfroute_net.c
CommitLineData
d24a74c5 1/*
05ca5655 2 * Copyright (C) 2009-2012 Tobias Brunner
d24a74c5
TB
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.
d24a74c5
TB
14 */
15
16#include <sys/types.h>
17#include <sys/socket.h>
18#include <net/if.h>
272bcac8 19#include <net/if_dl.h>
d24a74c5
TB
20#include <ifaddrs.h>
21#include <net/route.h>
22#include <unistd.h>
d24a74c5
TB
23#include <errno.h>
24
25#include "kernel_pfroute_net.h"
26
c5f7146b 27#include <hydra.h>
f05b4272 28#include <utils/debug.h>
2e7cc07e 29#include <networking/host.h>
2a2d7a4d 30#include <networking/tun_device.h>
4a5a5dd2 31#include <threading/thread.h>
eba64cef 32#include <threading/mutex.h>
3a7f4b5c 33#include <threading/condvar.h>
bdf36dac 34#include <threading/rwlock.h>
12642a68
TB
35#include <collections/hashtable.h>
36#include <collections/linked_list.h>
d24a74c5 37#include <processing/jobs/callback_job.h>
d24a74c5
TB
38
39#ifndef HAVE_STRUCT_SOCKADDR_SA_LEN
40#error Cannot compile this plugin on systems where 'struct sockaddr' has no sa_len member.
41#endif
42
aa33d2e6 43/** properly align sockaddrs */
4b3fea3d
TB
44#ifdef __APPLE__
45/* Apple always uses 4 bytes */
aa33d2e6 46#define SA_ALIGN 4
4b3fea3d
TB
47#else
48/* while on other platforms like FreeBSD it depends on the architecture */
49#define SA_ALIGN sizeof(long)
50#endif
aa33d2e6
TB
51#define SA_LEN(len) ((len) > 0 ? (((len)+SA_ALIGN-1) & ~(SA_ALIGN-1)) : SA_ALIGN)
52
ba26508d 53/** delay before firing roam events (ms) */
d24a74c5
TB
54#define ROAM_DELAY 100
55
d24a74c5
TB
56typedef struct addr_entry_t addr_entry_t;
57
58/**
59 * IP address in an inface_entry_t
60 */
61struct addr_entry_t {
7daf5226 62
d24a74c5
TB
63 /** The ip address */
64 host_t *ip;
7daf5226 65
d24a74c5
TB
66 /** virtual IP managed by us */
67 bool virtual;
d24a74c5
TB
68};
69
70/**
71 * destroy a addr_entry_t object
72 */
73static void addr_entry_destroy(addr_entry_t *this)
74{
75 this->ip->destroy(this->ip);
76 free(this);
77}
78
79typedef struct iface_entry_t iface_entry_t;
80
81/**
82 * A network interface on this system, containing addr_entry_t's
83 */
84struct iface_entry_t {
7daf5226 85
d24a74c5
TB
86 /** interface index */
87 int ifindex;
7daf5226 88
d24a74c5
TB
89 /** name of the interface */
90 char ifname[IFNAMSIZ];
7daf5226 91
d24a74c5
TB
92 /** interface flags, as in netdevice(7) SIOCGIFFLAGS */
93 u_int flags;
7daf5226 94
d24a74c5
TB
95 /** list of addresses as host_t */
96 linked_list_t *addrs;
940e1b0f
TB
97
98 /** TRUE if usable by config */
99 bool usable;
d24a74c5
TB
100};
101
102/**
103 * destroy an interface entry
104 */
105static void iface_entry_destroy(iface_entry_t *this)
106{
107 this->addrs->destroy_function(this->addrs, (void*)addr_entry_destroy);
108 free(this);
109}
110
1f97e1aa
TB
111/**
112 * check if an interface is up
113 */
114static inline bool iface_entry_up(iface_entry_t *iface)
115{
116 return (iface->flags & IFF_UP) == IFF_UP;
117}
118
940e1b0f
TB
119/**
120 * check if an interface is up and usable
121 */
122static inline bool iface_entry_up_and_usable(iface_entry_t *iface)
123{
1f97e1aa
TB
124 return iface->usable && iface_entry_up(iface);
125}
126
127typedef struct addr_map_entry_t addr_map_entry_t;
128
129/**
130 * Entry that maps an IP address to an interface entry
131 */
132struct addr_map_entry_t {
133 /** The IP address */
134 host_t *ip;
135
136 /** The interface this address is installed on */
137 iface_entry_t *iface;
138};
139
140/**
141 * Hash a addr_map_entry_t object, all entries with the same IP address
142 * are stored in the same bucket
143 */
144static u_int addr_map_entry_hash(addr_map_entry_t *this)
145{
146 return chunk_hash(this->ip->get_address(this->ip));
147}
148
149/**
150 * Compare two addr_map_entry_t objects, two entries are equal if they are
151 * installed on the same interface
152 */
153static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b)
154{
155 return a->iface->ifindex == b->iface->ifindex &&
156 a->ip->ip_equals(a->ip, b->ip);
940e1b0f
TB
157}
158
1f97e1aa
TB
159/**
160 * Used with get_match this finds an address entry if it is installed on
161 * an up and usable interface
162 */
163static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a,
164 addr_map_entry_t *b)
165{
166 return iface_entry_up_and_usable(b->iface) &&
167 a->ip->ip_equals(a->ip, b->ip);
168}
169
170/**
171 * Used with get_match this finds an address entry if it is installed on
172 * any active local interface
173 */
174static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b)
175{
176 return iface_entry_up(b->iface) && a->ip->ip_equals(a->ip, b->ip);
177}
d24a74c5
TB
178
179typedef struct private_kernel_pfroute_net_t private_kernel_pfroute_net_t;
180
181/**
182 * Private variables and functions of kernel_pfroute class.
183 */
184struct private_kernel_pfroute_net_t
185{
186 /**
187 * Public part of the kernel_pfroute_t object.
188 */
189 kernel_pfroute_net_t public;
7daf5226 190
d24a74c5 191 /**
bdf36dac 192 * lock to access lists and maps
d24a74c5 193 */
bdf36dac 194 rwlock_t *lock;
7daf5226 195
d24a74c5
TB
196 /**
197 * Cached list of interfaces and their addresses (iface_entry_t)
198 */
199 linked_list_t *ifaces;
7daf5226 200
1f97e1aa
TB
201 /**
202 * Map for IP addresses to iface_entry_t objects (addr_map_entry_t)
203 */
204 hashtable_t *addrs;
205
2a2d7a4d
MW
206 /**
207 * List of tun devices we installed for virtual IPs
208 */
209 linked_list_t *tuns;
210
d24a74c5 211 /**
3a7f4b5c 212 * mutex to communicate exclusively with PF_KEY
d24a74c5 213 */
3a7f4b5c
MW
214 mutex_t *mutex;
215
216 /**
217 * condvar to signal if PF_KEY query got a response
218 */
219 condvar_t *condvar;
220
221 /**
222 * pid to send PF_ROUTE messages with
223 */
224 pid_t pid;
7daf5226 225
d24a74c5
TB
226 /**
227 * PF_ROUTE socket to communicate with the kernel
228 */
229 int socket;
7daf5226 230
d24a74c5
TB
231 /**
232 * sequence number for messages sent to the kernel
233 */
234 int seq;
7daf5226 235
3a7f4b5c
MW
236 /**
237 * Sequence number a query is waiting for
238 */
239 int waiting_seq;
240
241 /**
242 * Allocated reply message from kernel
243 */
244 struct rt_msghdr *reply;
245
d24a74c5 246 /**
ba26508d 247 * time of last roam event
d24a74c5 248 */
de578445 249 timeval_t last_roam;
baa6419e
TB
250
251 /**
252 * Time in ms to wait for IP addresses to appear/disappear
253 */
254 int vip_wait;
d24a74c5
TB
255};
256
1f97e1aa
TB
257/**
258 * Add an address map entry
259 */
bfd2cc1c 260static void addr_map_entry_add(private_kernel_pfroute_net_t *this,
1f97e1aa
TB
261 addr_entry_t *addr, iface_entry_t *iface)
262{
263 addr_map_entry_t *entry;
264
265 if (addr->virtual)
266 { /* don't map virtual IPs */
267 return;
268 }
269
270 INIT(entry,
271 .ip = addr->ip,
272 .iface = iface,
273 );
274 entry = this->addrs->put(this->addrs, entry, entry);
275 free(entry);
276}
277
278/**
279 * Remove an address map entry (the argument order is a bit strange because
280 * it is also used with linked_list_t.invoke_function)
281 */
282static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface,
bfd2cc1c 283 private_kernel_pfroute_net_t *this)
1f97e1aa
TB
284{
285 addr_map_entry_t *entry, lookup = {
286 .ip = addr->ip,
287 .iface = iface,
288 };
289
290 if (addr->virtual)
291 { /* these are never mapped, but this check avoid problems if a virtual IP
292 * equals a regular one */
293 return;
294 }
295 entry = this->addrs->remove(this->addrs, &lookup);
296 free(entry);
297}
298
d24a74c5 299/**
ba26508d 300 * callback function that raises the delayed roam event
d24a74c5 301 */
ba26508d
TB
302static job_requeue_t roam_event(uintptr_t address)
303{
f6659688 304 hydra->kernel_interface->roam(hydra->kernel_interface, address != 0);
ba26508d
TB
305 return JOB_REQUEUE_NONE;
306}
307
308/**
309 * fire a roaming event. we delay it for a bit and fire only one event
310 * for multiple calls. otherwise we would create too many events.
311 */
312static void fire_roam_event(private_kernel_pfroute_net_t *this, bool address)
d24a74c5 313{
de578445 314 timeval_t now;
ba26508d 315 job_t *job;
7daf5226 316
de578445
MW
317 time_monotonic(&now);
318 if (timercmp(&now, &this->last_roam, >))
d24a74c5 319 {
eecd41e3 320 timeval_add_ms(&now, ROAM_DELAY);
de578445 321 this->last_roam = now;
ba26508d
TB
322
323 job = (job_t*)callback_job_create((callback_job_cb_t)roam_event,
324 (void*)(uintptr_t)(address ? 1 : 0),
325 NULL, NULL);
bb381e26 326 lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY);
d24a74c5
TB
327 }
328}
329
b1c6b68e
MW
330/**
331 * Data for enumerator over rtmsg sockaddrs
332 */
333typedef struct {
334 /** implements enumerator */
335 enumerator_t public;
336 /** copy of attribute bitfield */
337 int types;
338 /** bytes remaining in buffer */
339 int remaining;
340 /** next sockaddr to enumerate */
341 struct sockaddr *addr;
342} rt_enumerator_t;
343
344METHOD(enumerator_t, rt_enumerate, bool,
345 rt_enumerator_t *this, int *xtype, struct sockaddr **addr)
346{
347 int i, type;
348
349 if (this->remaining < sizeof(this->addr->sa_len) ||
350 this->remaining < this->addr->sa_len)
351 {
352 return FALSE;
353 }
354 for (i = 0; i < RTAX_MAX; i++)
355 {
356 type = (1 << i);
357 if (this->types & type)
358 {
359 this->types &= ~type;
360 *addr = this->addr;
361 *xtype = i;
aa33d2e6 362 this->remaining -= SA_LEN(this->addr->sa_len);
4b3fea3d
TB
363 this->addr = (struct sockaddr*)((char*)this->addr +
364 SA_LEN(this->addr->sa_len));
b1c6b68e
MW
365 return TRUE;
366 }
367 }
368 return FALSE;
369}
370
371/**
372 * Create a safe enumerator over sockaddrs in ifa/ifam/rt_msg
373 */
374static enumerator_t *create_rtmsg_enumerator(void *hdr, size_t hdrlen)
375{
376 struct rt_msghdr *rthdr = hdr;
377 rt_enumerator_t *this;
378
379 INIT(this,
380 .public = {
381 .enumerate = (void*)_rt_enumerate,
382 .destroy = (void*)free,
383 },
384 .types = rthdr->rtm_addrs,
385 .remaining = rthdr->rtm_msglen - hdrlen,
386 .addr = hdr + hdrlen,
387 );
388 return &this->public;
389}
390
d24a74c5
TB
391/**
392 * Process an RTM_*ADDR message from the kernel
393 */
394static void process_addr(private_kernel_pfroute_net_t *this,
b1c6b68e 395 struct ifa_msghdr *ifa)
d24a74c5 396{
b1c6b68e 397 struct sockaddr *sockaddr;
d24a74c5
TB
398 host_t *host = NULL;
399 enumerator_t *ifaces, *addrs;
400 iface_entry_t *iface;
401 addr_entry_t *addr;
402 bool found = FALSE, changed = FALSE, roam = FALSE;
b1c6b68e
MW
403 enumerator_t *enumerator;
404 int type;
7daf5226 405
b1c6b68e
MW
406 enumerator = create_rtmsg_enumerator(ifa, sizeof(*ifa));
407 while (enumerator->enumerate(enumerator, &type, &sockaddr))
d24a74c5 408 {
b1c6b68e 409 if (type == RTAX_IFA)
d24a74c5 410 {
b1c6b68e
MW
411 host = host_create_from_sockaddr(sockaddr);
412 break;
d24a74c5
TB
413 }
414 }
b1c6b68e 415 enumerator->destroy(enumerator);
7daf5226 416
d24a74c5
TB
417 if (!host)
418 {
419 return;
420 }
7daf5226 421
bdf36dac 422 this->lock->write_lock(this->lock);
d24a74c5
TB
423 ifaces = this->ifaces->create_enumerator(this->ifaces);
424 while (ifaces->enumerate(ifaces, &iface))
425 {
426 if (iface->ifindex == ifa->ifam_index)
427 {
428 addrs = iface->addrs->create_enumerator(iface->addrs);
429 while (addrs->enumerate(addrs, &addr))
430 {
431 if (host->ip_equals(host, addr->ip))
432 {
433 found = TRUE;
434 if (ifa->ifam_type == RTM_DELADDR)
435 {
436 iface->addrs->remove_at(iface->addrs, addrs);
940e1b0f 437 if (!addr->virtual && iface->usable)
d24a74c5
TB
438 {
439 changed = TRUE;
440 DBG1(DBG_KNL, "%H disappeared from %s",
441 host, iface->ifname);
442 }
1f97e1aa 443 addr_map_entry_remove(addr, iface, this);
d24a74c5
TB
444 addr_entry_destroy(addr);
445 }
d24a74c5
TB
446 }
447 }
448 addrs->destroy(addrs);
7daf5226 449
d24a74c5
TB
450 if (!found && ifa->ifam_type == RTM_NEWADDR)
451 {
9650bf3c
MW
452 INIT(addr,
453 .ip = host->clone(host),
9650bf3c 454 );
d24a74c5 455 changed = TRUE;
d24a74c5 456 iface->addrs->insert_last(iface->addrs, addr);
1f97e1aa 457 addr_map_entry_add(this, addr, iface);
940e1b0f
TB
458 if (iface->usable)
459 {
460 DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname);
461 }
d24a74c5 462 }
7daf5226 463
940e1b0f 464 if (changed && iface_entry_up_and_usable(iface))
d24a74c5
TB
465 {
466 roam = TRUE;
467 }
468 break;
469 }
470 }
471 ifaces->destroy(ifaces);
bdf36dac 472 this->lock->unlock(this->lock);
d24a74c5 473 host->destroy(host);
7daf5226 474
d24a74c5
TB
475 if (roam)
476 {
ba26508d 477 fire_roam_event(this, TRUE);
d24a74c5
TB
478 }
479}
480
6e879a59
MW
481/**
482 * Re-initialize address list of an interface if it changes state
483 */
484static void repopulate_iface(private_kernel_pfroute_net_t *this,
485 iface_entry_t *iface)
486{
487 struct ifaddrs *ifap, *ifa;
488 addr_entry_t *addr;
489
490 while (iface->addrs->remove_last(iface->addrs, (void**)&addr) == SUCCESS)
491 {
492 addr_map_entry_remove(addr, iface, this);
493 addr_entry_destroy(addr);
494 }
495
496 if (getifaddrs(&ifap) == 0)
497 {
498 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
499 {
500 if (ifa->ifa_addr && streq(ifa->ifa_name, iface->ifname))
501 {
502 switch (ifa->ifa_addr->sa_family)
503 {
504 case AF_INET:
505 case AF_INET6:
506 INIT(addr,
507 .ip = host_create_from_sockaddr(ifa->ifa_addr),
6e879a59
MW
508 );
509 iface->addrs->insert_last(iface->addrs, addr);
510 addr_map_entry_add(this, addr, iface);
511 break;
512 default:
513 break;
514 }
515 }
516 }
517 freeifaddrs(ifap);
518 }
519}
520
d24a74c5
TB
521/**
522 * Process an RTM_IFINFO message from the kernel
523 */
524static void process_link(private_kernel_pfroute_net_t *this,
b1c6b68e 525 struct if_msghdr *msg)
d24a74c5 526{
d24a74c5
TB
527 enumerator_t *enumerator;
528 iface_entry_t *iface;
f58f8bf4 529 bool roam = FALSE, found = FALSE;
7daf5226 530
bdf36dac 531 this->lock->write_lock(this->lock);
d24a74c5
TB
532 enumerator = this->ifaces->create_enumerator(this->ifaces);
533 while (enumerator->enumerate(enumerator, &iface))
534 {
535 if (iface->ifindex == msg->ifm_index)
536 {
940e1b0f 537 if (iface->usable)
d24a74c5 538 {
940e1b0f
TB
539 if (!(iface->flags & IFF_UP) && (msg->ifm_flags & IFF_UP))
540 {
541 roam = TRUE;
542 DBG1(DBG_KNL, "interface %s activated", iface->ifname);
543 }
544 else if ((iface->flags & IFF_UP) && !(msg->ifm_flags & IFF_UP))
545 {
546 roam = TRUE;
547 DBG1(DBG_KNL, "interface %s deactivated", iface->ifname);
548 }
d24a74c5
TB
549 }
550 iface->flags = msg->ifm_flags;
6e879a59 551 repopulate_iface(this, iface);
0fd409db 552 found = TRUE;
d24a74c5
TB
553 break;
554 }
555 }
556 enumerator->destroy(enumerator);
0fd409db
MW
557
558 if (!found)
559 {
560 INIT(iface,
561 .ifindex = msg->ifm_index,
562 .flags = msg->ifm_flags,
563 .addrs = linked_list_create(),
564 );
565 if (if_indextoname(iface->ifindex, iface->ifname))
566 {
567 DBG1(DBG_KNL, "interface %s appeared", iface->ifname);
568 iface->usable = hydra->kernel_interface->is_interface_usable(
569 hydra->kernel_interface, iface->ifname);
6e879a59 570 repopulate_iface(this, iface);
0fd409db
MW
571 this->ifaces->insert_last(this->ifaces, iface);
572 }
573 else
574 {
575 free(iface);
576 }
577 }
bdf36dac 578 this->lock->unlock(this->lock);
7daf5226 579
d24a74c5
TB
580 if (roam)
581 {
ba26508d 582 fire_roam_event(this, TRUE);
d24a74c5
TB
583 }
584}
585
586/**
587 * Process an RTM_*ROUTE message from the kernel
588 */
589static void process_route(private_kernel_pfroute_net_t *this,
590 struct rt_msghdr *msg)
591{
592
593}
594
595/**
b1c6b68e 596 * Receives PF_ROUTE messages from kernel
d24a74c5
TB
597 */
598static job_requeue_t receive_events(private_kernel_pfroute_net_t *this)
599{
b1c6b68e
MW
600 struct {
601 union {
602 struct rt_msghdr rtm;
603 struct if_msghdr ifm;
604 struct ifa_msghdr ifam;
605 };
606 char buf[sizeof(struct sockaddr_storage) * RTAX_MAX];
607 } msg;
608 int len, hdrlen;
4a5a5dd2 609 bool oldstate;
7daf5226 610
4a5a5dd2 611 oldstate = thread_cancelability(TRUE);
b1c6b68e 612 len = recv(this->socket, &msg, sizeof(msg), 0);
4a5a5dd2 613 thread_cancelability(oldstate);
7daf5226 614
d24a74c5
TB
615 if (len < 0)
616 {
617 switch (errno)
618 {
619 case EINTR:
d24a74c5 620 case EAGAIN:
d24a74c5
TB
621 return JOB_REQUEUE_DIRECT;
622 default:
623 DBG1(DBG_KNL, "unable to receive from PF_ROUTE event socket");
624 sleep(1);
625 return JOB_REQUEUE_FAIR;
626 }
627 }
7daf5226 628
b1c6b68e 629 if (len < offsetof(struct rt_msghdr, rtm_flags) || len < msg.rtm.rtm_msglen)
d24a74c5 630 {
b1c6b68e 631 DBG1(DBG_KNL, "received invalid PF_ROUTE message");
d24a74c5
TB
632 return JOB_REQUEUE_DIRECT;
633 }
b1c6b68e
MW
634 if (msg.rtm.rtm_version != RTM_VERSION)
635 {
636 DBG1(DBG_KNL, "received PF_ROUTE message with unsupported version: %d",
637 msg.rtm.rtm_version);
638 return JOB_REQUEUE_DIRECT;
639 }
640 switch (msg.rtm.rtm_type)
d24a74c5
TB
641 {
642 case RTM_NEWADDR:
643 case RTM_DELADDR:
b1c6b68e 644 hdrlen = sizeof(msg.ifam);
d24a74c5
TB
645 break;
646 case RTM_IFINFO:
b1c6b68e 647 hdrlen = sizeof(msg.ifm);
d24a74c5
TB
648 break;
649 case RTM_ADD:
650 case RTM_DELETE:
b1c6b68e
MW
651 case RTM_GET:
652 hdrlen = sizeof(msg.rtm);
653 break;
654 default:
655 return JOB_REQUEUE_DIRECT;
656 }
657 if (msg.rtm.rtm_msglen < hdrlen)
658 {
659 DBG1(DBG_KNL, "ignoring short PF_ROUTE message");
660 return JOB_REQUEUE_DIRECT;
661 }
662 switch (msg.rtm.rtm_type)
663 {
664 case RTM_NEWADDR:
665 case RTM_DELADDR:
666 process_addr(this, &msg.ifam);
667 break;
668 case RTM_IFINFO:
669 process_link(this, &msg.ifm);
670 break;
671 case RTM_ADD:
672 case RTM_DELETE:
673 process_route(this, &msg.rtm);
674 break;
d24a74c5
TB
675 default:
676 break;
677 }
3a7f4b5c
MW
678
679 this->mutex->lock(this->mutex);
680 if (msg.rtm.rtm_pid == this->pid && msg.rtm.rtm_seq == this->waiting_seq)
681 {
682 /* seems like the message someone is waiting for, deliver */
683 this->reply = realloc(this->reply, msg.rtm.rtm_msglen);
684 memcpy(this->reply, &msg, msg.rtm.rtm_msglen);
3a7f4b5c 685 }
2a2d7a4d 686 /* signal on any event, add_ip()/del_ip() might wait for it */
c9a323c1 687 this->condvar->broadcast(this->condvar);
3a7f4b5c
MW
688 this->mutex->unlock(this->mutex);
689
d24a74c5
TB
690 return JOB_REQUEUE_DIRECT;
691}
692
693
694/** enumerator over addresses */
695typedef struct {
696 private_kernel_pfroute_net_t* this;
4106aea8 697 /** which addresses to enumerate */
bfd2cc1c 698 kernel_address_type_t which;
d24a74c5
TB
699} address_enumerator_t;
700
701/**
702 * cleanup function for address enumerator
703 */
704static void address_enumerator_destroy(address_enumerator_t *data)
705{
bdf36dac 706 data->this->lock->unlock(data->this->lock);
d24a74c5
TB
707 free(data);
708}
709
710/**
711 * filter for addresses
712 */
e131f117
MW
713static bool filter_addresses(address_enumerator_t *data,
714 addr_entry_t** in, host_t** out)
d24a74c5
TB
715{
716 host_t *ip;
4106aea8 717 if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->virtual)
d24a74c5 718 { /* skip virtual interfaces added by us */
1a2a8bff
MW
719 return FALSE;
720 }
721 if (!(data->which & ADDR_TYPE_REGULAR) && !(*in)->virtual)
722 { /* address is regular, but not requested */
d24a74c5
TB
723 return FALSE;
724 }
725 ip = (*in)->ip;
726 if (ip->get_family(ip) == AF_INET6)
727 {
728 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ip->get_sockaddr(ip);
729 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
730 { /* skip addresses with a unusable scope */
731 return FALSE;
732 }
733 }
734 *out = ip;
735 return TRUE;
736}
737
738/**
739 * enumerator constructor for interfaces
740 */
e131f117
MW
741static enumerator_t *create_iface_enumerator(iface_entry_t *iface,
742 address_enumerator_t *data)
d24a74c5
TB
743{
744 return enumerator_create_filter(iface->addrs->create_enumerator(iface->addrs),
745 (void*)filter_addresses, data, NULL);
746}
747
748/**
749 * filter for interfaces
750 */
e131f117
MW
751static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in,
752 iface_entry_t** out)
d24a74c5 753{
4106aea8 754 if (!(data->which & ADDR_TYPE_IGNORED) && !(*in)->usable)
940e1b0f
TB
755 { /* skip interfaces excluded by config */
756 return FALSE;
757 }
4106aea8 758 if (!(data->which & ADDR_TYPE_LOOPBACK) && ((*in)->flags & IFF_LOOPBACK))
aed33805
TB
759 { /* ignore loopback devices */
760 return FALSE;
761 }
4106aea8
TB
762 if (!(data->which & ADDR_TYPE_DOWN) && !((*in)->flags & IFF_UP))
763 { /* skip interfaces not up */
d24a74c5
TB
764 return FALSE;
765 }
766 *out = *in;
767 return TRUE;
768}
769
e131f117 770METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
bfd2cc1c 771 private_kernel_pfroute_net_t *this, kernel_address_type_t which)
d24a74c5 772{
9650bf3c
MW
773 address_enumerator_t *data;
774
775 INIT(data,
776 .this = this,
777 .which = which,
778 );
d24a74c5 779
bdf36dac 780 this->lock->read_lock(this->lock);
d24a74c5 781 return enumerator_create_nested(
e131f117
MW
782 enumerator_create_filter(
783 this->ifaces->create_enumerator(this->ifaces),
784 (void*)filter_interfaces, data, NULL),
785 (void*)create_iface_enumerator, data,
786 (void*)address_enumerator_destroy);
d24a74c5
TB
787}
788
580b768d
MW
789METHOD(kernel_net_t, get_features, kernel_feature_t,
790 private_kernel_pfroute_net_t *this)
791{
792 return KERNEL_REQUIRE_EXCLUDE_ROUTE;
793}
794
9ba36c0f
TB
795METHOD(kernel_net_t, get_interface_name, bool,
796 private_kernel_pfroute_net_t *this, host_t* ip, char **name)
d24a74c5 797{
1f97e1aa
TB
798 addr_map_entry_t *entry, lookup = {
799 .ip = ip,
800 };
d24a74c5 801
645d7a5e
TB
802 if (ip->is_anyaddr(ip))
803 {
804 return FALSE;
805 }
bdf36dac 806 this->lock->read_lock(this->lock);
1f97e1aa
TB
807 /* first try to find it on an up and usable interface */
808 entry = this->addrs->get_match(this->addrs, &lookup,
809 (void*)addr_map_entry_match_up_and_usable);
810 if (entry)
d24a74c5 811 {
1f97e1aa 812 if (name)
940e1b0f 813 {
1f97e1aa 814 *name = strdup(entry->iface->ifname);
940e1b0f
TB
815 DBG2(DBG_KNL, "%H is on interface %s", ip, *name);
816 }
bdf36dac 817 this->lock->unlock(this->lock);
1f97e1aa 818 return TRUE;
d24a74c5 819 }
1f97e1aa
TB
820 /* maybe it is installed on an ignored interface */
821 entry = this->addrs->get_match(this->addrs, &lookup,
822 (void*)addr_map_entry_match_up);
823 if (!entry)
824 { /* the address does not exist, is on a down interface */
825 DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip);
826 }
bdf36dac 827 this->lock->unlock(this->lock);
1f97e1aa 828 return FALSE;
d24a74c5
TB
829}
830
e131f117 831METHOD(kernel_net_t, add_ip, status_t,
2a2d7a4d 832 private_kernel_pfroute_net_t *this, host_t *vip, int prefix,
77b6f196 833 char *ifname)
d24a74c5 834{
77b6f196
MW
835 enumerator_t *ifaces, *addrs;
836 iface_entry_t *iface;
837 addr_entry_t *addr;
2a2d7a4d
MW
838 tun_device_t *tun;
839 bool timeout = FALSE;
840
841 tun = tun_device_create(NULL);
842 if (!tun)
843 {
844 return FAILED;
845 }
846 if (prefix == -1)
847 {
848 prefix = vip->get_address(vip).len * 8;
849 }
93e4df37 850 if (!tun->up(tun) || !tun->set_address(tun, vip, prefix))
2a2d7a4d
MW
851 {
852 tun->destroy(tun);
853 return FAILED;
854 }
855
856 /* wait until address appears */
857 this->mutex->lock(this->mutex);
858 while (!timeout && !get_interface_name(this, vip, NULL))
859 {
baa6419e
TB
860 timeout = this->condvar->timed_wait(this->condvar, this->mutex,
861 this->vip_wait);
2a2d7a4d
MW
862 }
863 this->mutex->unlock(this->mutex);
864 if (timeout)
865 {
866 DBG1(DBG_KNL, "virtual IP %H did not appear on %s",
867 vip, tun->get_name(tun));
868 tun->destroy(tun);
869 return FAILED;
870 }
871
872 this->lock->write_lock(this->lock);
873 this->tuns->insert_last(this->tuns, tun);
77b6f196
MW
874
875 ifaces = this->ifaces->create_enumerator(this->ifaces);
876 while (ifaces->enumerate(ifaces, &iface))
877 {
878 if (streq(iface->ifname, tun->get_name(tun)))
879 {
880 addrs = iface->addrs->create_enumerator(iface->addrs);
881 while (addrs->enumerate(addrs, &addr))
882 {
883 if (addr->ip->ip_equals(addr->ip, vip))
884 {
885 addr->virtual = TRUE;
77b6f196
MW
886 }
887 }
888 addrs->destroy(addrs);
889 }
890 }
891 ifaces->destroy(ifaces);
554c4276
TB
892 /* lets do this while holding the lock, thus preventing another thread
893 * from deleting the TUN device concurrently, hopefully listeneres are quick
894 * and cause no deadlocks */
895 hydra->kernel_interface->tun(hydra->kernel_interface, tun, TRUE);
2a2d7a4d
MW
896 this->lock->unlock(this->lock);
897
898 return SUCCESS;
d24a74c5
TB
899}
900
e131f117 901METHOD(kernel_net_t, del_ip, status_t,
2a2d7a4d 902 private_kernel_pfroute_net_t *this, host_t *vip, int prefix,
d88597f0 903 bool wait)
d24a74c5 904{
2a2d7a4d
MW
905 enumerator_t *enumerator;
906 tun_device_t *tun;
907 host_t *addr;
908 bool timeout = FALSE, found = FALSE;
909
910 this->lock->write_lock(this->lock);
911 enumerator = this->tuns->create_enumerator(this->tuns);
912 while (enumerator->enumerate(enumerator, &tun))
913 {
914 addr = tun->get_address(tun, NULL);
915 if (addr && addr->ip_equals(addr, vip))
916 {
917 this->tuns->remove_at(this->tuns, enumerator);
554c4276
TB
918 hydra->kernel_interface->tun(hydra->kernel_interface, tun,
919 FALSE);
2a2d7a4d
MW
920 tun->destroy(tun);
921 found = TRUE;
922 break;
923 }
924 }
925 enumerator->destroy(enumerator);
926 this->lock->unlock(this->lock);
927
928 if (!found)
929 {
930 return NOT_FOUND;
931 }
932 /* wait until address disappears */
933 if (wait)
934 {
935 this->mutex->lock(this->mutex);
936 while (!timeout && get_interface_name(this, vip, NULL))
937 {
baa6419e
TB
938 timeout = this->condvar->timed_wait(this->condvar, this->mutex,
939 this->vip_wait);
2a2d7a4d
MW
940 }
941 this->mutex->unlock(this->mutex);
942 if (timeout)
943 {
944 DBG1(DBG_KNL, "virtual IP %H did not disappear from tun", vip);
945 return FAILED;
946 }
947 }
948 return SUCCESS;
d24a74c5
TB
949}
950
272bcac8
MW
951/**
952 * Append a sockaddr_in/in6 of given type to routing message
953 */
954static void add_rt_addr(struct rt_msghdr *hdr, int type, host_t *addr)
955{
956 if (addr)
957 {
958 int len;
959
960 len = *addr->get_sockaddr_len(addr);
961 memcpy((char*)hdr + hdr->rtm_msglen, addr->get_sockaddr(addr), len);
aa33d2e6 962 hdr->rtm_msglen += SA_LEN(len);
272bcac8
MW
963 hdr->rtm_addrs |= type;
964 }
965}
966
967/**
968 * Append a subnet mask sockaddr using the given prefix to routing message
969 */
970static void add_rt_mask(struct rt_msghdr *hdr, int type, int family, int prefix)
971{
972 host_t *mask;
973
974 mask = host_create_netmask(family, prefix);
975 if (mask)
976 {
977 add_rt_addr(hdr, type, mask);
978 mask->destroy(mask);
979 }
980}
981
982/**
983 * Append an interface name sockaddr_dl to routing message
984 */
985static void add_rt_ifname(struct rt_msghdr *hdr, int type, char *name)
986{
987 struct sockaddr_dl sdl = {
988 .sdl_len = sizeof(struct sockaddr_dl),
989 .sdl_family = AF_LINK,
990 .sdl_nlen = strlen(name),
991 };
992
993 if (strlen(name) <= sizeof(sdl.sdl_data))
994 {
995 memcpy(sdl.sdl_data, name, sdl.sdl_nlen);
996 memcpy((char*)hdr + hdr->rtm_msglen, &sdl, sdl.sdl_len);
aa33d2e6 997 hdr->rtm_msglen += SA_LEN(sdl.sdl_len);
272bcac8
MW
998 hdr->rtm_addrs |= type;
999 }
1000}
1001
1002/**
1003 * Add or remove a route
1004 */
1005static status_t manage_route(private_kernel_pfroute_net_t *this, int op,
1006 chunk_t dst_net, u_int8_t prefixlen,
1007 host_t *gateway, char *if_name)
1008{
1009 struct {
1010 struct rt_msghdr hdr;
1011 char buf[sizeof(struct sockaddr_storage) * RTAX_MAX];
1012 } msg = {
1013 .hdr = {
1014 .rtm_version = RTM_VERSION,
1015 .rtm_type = op,
1016 .rtm_flags = RTF_UP | RTF_STATIC,
1017 .rtm_pid = this->pid,
1018 .rtm_seq = ++this->seq,
1019 },
1020 };
1021 host_t *dst;
1022 int type;
1023
12178303
MW
1024 if (prefixlen == 0 && dst_net.len)
1025 {
1026 status_t status;
1027 chunk_t half;
1028
1029 half = chunk_clonea(dst_net);
1030 half.ptr[0] |= 0x80;
1031 prefixlen = 1;
1032 status = manage_route(this, op, half, prefixlen, gateway, if_name);
1033 if (status != SUCCESS)
1034 {
1035 return status;
1036 }
1037 }
1038
272bcac8
MW
1039 dst = host_create_from_chunk(AF_UNSPEC, dst_net, 0);
1040 if (!dst)
1041 {
1042 return FAILED;
1043 }
1044
1045 if ((dst->get_family(dst) == AF_INET && prefixlen == 32) ||
1046 (dst->get_family(dst) == AF_INET6 && prefixlen == 128))
1047 {
1048 msg.hdr.rtm_flags |= RTF_HOST | RTF_GATEWAY;
1049 }
1050
1051 msg.hdr.rtm_msglen = sizeof(struct rt_msghdr);
1052 for (type = 0; type < RTAX_MAX; type++)
1053 {
1054 switch (type)
1055 {
1056 case RTAX_DST:
1057 add_rt_addr(&msg.hdr, RTA_DST, dst);
1058 break;
1059 case RTAX_NETMASK:
1060 if (!(msg.hdr.rtm_flags & RTF_HOST))
1061 {
1062 add_rt_mask(&msg.hdr, RTA_NETMASK,
1063 dst->get_family(dst), prefixlen);
1064 }
1065 break;
f58f8bf4 1066 case RTAX_IFP:
272bcac8
MW
1067 if (if_name)
1068 {
f58f8bf4 1069 add_rt_ifname(&msg.hdr, RTA_IFP, if_name);
272bcac8 1070 }
f58f8bf4
TB
1071 break;
1072 case RTAX_GATEWAY:
1073 if (gateway)
272bcac8
MW
1074 {
1075 add_rt_addr(&msg.hdr, RTA_GATEWAY, gateway);
1076 }
1077 break;
1078 default:
1079 break;
1080 }
1081 }
1082 dst->destroy(dst);
1083
1084 if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) != msg.hdr.rtm_msglen)
1085 {
1086 DBG1(DBG_KNL, "%s PF_ROUTE route failed: %s",
1087 op == RTM_ADD ? "adding" : "deleting", strerror(errno));
1088 return FAILED;
1089 }
1090 return SUCCESS;
1091}
1092
e131f117
MW
1093METHOD(kernel_net_t, add_route, status_t,
1094 private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
1095 host_t *gateway, host_t *src_ip, char *if_name)
d24a74c5 1096{
272bcac8 1097 return manage_route(this, RTM_ADD, dst_net, prefixlen, gateway, if_name);
d24a74c5
TB
1098}
1099
e131f117
MW
1100METHOD(kernel_net_t, del_route, status_t,
1101 private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
1102 host_t *gateway, host_t *src_ip, char *if_name)
d24a74c5 1103{
272bcac8 1104 return manage_route(this, RTM_DELETE, dst_net, prefixlen, gateway, if_name);
d24a74c5
TB
1105}
1106
d6c17e96
TB
1107/**
1108 * Do a route lookup for dest and return either the nexthop or the source
1109 * address.
1110 */
1111static host_t *get_route(private_kernel_pfroute_net_t *this, bool nexthop,
1112 host_t *dest, host_t *src)
9bc342ea
MW
1113{
1114 struct {
1115 struct rt_msghdr hdr;
1116 char buf[sizeof(struct sockaddr_storage) * RTAX_MAX];
1117 } msg = {
1118 .hdr = {
1119 .rtm_version = RTM_VERSION,
1120 .rtm_type = RTM_GET,
1121 .rtm_pid = this->pid,
1122 .rtm_seq = ++this->seq,
1123 },
1124 };
12488efa 1125 host_t *host = NULL;
9bc342ea
MW
1126 enumerator_t *enumerator;
1127 struct sockaddr *addr;
dc8b083d 1128 bool failed = FALSE;
9bc342ea
MW
1129 int type;
1130
dc8b083d 1131retry:
9bc342ea
MW
1132 msg.hdr.rtm_msglen = sizeof(struct rt_msghdr);
1133 for (type = 0; type < RTAX_MAX; type++)
1134 {
1135 switch (type)
1136 {
1137 case RTAX_DST:
1138 add_rt_addr(&msg.hdr, RTA_DST, dest);
1139 break;
1140 case RTAX_IFA:
1141 add_rt_addr(&msg.hdr, RTA_IFA, src);
1142 break;
1c697ff1
TB
1143 case RTAX_IFP:
1144 if (!nexthop)
1145 { /* add an empty IFP to ensure we get a source address */
1146 add_rt_ifname(&msg.hdr, RTA_IFP, "");
1147 }
1148 break;
9bc342ea
MW
1149 default:
1150 break;
1151 }
1152 }
1153 this->mutex->lock(this->mutex);
1154
c9a323c1
MW
1155 while (this->waiting_seq)
1156 {
1157 this->condvar->wait(this->condvar, this->mutex);
1158 }
9bc342ea
MW
1159 this->waiting_seq = msg.hdr.rtm_seq;
1160 if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) == msg.hdr.rtm_msglen)
1161 {
1162 while (TRUE)
1163 {
1164 if (this->condvar->timed_wait(this->condvar, this->mutex, 1000))
1165 { /* timed out? */
1166 break;
1167 }
1168 if (this->reply->rtm_msglen < sizeof(*this->reply) ||
1169 msg.hdr.rtm_seq != this->reply->rtm_seq)
1170 {
1171 continue;
1172 }
1173 enumerator = create_rtmsg_enumerator(this->reply,
1174 sizeof(*this->reply));
1175 while (enumerator->enumerate(enumerator, &type, &addr))
1176 {
12488efa 1177 if (nexthop)
b0629f7d 1178 {
12488efa
TB
1179 if (type == RTAX_DST && this->reply->rtm_flags & RTF_HOST)
1180 { /* probably a cloned/cached direct route, only use that
1181 * as fallback if no gateway is found */
1182 host = host ?: host_create_from_sockaddr(addr);
1183 }
1184 if (type == RTAX_GATEWAY)
1185 { /* could actually be a MAC address */
1186 host_t *gtw = host_create_from_sockaddr(addr);
1187 if (gtw)
1188 {
1189 DESTROY_IF(host);
1190 host = gtw;
1191 }
1192 }
34b0ad06 1193 }
12488efa 1194 else
d6c17e96 1195 {
12488efa 1196 if (type == RTAX_IFA)
b0629f7d 1197 {
12488efa 1198 host = host_create_from_sockaddr(addr);
b0629f7d 1199 }
9bc342ea
MW
1200 }
1201 }
1202 enumerator->destroy(enumerator);
1203 break;
1204 }
1205 }
1206 else
1207 {
dc8b083d 1208 failed = TRUE;
9bc342ea 1209 }
c9a323c1
MW
1210 /* signal completion of query to a waiting thread */
1211 this->waiting_seq = 0;
1212 this->condvar->signal(this->condvar);
9bc342ea
MW
1213 this->mutex->unlock(this->mutex);
1214
dc8b083d
TB
1215 if (failed)
1216 {
1217 if (src)
1218 { /* the given source address might be gone, try again without */
1219 src = NULL;
1220 msg.hdr.rtm_seq = ++this->seq;
1221 msg.hdr.rtm_addrs = 0;
1222 memset(msg.buf, sizeof(msg.buf), 0);
1223 goto retry;
1224 }
1225 DBG1(DBG_KNL, "PF_ROUTE lookup failed: %s", strerror(errno));
1226 }
1227
b0629f7d
TB
1228 if (host)
1229 {
1230 DBG2(DBG_KNL, "using %H as %s to reach %H", host,
1231 nexthop ? "nexthop" : "address", dest);
1232 }
d6c17e96
TB
1233 return host;
1234}
1235
1236METHOD(kernel_net_t, get_source_addr, host_t*,
1237 private_kernel_pfroute_net_t *this, host_t *dest, host_t *src)
1238{
1239 return get_route(this, FALSE, dest, src);
1240}
1241
1242METHOD(kernel_net_t, get_nexthop, host_t*,
1243 private_kernel_pfroute_net_t *this, host_t *dest, host_t *src)
1244{
1245 return get_route(this, TRUE, dest, src);
9bc342ea
MW
1246}
1247
d24a74c5
TB
1248/**
1249 * Initialize a list of local addresses.
1250 */
1251static status_t init_address_list(private_kernel_pfroute_net_t *this)
1252{
1253 struct ifaddrs *ifap, *ifa;
1254 iface_entry_t *iface, *current;
1255 addr_entry_t *addr;
1256 enumerator_t *ifaces, *addrs;
7daf5226 1257
31a0e24b 1258 DBG2(DBG_KNL, "known interfaces and IP addresses:");
7daf5226 1259
d24a74c5
TB
1260 if (getifaddrs(&ifap) < 0)
1261 {
1262 DBG1(DBG_KNL, " failed to get interfaces!");
1263 return FAILED;
1264 }
7daf5226 1265
d24a74c5
TB
1266 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
1267 {
1268 if (ifa->ifa_addr == NULL)
1269 {
1270 continue;
1271 }
1272 switch(ifa->ifa_addr->sa_family)
1273 {
1274 case AF_LINK:
1275 case AF_INET:
1276 case AF_INET6:
1277 {
d24a74c5
TB
1278 iface = NULL;
1279 ifaces = this->ifaces->create_enumerator(this->ifaces);
1280 while (ifaces->enumerate(ifaces, &current))
1281 {
1282 if (streq(current->ifname, ifa->ifa_name))
1283 {
1284 iface = current;
1285 break;
1286 }
1287 }
1288 ifaces->destroy(ifaces);
7daf5226 1289
d24a74c5
TB
1290 if (!iface)
1291 {
9650bf3c
MW
1292 INIT(iface,
1293 .ifindex = if_nametoindex(ifa->ifa_name),
1294 .flags = ifa->ifa_flags,
1295 .addrs = linked_list_create(),
1296 .usable = hydra->kernel_interface->is_interface_usable(
1297 hydra->kernel_interface, ifa->ifa_name),
1298 );
d24a74c5 1299 memcpy(iface->ifname, ifa->ifa_name, IFNAMSIZ);
d24a74c5
TB
1300 this->ifaces->insert_last(this->ifaces, iface);
1301 }
7daf5226 1302
d24a74c5
TB
1303 if (ifa->ifa_addr->sa_family != AF_LINK)
1304 {
9650bf3c
MW
1305 INIT(addr,
1306 .ip = host_create_from_sockaddr(ifa->ifa_addr),
9650bf3c 1307 );
d24a74c5 1308 iface->addrs->insert_last(iface->addrs, addr);
9845391a 1309 addr_map_entry_add(this, addr, iface);
d24a74c5
TB
1310 }
1311 }
1312 }
1313 }
1314 freeifaddrs(ifap);
7daf5226 1315
d24a74c5
TB
1316 ifaces = this->ifaces->create_enumerator(this->ifaces);
1317 while (ifaces->enumerate(ifaces, &iface))
1318 {
940e1b0f 1319 if (iface->usable && iface->flags & IFF_UP)
d24a74c5 1320 {
31a0e24b 1321 DBG2(DBG_KNL, " %s", iface->ifname);
d24a74c5
TB
1322 addrs = iface->addrs->create_enumerator(iface->addrs);
1323 while (addrs->enumerate(addrs, (void**)&addr))
1324 {
31a0e24b 1325 DBG2(DBG_KNL, " %H", addr->ip);
d24a74c5
TB
1326 }
1327 addrs->destroy(addrs);
1328 }
1329 }
1330 ifaces->destroy(ifaces);
7daf5226 1331
d24a74c5
TB
1332 return SUCCESS;
1333}
1334
cce8f652 1335METHOD(kernel_net_t, destroy, void,
e131f117 1336 private_kernel_pfroute_net_t *this)
d24a74c5 1337{
1f97e1aa 1338 enumerator_t *enumerator;
bfd2cc1c 1339 addr_entry_t *addr;
1f97e1aa 1340
0e107f03 1341 if (this->socket != -1)
d6a27ec6
MW
1342 {
1343 close(this->socket);
1344 }
1f97e1aa
TB
1345 enumerator = this->addrs->create_enumerator(this->addrs);
1346 while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
1347 {
1348 free(addr);
1349 }
1350 enumerator->destroy(enumerator);
1351 this->addrs->destroy(this->addrs);
d24a74c5 1352 this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
2a2d7a4d 1353 this->tuns->destroy(this->tuns);
bdf36dac 1354 this->lock->destroy(this->lock);
3a7f4b5c
MW
1355 this->mutex->destroy(this->mutex);
1356 this->condvar->destroy(this->condvar);
1357 free(this->reply);
d24a74c5
TB
1358 free(this);
1359}
1360
1361/*
1362 * Described in header.
1363 */
1364kernel_pfroute_net_t *kernel_pfroute_net_create()
1365{
e131f117
MW
1366 private_kernel_pfroute_net_t *this;
1367
1368 INIT(this,
1369 .public = {
1370 .interface = {
580b768d 1371 .get_features = _get_features,
e131f117
MW
1372 .get_interface = _get_interface_name,
1373 .create_address_enumerator = _create_address_enumerator,
1374 .get_source_addr = _get_source_addr,
1375 .get_nexthop = _get_nexthop,
1376 .add_ip = _add_ip,
1377 .del_ip = _del_ip,
1378 .add_route = _add_route,
1379 .del_route = _del_route,
1380 .destroy = _destroy,
1381 },
1382 },
3a7f4b5c 1383 .pid = getpid(),
e131f117 1384 .ifaces = linked_list_create(),
1f97e1aa
TB
1385 .addrs = hashtable_create(
1386 (hashtable_hash_t)addr_map_entry_hash,
1387 (hashtable_equals_t)addr_map_entry_equals, 16),
2a2d7a4d 1388 .tuns = linked_list_create(),
bdf36dac 1389 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
3a7f4b5c
MW
1390 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
1391 .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
baa6419e
TB
1392 .vip_wait = lib->settings->get_int(lib->settings,
1393 "%s.plugins.kernel-pfroute.vip_wait", 1000, hydra->daemon),
e131f117 1394 );
7daf5226 1395
d24a74c5
TB
1396 /* create a PF_ROUTE socket to communicate with the kernel */
1397 this->socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
0e107f03 1398 if (this->socket == -1)
d24a74c5 1399 {
d6a27ec6
MW
1400 DBG1(DBG_KNL, "unable to create PF_ROUTE socket");
1401 destroy(this);
1402 return NULL;
d24a74c5 1403 }
7daf5226 1404
0e107f03 1405 if (streq(hydra->daemon, "starter"))
d24a74c5 1406 {
0e107f03
MW
1407 /* starter has no threads, so we do not register for kernel events */
1408 if (shutdown(this->socket, SHUT_RD) != 0)
05ca5655 1409 {
0e107f03
MW
1410 DBG1(DBG_KNL, "closing read end of PF_ROUTE socket failed: %s",
1411 strerror(errno));
05ca5655 1412 }
0e107f03
MW
1413 }
1414 else
1415 {
26d77eb3
TB
1416 lib->processor->queue_job(lib->processor,
1417 (job_t*)callback_job_create_with_prio(
1418 (callback_job_cb_t)receive_events, this, NULL,
1419 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
05ca5655 1420 }
d24a74c5
TB
1421 if (init_address_list(this) != SUCCESS)
1422 {
d6a27ec6
MW
1423 DBG1(DBG_KNL, "unable to get interface list");
1424 destroy(this);
1425 return NULL;
d24a74c5 1426 }
7daf5226 1427
d24a74c5
TB
1428 return &this->public;
1429}