2 * Copyright (C) 2009-2012 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include <sys/types.h>
17 #include <sys/socket.h>
20 #include <net/route.h>
24 #include "kernel_pfroute_net.h"
28 #include <utils/host.h>
29 #include <threading/thread.h>
30 #include <threading/mutex.h>
31 #include <utils/linked_list.h>
32 #include <processing/jobs/callback_job.h>
34 #ifndef HAVE_STRUCT_SOCKADDR_SA_LEN
35 #error Cannot compile this plugin on systems where 'struct sockaddr' has no sa_len member.
38 /** delay before firing roam events (ms) */
39 #define ROAM_DELAY 100
41 /** buffer size for PF_ROUTE messages */
42 #define PFROUTE_BUFFER_SIZE 4096
44 typedef struct addr_entry_t addr_entry_t
;
47 * IP address in an inface_entry_t
54 /** virtual IP managed by us */
57 /** Number of times this IP is used, if virtual */
62 * destroy a addr_entry_t object
64 static void addr_entry_destroy(addr_entry_t
*this)
66 this->ip
->destroy(this->ip
);
70 typedef struct iface_entry_t iface_entry_t
;
73 * A network interface on this system, containing addr_entry_t's
75 struct iface_entry_t
{
77 /** interface index */
80 /** name of the interface */
81 char ifname
[IFNAMSIZ
];
83 /** interface flags, as in netdevice(7) SIOCGIFFLAGS */
86 /** list of addresses as host_t */
91 * destroy an interface entry
93 static void iface_entry_destroy(iface_entry_t
*this)
95 this->addrs
->destroy_function(this->addrs
, (void*)addr_entry_destroy
);
100 typedef struct private_kernel_pfroute_net_t private_kernel_pfroute_net_t
;
103 * Private variables and functions of kernel_pfroute class.
105 struct private_kernel_pfroute_net_t
108 * Public part of the kernel_pfroute_t object.
110 kernel_pfroute_net_t
public;
113 * mutex to lock access to various lists
118 * Cached list of interfaces and their addresses (iface_entry_t)
120 linked_list_t
*ifaces
;
123 * mutex to lock access to the PF_ROUTE socket
125 mutex_t
*mutex_pfroute
;
128 * PF_ROUTE socket to communicate with the kernel
133 * PF_ROUTE socket to receive events
138 * sequence number for messages sent to the kernel
143 * time of last roam event
149 * callback function that raises the delayed roam event
151 static job_requeue_t
roam_event(uintptr_t address
)
153 hydra
->kernel_interface
->roam(hydra
->kernel_interface
, address
!= 0);
154 return JOB_REQUEUE_NONE
;
158 * fire a roaming event. we delay it for a bit and fire only one event
159 * for multiple calls. otherwise we would create too many events.
161 static void fire_roam_event(private_kernel_pfroute_net_t
*this, bool address
)
166 time_monotonic(&now
);
167 if (timercmp(&now
, &this->last_roam
, >))
169 now
.tv_usec
+= ROAM_DELAY
* 1000;
170 while (now
.tv_usec
> 1000000)
173 now
.tv_usec
-= 1000000;
175 this->last_roam
= now
;
177 job
= (job_t
*)callback_job_create((callback_job_cb_t
)roam_event
,
178 (void*)(uintptr_t)(address ?
1 : 0),
180 lib
->scheduler
->schedule_job_ms(lib
->scheduler
, job
, ROAM_DELAY
);
185 * Process an RTM_*ADDR message from the kernel
187 static void process_addr(private_kernel_pfroute_net_t
*this,
188 struct rt_msghdr
*msg
)
190 struct ifa_msghdr
*ifa
= (struct ifa_msghdr
*)msg
;
191 sockaddr_t
*sockaddr
= (sockaddr_t
*)(ifa
+ 1);
193 enumerator_t
*ifaces
, *addrs
;
194 iface_entry_t
*iface
;
196 bool found
= FALSE
, changed
= FALSE
, roam
= FALSE
;
199 for (i
= 1; i
< (1 << RTAX_MAX
); i
<<= 1)
201 if (ifa
->ifam_addrs
& i
)
205 host
= host_create_from_sockaddr(sockaddr
);
208 sockaddr
= (sockaddr_t
*)((char*)sockaddr
+ sockaddr
->sa_len
);
217 this->mutex
->lock(this->mutex
);
218 ifaces
= this->ifaces
->create_enumerator(this->ifaces
);
219 while (ifaces
->enumerate(ifaces
, &iface
))
221 if (iface
->ifindex
== ifa
->ifam_index
)
223 addrs
= iface
->addrs
->create_enumerator(iface
->addrs
);
224 while (addrs
->enumerate(addrs
, &addr
))
226 if (host
->ip_equals(host
, addr
->ip
))
229 if (ifa
->ifam_type
== RTM_DELADDR
)
231 iface
->addrs
->remove_at(iface
->addrs
, addrs
);
235 DBG1(DBG_KNL
, "%H disappeared from %s",
236 host
, iface
->ifname
);
238 addr_entry_destroy(addr
);
240 else if (ifa
->ifam_type
== RTM_NEWADDR
&& addr
->virtual)
246 addrs
->destroy(addrs
);
248 if (!found
&& ifa
->ifam_type
== RTM_NEWADDR
)
251 addr
= malloc_thing(addr_entry_t
);
252 addr
->ip
= host
->clone(host
);
253 addr
->virtual = FALSE
;
255 iface
->addrs
->insert_last(iface
->addrs
, addr
);
256 DBG1(DBG_KNL
, "%H appeared on %s", host
, iface
->ifname
);
259 if (changed
&& (iface
->flags
& IFF_UP
))
266 ifaces
->destroy(ifaces
);
267 this->mutex
->unlock(this->mutex
);
272 fire_roam_event(this, TRUE
);
277 * Process an RTM_IFINFO message from the kernel
279 static void process_link(private_kernel_pfroute_net_t
*this,
280 struct rt_msghdr
*hdr
)
282 struct if_msghdr
*msg
= (struct if_msghdr
*)hdr
;
283 enumerator_t
*enumerator
;
284 iface_entry_t
*iface
;
287 this->mutex
->lock(this->mutex
);
288 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
289 while (enumerator
->enumerate(enumerator
, &iface
))
291 if (iface
->ifindex
== msg
->ifm_index
)
293 if (!(iface
->flags
& IFF_UP
) && (msg
->ifm_flags
& IFF_UP
))
296 DBG1(DBG_KNL
, "interface %s activated", iface
->ifname
);
298 else if ((iface
->flags
& IFF_UP
) && !(msg
->ifm_flags
& IFF_UP
))
301 DBG1(DBG_KNL
, "interface %s deactivated", iface
->ifname
);
303 iface
->flags
= msg
->ifm_flags
;
307 enumerator
->destroy(enumerator
);
308 this->mutex
->unlock(this->mutex
);
312 fire_roam_event(this, TRUE
);
317 * Process an RTM_*ROUTE message from the kernel
319 static void process_route(private_kernel_pfroute_net_t
*this,
320 struct rt_msghdr
*msg
)
326 * Receives events from kernel
328 static job_requeue_t
receive_events(private_kernel_pfroute_net_t
*this)
330 unsigned char buf
[PFROUTE_BUFFER_SIZE
];
331 struct rt_msghdr
*msg
= (struct rt_msghdr
*)buf
;
335 oldstate
= thread_cancelability(TRUE
);
336 len
= recvfrom(this->socket_events
, buf
, sizeof(buf
), 0, NULL
, 0);
337 thread_cancelability(oldstate
);
344 /* interrupted, try again */
345 return JOB_REQUEUE_DIRECT
;
347 /* no data ready, select again */
348 return JOB_REQUEUE_DIRECT
;
350 DBG1(DBG_KNL
, "unable to receive from PF_ROUTE event socket");
352 return JOB_REQUEUE_FAIR
;
356 if (len
< sizeof(msg
->rtm_msglen
) || len
< msg
->rtm_msglen
||
357 msg
->rtm_version
!= RTM_VERSION
)
359 DBG2(DBG_KNL
, "received corrupted PF_ROUTE message");
360 return JOB_REQUEUE_DIRECT
;
363 switch (msg
->rtm_type
)
367 process_addr(this, msg
);
370 /*case RTM_IFANNOUNCE <- what about this*/
371 process_link(this, msg
);
375 process_route(this, msg
);
380 return JOB_REQUEUE_DIRECT
;
384 /** enumerator over addresses */
386 private_kernel_pfroute_net_t
* this;
387 /** whether to enumerate down interfaces */
388 bool include_down_ifaces
;
389 /** whether to enumerate virtual ip addresses */
390 bool include_virtual_ips
;
391 /** whether to enumerate loopback interfaces */
392 bool include_loopback
;
393 } address_enumerator_t
;
396 * cleanup function for address enumerator
398 static void address_enumerator_destroy(address_enumerator_t
*data
)
400 data
->this->mutex
->unlock(data
->this->mutex
);
405 * filter for addresses
407 static bool filter_addresses(address_enumerator_t
*data
,
408 addr_entry_t
** in
, host_t
** out
)
411 if (!data
->include_virtual_ips
&& (*in
)->virtual)
412 { /* skip virtual interfaces added by us */
416 if (ip
->get_family(ip
) == AF_INET6
)
418 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)ip
->get_sockaddr(ip
);
419 if (IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
))
420 { /* skip addresses with a unusable scope */
429 * enumerator constructor for interfaces
431 static enumerator_t
*create_iface_enumerator(iface_entry_t
*iface
,
432 address_enumerator_t
*data
)
434 return enumerator_create_filter(iface
->addrs
->create_enumerator(iface
->addrs
),
435 (void*)filter_addresses
, data
, NULL
);
439 * filter for interfaces
441 static bool filter_interfaces(address_enumerator_t
*data
, iface_entry_t
** in
,
444 if (!data
->include_loopback
&& ((*in
)->flags
& IFF_LOOPBACK
))
445 { /* ignore loopback devices */
448 if (!data
->include_down_ifaces
&& !((*in
)->flags
& IFF_UP
))
449 { /* skip interfaces not up */
456 METHOD(kernel_net_t
, create_address_enumerator
, enumerator_t
*,
457 private_kernel_pfroute_net_t
*this,
458 bool include_down_ifaces
, bool include_virtual_ips
, bool include_loopback
)
460 address_enumerator_t
*data
= malloc_thing(address_enumerator_t
);
462 data
->include_down_ifaces
= include_down_ifaces
;
463 data
->include_virtual_ips
= include_virtual_ips
;
464 data
->include_loopback
= include_loopback
;
466 this->mutex
->lock(this->mutex
);
467 return enumerator_create_nested(
468 enumerator_create_filter(
469 this->ifaces
->create_enumerator(this->ifaces
),
470 (void*)filter_interfaces
, data
, NULL
),
471 (void*)create_iface_enumerator
, data
,
472 (void*)address_enumerator_destroy
);
475 METHOD(kernel_net_t
, get_interface_name
, bool,
476 private_kernel_pfroute_net_t
*this, host_t
* ip
, char **name
)
478 enumerator_t
*ifaces
, *addrs
;
479 iface_entry_t
*iface
;
483 this->mutex
->lock(this->mutex
);
484 ifaces
= this->ifaces
->create_enumerator(this->ifaces
);
485 while (ifaces
->enumerate(ifaces
, &iface
))
487 addrs
= iface
->addrs
->create_enumerator(iface
->addrs
);
488 while (addrs
->enumerate(addrs
, &addr
))
490 if (ip
->ip_equals(ip
, addr
->ip
))
495 *name
= strdup(iface
->ifname
);
500 addrs
->destroy(addrs
);
506 ifaces
->destroy(ifaces
);
507 this->mutex
->unlock(this->mutex
);
511 DBG2(DBG_KNL
, "%H is not a local address", ip
);
515 DBG2(DBG_KNL
, "%H is on interface %s", ip
, *name
);
520 METHOD(kernel_net_t
, get_source_addr
, host_t
*,
521 private_kernel_pfroute_net_t
*this, host_t
*dest
, host_t
*src
)
526 METHOD(kernel_net_t
, get_nexthop
, host_t
*,
527 private_kernel_pfroute_net_t
*this, host_t
*dest
, host_t
*src
)
532 METHOD(kernel_net_t
, add_ip
, status_t
,
533 private_kernel_pfroute_net_t
*this, host_t
*virtual_ip
, host_t
*iface_ip
)
538 METHOD(kernel_net_t
, del_ip
, status_t
,
539 private_kernel_pfroute_net_t
*this, host_t
*virtual_ip
)
544 METHOD(kernel_net_t
, add_route
, status_t
,
545 private_kernel_pfroute_net_t
*this, chunk_t dst_net
, u_int8_t prefixlen
,
546 host_t
*gateway
, host_t
*src_ip
, char *if_name
)
551 METHOD(kernel_net_t
, del_route
, status_t
,
552 private_kernel_pfroute_net_t
*this, chunk_t dst_net
, u_int8_t prefixlen
,
553 host_t
*gateway
, host_t
*src_ip
, char *if_name
)
559 * Initialize a list of local addresses.
561 static status_t
init_address_list(private_kernel_pfroute_net_t
*this)
563 struct ifaddrs
*ifap
, *ifa
;
564 iface_entry_t
*iface
, *current
;
566 enumerator_t
*ifaces
, *addrs
;
568 DBG2(DBG_KNL
, "known interfaces and IP addresses:");
570 if (getifaddrs(&ifap
) < 0)
572 DBG1(DBG_KNL
, " failed to get interfaces!");
576 for (ifa
= ifap
; ifa
!= NULL
; ifa
= ifa
->ifa_next
)
578 if (ifa
->ifa_addr
== NULL
)
582 switch(ifa
->ifa_addr
->sa_family
)
589 ifaces
= this->ifaces
->create_enumerator(this->ifaces
);
590 while (ifaces
->enumerate(ifaces
, ¤t
))
592 if (streq(current
->ifname
, ifa
->ifa_name
))
598 ifaces
->destroy(ifaces
);
602 iface
= malloc_thing(iface_entry_t
);
603 memcpy(iface
->ifname
, ifa
->ifa_name
, IFNAMSIZ
);
604 iface
->ifindex
= if_nametoindex(ifa
->ifa_name
);
605 iface
->flags
= ifa
->ifa_flags
;
606 iface
->addrs
= linked_list_create();
607 this->ifaces
->insert_last(this->ifaces
, iface
);
610 if (ifa
->ifa_addr
->sa_family
!= AF_LINK
)
612 addr
= malloc_thing(addr_entry_t
);
613 addr
->ip
= host_create_from_sockaddr(ifa
->ifa_addr
);
614 addr
->virtual = FALSE
;
616 iface
->addrs
->insert_last(iface
->addrs
, addr
);
623 ifaces
= this->ifaces
->create_enumerator(this->ifaces
);
624 while (ifaces
->enumerate(ifaces
, &iface
))
626 if (iface
->flags
& IFF_UP
)
628 DBG2(DBG_KNL
, " %s", iface
->ifname
);
629 addrs
= iface
->addrs
->create_enumerator(iface
->addrs
);
630 while (addrs
->enumerate(addrs
, (void**)&addr
))
632 DBG2(DBG_KNL
, " %H", addr
->ip
);
634 addrs
->destroy(addrs
);
637 ifaces
->destroy(ifaces
);
642 METHOD(kernel_net_t
, destroy
, void,
643 private_kernel_pfroute_net_t
*this)
645 if (this->socket
> 0)
649 if (this->socket_events
)
651 close(this->socket_events
);
653 this->ifaces
->destroy_function(this->ifaces
, (void*)iface_entry_destroy
);
654 this->mutex
->destroy(this->mutex
);
655 this->mutex_pfroute
->destroy(this->mutex_pfroute
);
660 * Described in header.
662 kernel_pfroute_net_t
*kernel_pfroute_net_create()
664 private_kernel_pfroute_net_t
*this;
665 bool register_for_events
= TRUE
;
670 .get_interface
= _get_interface_name
,
671 .create_address_enumerator
= _create_address_enumerator
,
672 .get_source_addr
= _get_source_addr
,
673 .get_nexthop
= _get_nexthop
,
676 .add_route
= _add_route
,
677 .del_route
= _del_route
,
681 .ifaces
= linked_list_create(),
682 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
683 .mutex_pfroute
= mutex_create(MUTEX_TYPE_DEFAULT
),
686 if (streq(hydra
->daemon
, "starter"))
687 { /* starter has no threads, so we do not register for kernel events */
688 register_for_events
= FALSE
;
691 /* create a PF_ROUTE socket to communicate with the kernel */
692 this->socket
= socket(PF_ROUTE
, SOCK_RAW
, AF_UNSPEC
);
693 if (this->socket
< 0)
695 DBG1(DBG_KNL
, "unable to create PF_ROUTE socket");
700 if (register_for_events
)
702 /* create a PF_ROUTE socket to receive events */
703 this->socket_events
= socket(PF_ROUTE
, SOCK_RAW
, AF_UNSPEC
);
704 if (this->socket_events
< 0)
706 DBG1(DBG_KNL
, "unable to create PF_ROUTE event socket");
711 lib
->processor
->queue_job(lib
->processor
,
712 (job_t
*)callback_job_create_with_prio(
713 (callback_job_cb_t
)receive_events
, this, NULL
,
714 (callback_job_cancel_t
)return_false
, JOB_PRIO_CRITICAL
));
717 if (init_address_list(this) != SUCCESS
)
719 DBG1(DBG_KNL
, "unable to get interface list");
724 return &this->public;