]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c
kernel-pfroute: use only a single PF_ROUTE socket for both events and queries
[thirdparty/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>
19#include <ifaddrs.h>
20#include <net/route.h>
21#include <unistd.h>
d24a74c5
TB
22#include <errno.h>
23
24#include "kernel_pfroute_net.h"
25
c5f7146b 26#include <hydra.h>
f05b4272 27#include <utils/debug.h>
2e7cc07e 28#include <networking/host.h>
4a5a5dd2 29#include <threading/thread.h>
eba64cef 30#include <threading/mutex.h>
bdf36dac 31#include <threading/rwlock.h>
12642a68
TB
32#include <collections/hashtable.h>
33#include <collections/linked_list.h>
d24a74c5 34#include <processing/jobs/callback_job.h>
d24a74c5
TB
35
36#ifndef HAVE_STRUCT_SOCKADDR_SA_LEN
37#error Cannot compile this plugin on systems where 'struct sockaddr' has no sa_len member.
38#endif
39
ba26508d 40/** delay before firing roam events (ms) */
d24a74c5
TB
41#define ROAM_DELAY 100
42
43/** buffer size for PF_ROUTE messages */
44#define PFROUTE_BUFFER_SIZE 4096
45
46typedef struct addr_entry_t addr_entry_t;
47
48/**
49 * IP address in an inface_entry_t
50 */
51struct addr_entry_t {
7daf5226 52
d24a74c5
TB
53 /** The ip address */
54 host_t *ip;
7daf5226 55
d24a74c5
TB
56 /** virtual IP managed by us */
57 bool virtual;
7daf5226 58
d24a74c5
TB
59 /** Number of times this IP is used, if virtual */
60 u_int refcount;
61};
62
63/**
64 * destroy a addr_entry_t object
65 */
66static void addr_entry_destroy(addr_entry_t *this)
67{
68 this->ip->destroy(this->ip);
69 free(this);
70}
71
72typedef struct iface_entry_t iface_entry_t;
73
74/**
75 * A network interface on this system, containing addr_entry_t's
76 */
77struct iface_entry_t {
7daf5226 78
d24a74c5
TB
79 /** interface index */
80 int ifindex;
7daf5226 81
d24a74c5
TB
82 /** name of the interface */
83 char ifname[IFNAMSIZ];
7daf5226 84
d24a74c5
TB
85 /** interface flags, as in netdevice(7) SIOCGIFFLAGS */
86 u_int flags;
7daf5226 87
d24a74c5
TB
88 /** list of addresses as host_t */
89 linked_list_t *addrs;
940e1b0f
TB
90
91 /** TRUE if usable by config */
92 bool usable;
d24a74c5
TB
93};
94
95/**
96 * destroy an interface entry
97 */
98static void iface_entry_destroy(iface_entry_t *this)
99{
100 this->addrs->destroy_function(this->addrs, (void*)addr_entry_destroy);
101 free(this);
102}
103
1f97e1aa
TB
104/**
105 * check if an interface is up
106 */
107static inline bool iface_entry_up(iface_entry_t *iface)
108{
109 return (iface->flags & IFF_UP) == IFF_UP;
110}
111
940e1b0f
TB
112/**
113 * check if an interface is up and usable
114 */
115static inline bool iface_entry_up_and_usable(iface_entry_t *iface)
116{
1f97e1aa
TB
117 return iface->usable && iface_entry_up(iface);
118}
119
120typedef struct addr_map_entry_t addr_map_entry_t;
121
122/**
123 * Entry that maps an IP address to an interface entry
124 */
125struct addr_map_entry_t {
126 /** The IP address */
127 host_t *ip;
128
129 /** The interface this address is installed on */
130 iface_entry_t *iface;
131};
132
133/**
134 * Hash a addr_map_entry_t object, all entries with the same IP address
135 * are stored in the same bucket
136 */
137static u_int addr_map_entry_hash(addr_map_entry_t *this)
138{
139 return chunk_hash(this->ip->get_address(this->ip));
140}
141
142/**
143 * Compare two addr_map_entry_t objects, two entries are equal if they are
144 * installed on the same interface
145 */
146static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b)
147{
148 return a->iface->ifindex == b->iface->ifindex &&
149 a->ip->ip_equals(a->ip, b->ip);
940e1b0f
TB
150}
151
1f97e1aa
TB
152/**
153 * Used with get_match this finds an address entry if it is installed on
154 * an up and usable interface
155 */
156static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a,
157 addr_map_entry_t *b)
158{
159 return iface_entry_up_and_usable(b->iface) &&
160 a->ip->ip_equals(a->ip, b->ip);
161}
162
163/**
164 * Used with get_match this finds an address entry if it is installed on
165 * any active local interface
166 */
167static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b)
168{
169 return iface_entry_up(b->iface) && a->ip->ip_equals(a->ip, b->ip);
170}
d24a74c5
TB
171
172typedef struct private_kernel_pfroute_net_t private_kernel_pfroute_net_t;
173
174/**
175 * Private variables and functions of kernel_pfroute class.
176 */
177struct private_kernel_pfroute_net_t
178{
179 /**
180 * Public part of the kernel_pfroute_t object.
181 */
182 kernel_pfroute_net_t public;
7daf5226 183
d24a74c5 184 /**
bdf36dac 185 * lock to access lists and maps
d24a74c5 186 */
bdf36dac 187 rwlock_t *lock;
7daf5226 188
d24a74c5
TB
189 /**
190 * Cached list of interfaces and their addresses (iface_entry_t)
191 */
192 linked_list_t *ifaces;
7daf5226 193
1f97e1aa
TB
194 /**
195 * Map for IP addresses to iface_entry_t objects (addr_map_entry_t)
196 */
197 hashtable_t *addrs;
198
d24a74c5
TB
199 /**
200 * mutex to lock access to the PF_ROUTE socket
201 */
202 mutex_t *mutex_pfroute;
7daf5226 203
d24a74c5
TB
204 /**
205 * PF_ROUTE socket to communicate with the kernel
206 */
207 int socket;
7daf5226 208
d24a74c5
TB
209 /**
210 * sequence number for messages sent to the kernel
211 */
212 int seq;
7daf5226 213
d24a74c5 214 /**
ba26508d 215 * time of last roam event
d24a74c5 216 */
de578445 217 timeval_t last_roam;
d24a74c5
TB
218};
219
1f97e1aa
TB
220/**
221 * Add an address map entry
222 */
bfd2cc1c 223static void addr_map_entry_add(private_kernel_pfroute_net_t *this,
1f97e1aa
TB
224 addr_entry_t *addr, iface_entry_t *iface)
225{
226 addr_map_entry_t *entry;
227
228 if (addr->virtual)
229 { /* don't map virtual IPs */
230 return;
231 }
232
233 INIT(entry,
234 .ip = addr->ip,
235 .iface = iface,
236 );
237 entry = this->addrs->put(this->addrs, entry, entry);
238 free(entry);
239}
240
241/**
242 * Remove an address map entry (the argument order is a bit strange because
243 * it is also used with linked_list_t.invoke_function)
244 */
245static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface,
bfd2cc1c 246 private_kernel_pfroute_net_t *this)
1f97e1aa
TB
247{
248 addr_map_entry_t *entry, lookup = {
249 .ip = addr->ip,
250 .iface = iface,
251 };
252
253 if (addr->virtual)
254 { /* these are never mapped, but this check avoid problems if a virtual IP
255 * equals a regular one */
256 return;
257 }
258 entry = this->addrs->remove(this->addrs, &lookup);
259 free(entry);
260}
261
d24a74c5 262/**
ba26508d 263 * callback function that raises the delayed roam event
d24a74c5 264 */
ba26508d
TB
265static job_requeue_t roam_event(uintptr_t address)
266{
f6659688 267 hydra->kernel_interface->roam(hydra->kernel_interface, address != 0);
ba26508d
TB
268 return JOB_REQUEUE_NONE;
269}
270
271/**
272 * fire a roaming event. we delay it for a bit and fire only one event
273 * for multiple calls. otherwise we would create too many events.
274 */
275static void fire_roam_event(private_kernel_pfroute_net_t *this, bool address)
d24a74c5 276{
de578445 277 timeval_t now;
ba26508d 278 job_t *job;
7daf5226 279
de578445
MW
280 time_monotonic(&now);
281 if (timercmp(&now, &this->last_roam, >))
d24a74c5 282 {
eecd41e3 283 timeval_add_ms(&now, ROAM_DELAY);
de578445 284 this->last_roam = now;
ba26508d
TB
285
286 job = (job_t*)callback_job_create((callback_job_cb_t)roam_event,
287 (void*)(uintptr_t)(address ? 1 : 0),
288 NULL, NULL);
bb381e26 289 lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY);
d24a74c5
TB
290 }
291}
292
293/**
294 * Process an RTM_*ADDR message from the kernel
295 */
296static void process_addr(private_kernel_pfroute_net_t *this,
297 struct rt_msghdr *msg)
298{
299 struct ifa_msghdr *ifa = (struct ifa_msghdr*)msg;
300 sockaddr_t *sockaddr = (sockaddr_t*)(ifa + 1);
301 host_t *host = NULL;
302 enumerator_t *ifaces, *addrs;
303 iface_entry_t *iface;
304 addr_entry_t *addr;
305 bool found = FALSE, changed = FALSE, roam = FALSE;
306 int i;
7daf5226 307
d24a74c5
TB
308 for (i = 1; i < (1 << RTAX_MAX); i <<= 1)
309 {
310 if (ifa->ifam_addrs & i)
311 {
312 if (RTA_IFA & i)
313 {
314 host = host_create_from_sockaddr(sockaddr);
315 break;
316 }
317 sockaddr = (sockaddr_t*)((char*)sockaddr + sockaddr->sa_len);
318 }
319 }
7daf5226 320
d24a74c5
TB
321 if (!host)
322 {
323 return;
324 }
7daf5226 325
bdf36dac 326 this->lock->write_lock(this->lock);
d24a74c5
TB
327 ifaces = this->ifaces->create_enumerator(this->ifaces);
328 while (ifaces->enumerate(ifaces, &iface))
329 {
330 if (iface->ifindex == ifa->ifam_index)
331 {
332 addrs = iface->addrs->create_enumerator(iface->addrs);
333 while (addrs->enumerate(addrs, &addr))
334 {
335 if (host->ip_equals(host, addr->ip))
336 {
337 found = TRUE;
338 if (ifa->ifam_type == RTM_DELADDR)
339 {
340 iface->addrs->remove_at(iface->addrs, addrs);
940e1b0f 341 if (!addr->virtual && iface->usable)
d24a74c5
TB
342 {
343 changed = TRUE;
344 DBG1(DBG_KNL, "%H disappeared from %s",
345 host, iface->ifname);
346 }
1f97e1aa 347 addr_map_entry_remove(addr, iface, this);
d24a74c5
TB
348 addr_entry_destroy(addr);
349 }
350 else if (ifa->ifam_type == RTM_NEWADDR && addr->virtual)
351 {
352 addr->refcount = 1;
353 }
354 }
355 }
356 addrs->destroy(addrs);
7daf5226 357
d24a74c5
TB
358 if (!found && ifa->ifam_type == RTM_NEWADDR)
359 {
360 changed = TRUE;
361 addr = malloc_thing(addr_entry_t);
362 addr->ip = host->clone(host);
363 addr->virtual = FALSE;
364 addr->refcount = 1;
365 iface->addrs->insert_last(iface->addrs, addr);
1f97e1aa 366 addr_map_entry_add(this, addr, iface);
940e1b0f
TB
367 if (iface->usable)
368 {
369 DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname);
370 }
d24a74c5 371 }
7daf5226 372
940e1b0f 373 if (changed && iface_entry_up_and_usable(iface))
d24a74c5
TB
374 {
375 roam = TRUE;
376 }
377 break;
378 }
379 }
380 ifaces->destroy(ifaces);
bdf36dac 381 this->lock->unlock(this->lock);
d24a74c5 382 host->destroy(host);
7daf5226 383
d24a74c5
TB
384 if (roam)
385 {
ba26508d 386 fire_roam_event(this, TRUE);
d24a74c5
TB
387 }
388}
389
390/**
391 * Process an RTM_IFINFO message from the kernel
392 */
393static void process_link(private_kernel_pfroute_net_t *this,
394 struct rt_msghdr *hdr)
395{
396 struct if_msghdr *msg = (struct if_msghdr*)hdr;
397 enumerator_t *enumerator;
398 iface_entry_t *iface;
399 bool roam = FALSE;
7daf5226 400
bdf36dac 401 this->lock->write_lock(this->lock);
d24a74c5
TB
402 enumerator = this->ifaces->create_enumerator(this->ifaces);
403 while (enumerator->enumerate(enumerator, &iface))
404 {
405 if (iface->ifindex == msg->ifm_index)
406 {
940e1b0f 407 if (iface->usable)
d24a74c5 408 {
940e1b0f
TB
409 if (!(iface->flags & IFF_UP) && (msg->ifm_flags & IFF_UP))
410 {
411 roam = TRUE;
412 DBG1(DBG_KNL, "interface %s activated", iface->ifname);
413 }
414 else if ((iface->flags & IFF_UP) && !(msg->ifm_flags & IFF_UP))
415 {
416 roam = TRUE;
417 DBG1(DBG_KNL, "interface %s deactivated", iface->ifname);
418 }
d24a74c5
TB
419 }
420 iface->flags = msg->ifm_flags;
421 break;
422 }
423 }
424 enumerator->destroy(enumerator);
bdf36dac 425 this->lock->unlock(this->lock);
7daf5226 426
d24a74c5
TB
427 if (roam)
428 {
ba26508d 429 fire_roam_event(this, TRUE);
d24a74c5
TB
430 }
431}
432
433/**
434 * Process an RTM_*ROUTE message from the kernel
435 */
436static void process_route(private_kernel_pfroute_net_t *this,
437 struct rt_msghdr *msg)
438{
439
440}
441
442/**
443 * Receives events from kernel
444 */
445static job_requeue_t receive_events(private_kernel_pfroute_net_t *this)
446{
447 unsigned char buf[PFROUTE_BUFFER_SIZE];
448 struct rt_msghdr *msg = (struct rt_msghdr*)buf;
4a5a5dd2
TB
449 int len;
450 bool oldstate;
7daf5226 451
4a5a5dd2 452 oldstate = thread_cancelability(TRUE);
0e107f03 453 len = recvfrom(this->socket, buf, sizeof(buf), 0, NULL, 0);
4a5a5dd2 454 thread_cancelability(oldstate);
7daf5226 455
d24a74c5
TB
456 if (len < 0)
457 {
458 switch (errno)
459 {
460 case EINTR:
461 /* interrupted, try again */
462 return JOB_REQUEUE_DIRECT;
463 case EAGAIN:
464 /* no data ready, select again */
465 return JOB_REQUEUE_DIRECT;
466 default:
467 DBG1(DBG_KNL, "unable to receive from PF_ROUTE event socket");
468 sleep(1);
469 return JOB_REQUEUE_FAIR;
470 }
471 }
7daf5226 472
e8002956 473 if (len < sizeof(*msg) || len < msg->rtm_msglen ||
d24a74c5
TB
474 msg->rtm_version != RTM_VERSION)
475 {
476 DBG2(DBG_KNL, "received corrupted PF_ROUTE message");
477 return JOB_REQUEUE_DIRECT;
478 }
7daf5226 479
d24a74c5
TB
480 switch (msg->rtm_type)
481 {
482 case RTM_NEWADDR:
483 case RTM_DELADDR:
484 process_addr(this, msg);
485 break;
486 case RTM_IFINFO:
487 /*case RTM_IFANNOUNCE <- what about this*/
488 process_link(this, msg);
489 break;
490 case RTM_ADD:
491 case RTM_DELETE:
492 process_route(this, msg);
493 default:
494 break;
495 }
7daf5226 496
d24a74c5
TB
497 return JOB_REQUEUE_DIRECT;
498}
499
500
501/** enumerator over addresses */
502typedef struct {
503 private_kernel_pfroute_net_t* this;
4106aea8 504 /** which addresses to enumerate */
bfd2cc1c 505 kernel_address_type_t which;
d24a74c5
TB
506} address_enumerator_t;
507
508/**
509 * cleanup function for address enumerator
510 */
511static void address_enumerator_destroy(address_enumerator_t *data)
512{
bdf36dac 513 data->this->lock->unlock(data->this->lock);
d24a74c5
TB
514 free(data);
515}
516
517/**
518 * filter for addresses
519 */
e131f117
MW
520static bool filter_addresses(address_enumerator_t *data,
521 addr_entry_t** in, host_t** out)
d24a74c5
TB
522{
523 host_t *ip;
4106aea8 524 if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->virtual)
d24a74c5
TB
525 { /* skip virtual interfaces added by us */
526 return FALSE;
527 }
528 ip = (*in)->ip;
529 if (ip->get_family(ip) == AF_INET6)
530 {
531 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ip->get_sockaddr(ip);
532 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
533 { /* skip addresses with a unusable scope */
534 return FALSE;
535 }
536 }
537 *out = ip;
538 return TRUE;
539}
540
541/**
542 * enumerator constructor for interfaces
543 */
e131f117
MW
544static enumerator_t *create_iface_enumerator(iface_entry_t *iface,
545 address_enumerator_t *data)
d24a74c5
TB
546{
547 return enumerator_create_filter(iface->addrs->create_enumerator(iface->addrs),
548 (void*)filter_addresses, data, NULL);
549}
550
551/**
552 * filter for interfaces
553 */
e131f117
MW
554static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in,
555 iface_entry_t** out)
d24a74c5 556{
4106aea8 557 if (!(data->which & ADDR_TYPE_IGNORED) && !(*in)->usable)
940e1b0f
TB
558 { /* skip interfaces excluded by config */
559 return FALSE;
560 }
4106aea8 561 if (!(data->which & ADDR_TYPE_LOOPBACK) && ((*in)->flags & IFF_LOOPBACK))
aed33805
TB
562 { /* ignore loopback devices */
563 return FALSE;
564 }
4106aea8
TB
565 if (!(data->which & ADDR_TYPE_DOWN) && !((*in)->flags & IFF_UP))
566 { /* skip interfaces not up */
d24a74c5
TB
567 return FALSE;
568 }
569 *out = *in;
570 return TRUE;
571}
572
e131f117 573METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
bfd2cc1c 574 private_kernel_pfroute_net_t *this, kernel_address_type_t which)
d24a74c5
TB
575{
576 address_enumerator_t *data = malloc_thing(address_enumerator_t);
577 data->this = this;
4106aea8 578 data->which = which;
d24a74c5 579
bdf36dac 580 this->lock->read_lock(this->lock);
d24a74c5 581 return enumerator_create_nested(
e131f117
MW
582 enumerator_create_filter(
583 this->ifaces->create_enumerator(this->ifaces),
584 (void*)filter_interfaces, data, NULL),
585 (void*)create_iface_enumerator, data,
586 (void*)address_enumerator_destroy);
d24a74c5
TB
587}
588
9ba36c0f
TB
589METHOD(kernel_net_t, get_interface_name, bool,
590 private_kernel_pfroute_net_t *this, host_t* ip, char **name)
d24a74c5 591{
1f97e1aa
TB
592 addr_map_entry_t *entry, lookup = {
593 .ip = ip,
594 };
d24a74c5 595
645d7a5e
TB
596 if (ip->is_anyaddr(ip))
597 {
598 return FALSE;
599 }
bdf36dac 600 this->lock->read_lock(this->lock);
1f97e1aa
TB
601 /* first try to find it on an up and usable interface */
602 entry = this->addrs->get_match(this->addrs, &lookup,
603 (void*)addr_map_entry_match_up_and_usable);
604 if (entry)
d24a74c5 605 {
1f97e1aa 606 if (name)
940e1b0f 607 {
1f97e1aa 608 *name = strdup(entry->iface->ifname);
940e1b0f
TB
609 DBG2(DBG_KNL, "%H is on interface %s", ip, *name);
610 }
bdf36dac 611 this->lock->unlock(this->lock);
1f97e1aa 612 return TRUE;
d24a74c5 613 }
1f97e1aa
TB
614 /* maybe it is installed on an ignored interface */
615 entry = this->addrs->get_match(this->addrs, &lookup,
616 (void*)addr_map_entry_match_up);
617 if (!entry)
618 { /* the address does not exist, is on a down interface */
619 DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip);
620 }
bdf36dac 621 this->lock->unlock(this->lock);
1f97e1aa 622 return FALSE;
d24a74c5
TB
623}
624
e131f117
MW
625METHOD(kernel_net_t, get_source_addr, host_t*,
626 private_kernel_pfroute_net_t *this, host_t *dest, host_t *src)
d24a74c5
TB
627{
628 return NULL;
629}
630
cce8f652 631METHOD(kernel_net_t, get_nexthop, host_t*,
dad6d904 632 private_kernel_pfroute_net_t *this, host_t *dest, host_t *src)
d24a74c5
TB
633{
634 return NULL;
635}
636
e131f117 637METHOD(kernel_net_t, add_ip, status_t,
50bd7558 638 private_kernel_pfroute_net_t *this, host_t *virtual_ip, int prefix,
b185cdd1 639 char *iface)
d24a74c5
TB
640{
641 return FAILED;
642}
643
e131f117 644METHOD(kernel_net_t, del_ip, status_t,
d88597f0
MW
645 private_kernel_pfroute_net_t *this, host_t *virtual_ip, int prefix,
646 bool wait)
d24a74c5
TB
647{
648 return FAILED;
649}
650
e131f117
MW
651METHOD(kernel_net_t, add_route, status_t,
652 private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
653 host_t *gateway, host_t *src_ip, char *if_name)
d24a74c5
TB
654{
655 return FAILED;
656}
657
e131f117
MW
658METHOD(kernel_net_t, del_route, status_t,
659 private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
660 host_t *gateway, host_t *src_ip, char *if_name)
d24a74c5
TB
661{
662 return FAILED;
663}
664
665/**
666 * Initialize a list of local addresses.
667 */
668static status_t init_address_list(private_kernel_pfroute_net_t *this)
669{
670 struct ifaddrs *ifap, *ifa;
671 iface_entry_t *iface, *current;
672 addr_entry_t *addr;
673 enumerator_t *ifaces, *addrs;
7daf5226 674
31a0e24b 675 DBG2(DBG_KNL, "known interfaces and IP addresses:");
7daf5226 676
d24a74c5
TB
677 if (getifaddrs(&ifap) < 0)
678 {
679 DBG1(DBG_KNL, " failed to get interfaces!");
680 return FAILED;
681 }
7daf5226 682
d24a74c5
TB
683 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
684 {
685 if (ifa->ifa_addr == NULL)
686 {
687 continue;
688 }
689 switch(ifa->ifa_addr->sa_family)
690 {
691 case AF_LINK:
692 case AF_INET:
693 case AF_INET6:
694 {
d24a74c5
TB
695 iface = NULL;
696 ifaces = this->ifaces->create_enumerator(this->ifaces);
697 while (ifaces->enumerate(ifaces, &current))
698 {
699 if (streq(current->ifname, ifa->ifa_name))
700 {
701 iface = current;
702 break;
703 }
704 }
705 ifaces->destroy(ifaces);
7daf5226 706
d24a74c5
TB
707 if (!iface)
708 {
709 iface = malloc_thing(iface_entry_t);
710 memcpy(iface->ifname, ifa->ifa_name, IFNAMSIZ);
711 iface->ifindex = if_nametoindex(ifa->ifa_name);
712 iface->flags = ifa->ifa_flags;
713 iface->addrs = linked_list_create();
940e1b0f
TB
714 iface->usable = hydra->kernel_interface->is_interface_usable(
715 hydra->kernel_interface, ifa->ifa_name);
d24a74c5
TB
716 this->ifaces->insert_last(this->ifaces, iface);
717 }
7daf5226 718
d24a74c5
TB
719 if (ifa->ifa_addr->sa_family != AF_LINK)
720 {
721 addr = malloc_thing(addr_entry_t);
722 addr->ip = host_create_from_sockaddr(ifa->ifa_addr);
723 addr->virtual = FALSE;
724 addr->refcount = 1;
725 iface->addrs->insert_last(iface->addrs, addr);
9845391a 726 addr_map_entry_add(this, addr, iface);
d24a74c5
TB
727 }
728 }
729 }
730 }
731 freeifaddrs(ifap);
7daf5226 732
d24a74c5
TB
733 ifaces = this->ifaces->create_enumerator(this->ifaces);
734 while (ifaces->enumerate(ifaces, &iface))
735 {
940e1b0f 736 if (iface->usable && iface->flags & IFF_UP)
d24a74c5 737 {
31a0e24b 738 DBG2(DBG_KNL, " %s", iface->ifname);
d24a74c5
TB
739 addrs = iface->addrs->create_enumerator(iface->addrs);
740 while (addrs->enumerate(addrs, (void**)&addr))
741 {
31a0e24b 742 DBG2(DBG_KNL, " %H", addr->ip);
d24a74c5
TB
743 }
744 addrs->destroy(addrs);
745 }
746 }
747 ifaces->destroy(ifaces);
7daf5226 748
d24a74c5
TB
749 return SUCCESS;
750}
751
cce8f652 752METHOD(kernel_net_t, destroy, void,
e131f117 753 private_kernel_pfroute_net_t *this)
d24a74c5 754{
1f97e1aa 755 enumerator_t *enumerator;
bfd2cc1c 756 addr_entry_t *addr;
1f97e1aa 757
0e107f03 758 if (this->socket != -1)
d6a27ec6
MW
759 {
760 close(this->socket);
761 }
1f97e1aa
TB
762 enumerator = this->addrs->create_enumerator(this->addrs);
763 while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
764 {
765 free(addr);
766 }
767 enumerator->destroy(enumerator);
768 this->addrs->destroy(this->addrs);
d24a74c5 769 this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
bdf36dac 770 this->lock->destroy(this->lock);
d24a74c5
TB
771 this->mutex_pfroute->destroy(this->mutex_pfroute);
772 free(this);
773}
774
775/*
776 * Described in header.
777 */
778kernel_pfroute_net_t *kernel_pfroute_net_create()
779{
e131f117
MW
780 private_kernel_pfroute_net_t *this;
781
782 INIT(this,
783 .public = {
784 .interface = {
785 .get_interface = _get_interface_name,
786 .create_address_enumerator = _create_address_enumerator,
787 .get_source_addr = _get_source_addr,
788 .get_nexthop = _get_nexthop,
789 .add_ip = _add_ip,
790 .del_ip = _del_ip,
791 .add_route = _add_route,
792 .del_route = _del_route,
793 .destroy = _destroy,
794 },
795 },
796 .ifaces = linked_list_create(),
1f97e1aa
TB
797 .addrs = hashtable_create(
798 (hashtable_hash_t)addr_map_entry_hash,
799 (hashtable_equals_t)addr_map_entry_equals, 16),
bdf36dac 800 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
e131f117
MW
801 .mutex_pfroute = mutex_create(MUTEX_TYPE_DEFAULT),
802 );
7daf5226 803
d24a74c5
TB
804 /* create a PF_ROUTE socket to communicate with the kernel */
805 this->socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
0e107f03 806 if (this->socket == -1)
d24a74c5 807 {
d6a27ec6
MW
808 DBG1(DBG_KNL, "unable to create PF_ROUTE socket");
809 destroy(this);
810 return NULL;
d24a74c5 811 }
7daf5226 812
0e107f03 813 if (streq(hydra->daemon, "starter"))
d24a74c5 814 {
0e107f03
MW
815 /* starter has no threads, so we do not register for kernel events */
816 if (shutdown(this->socket, SHUT_RD) != 0)
05ca5655 817 {
0e107f03
MW
818 DBG1(DBG_KNL, "closing read end of PF_ROUTE socket failed: %s",
819 strerror(errno));
05ca5655 820 }
0e107f03
MW
821 }
822 else
823 {
26d77eb3
TB
824 lib->processor->queue_job(lib->processor,
825 (job_t*)callback_job_create_with_prio(
826 (callback_job_cb_t)receive_events, this, NULL,
827 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
05ca5655 828 }
d24a74c5
TB
829 if (init_address_list(this) != SUCCESS)
830 {
d6a27ec6
MW
831 DBG1(DBG_KNL, "unable to get interface list");
832 destroy(this);
833 return NULL;
d24a74c5 834 }
7daf5226 835
d24a74c5
TB
836 return &this->public;
837}