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