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 if (msg
->ifm_flags
& IFF_LOOPBACK
)
288 { /* ignore loopback interfaces */
292 this->mutex
->lock(this->mutex
);
293 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
294 while (enumerator
->enumerate(enumerator
, &iface
))
296 if (iface
->ifindex
== msg
->ifm_index
)
298 if (!(iface
->flags
& IFF_UP
) && (msg
->ifm_flags
& IFF_UP
))
301 DBG1(DBG_KNL
, "interface %s activated", iface
->ifname
);
303 else if ((iface
->flags
& IFF_UP
) && !(msg
->ifm_flags
& IFF_UP
))
306 DBG1(DBG_KNL
, "interface %s deactivated", iface
->ifname
);
308 iface
->flags
= msg
->ifm_flags
;
312 enumerator
->destroy(enumerator
);
313 this->mutex
->unlock(this->mutex
);
317 fire_roam_event(this, TRUE
);
322 * Process an RTM_*ROUTE message from the kernel
324 static void process_route(private_kernel_pfroute_net_t
*this,
325 struct rt_msghdr
*msg
)
331 * Receives events from kernel
333 static job_requeue_t
receive_events(private_kernel_pfroute_net_t
*this)
335 unsigned char buf
[PFROUTE_BUFFER_SIZE
];
336 struct rt_msghdr
*msg
= (struct rt_msghdr
*)buf
;
340 oldstate
= thread_cancelability(TRUE
);
341 len
= recvfrom(this->socket_events
, buf
, sizeof(buf
), 0, NULL
, 0);
342 thread_cancelability(oldstate
);
349 /* interrupted, try again */
350 return JOB_REQUEUE_DIRECT
;
352 /* no data ready, select again */
353 return JOB_REQUEUE_DIRECT
;
355 DBG1(DBG_KNL
, "unable to receive from PF_ROUTE event socket");
357 return JOB_REQUEUE_FAIR
;
361 if (len
< sizeof(msg
->rtm_msglen
) || len
< msg
->rtm_msglen
||
362 msg
->rtm_version
!= RTM_VERSION
)
364 DBG2(DBG_KNL
, "received corrupted PF_ROUTE message");
365 return JOB_REQUEUE_DIRECT
;
368 switch (msg
->rtm_type
)
372 process_addr(this, msg
);
375 /*case RTM_IFANNOUNCE <- what about this*/
376 process_link(this, msg
);
380 process_route(this, msg
);
385 return JOB_REQUEUE_DIRECT
;
389 /** enumerator over addresses */
391 private_kernel_pfroute_net_t
* this;
392 /** whether to enumerate down interfaces */
393 bool include_down_ifaces
;
394 /** whether to enumerate virtual ip addresses */
395 bool include_virtual_ips
;
396 } address_enumerator_t
;
399 * cleanup function for address enumerator
401 static void address_enumerator_destroy(address_enumerator_t
*data
)
403 data
->this->mutex
->unlock(data
->this->mutex
);
408 * filter for addresses
410 static bool filter_addresses(address_enumerator_t
*data
,
411 addr_entry_t
** in
, host_t
** out
)
414 if (!data
->include_virtual_ips
&& (*in
)->virtual)
415 { /* skip virtual interfaces added by us */
419 if (ip
->get_family(ip
) == AF_INET6
)
421 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)ip
->get_sockaddr(ip
);
422 if (IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
))
423 { /* skip addresses with a unusable scope */
432 * enumerator constructor for interfaces
434 static enumerator_t
*create_iface_enumerator(iface_entry_t
*iface
,
435 address_enumerator_t
*data
)
437 return enumerator_create_filter(iface
->addrs
->create_enumerator(iface
->addrs
),
438 (void*)filter_addresses
, data
, NULL
);
442 * filter for interfaces
444 static bool filter_interfaces(address_enumerator_t
*data
, iface_entry_t
** in
,
447 if (!data
->include_down_ifaces
&& !((*in
)->flags
& IFF_UP
))
448 { /* skip interfaces not up */
455 METHOD(kernel_net_t
, create_address_enumerator
, enumerator_t
*,
456 private_kernel_pfroute_net_t
*this,
457 bool include_down_ifaces
, bool include_virtual_ips
)
459 address_enumerator_t
*data
= malloc_thing(address_enumerator_t
);
461 data
->include_down_ifaces
= include_down_ifaces
;
462 data
->include_virtual_ips
= include_virtual_ips
;
464 this->mutex
->lock(this->mutex
);
465 return enumerator_create_nested(
466 enumerator_create_filter(
467 this->ifaces
->create_enumerator(this->ifaces
),
468 (void*)filter_interfaces
, data
, NULL
),
469 (void*)create_iface_enumerator
, data
,
470 (void*)address_enumerator_destroy
);
473 METHOD(kernel_net_t
, get_interface_name
, char*,
474 private_kernel_pfroute_net_t
*this, host_t
* ip
)
476 enumerator_t
*ifaces
, *addrs
;
477 iface_entry_t
*iface
;
481 DBG2(DBG_KNL
, "getting interface name for %H", ip
);
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
))
492 name
= strdup(iface
->ifname
);
496 addrs
->destroy(addrs
);
502 ifaces
->destroy(ifaces
);
503 this->mutex
->unlock(this->mutex
);
507 DBG2(DBG_KNL
, "%H is on interface %s", ip
, name
);
511 DBG2(DBG_KNL
, "%H is not a local address", ip
);
516 METHOD(kernel_net_t
, get_source_addr
, host_t
*,
517 private_kernel_pfroute_net_t
*this, host_t
*dest
, host_t
*src
)
522 METHOD(kernel_net_t
, get_nexthop
, host_t
*,
523 private_kernel_pfroute_net_t
*this, host_t
*dest
)
528 METHOD(kernel_net_t
, add_ip
, status_t
,
529 private_kernel_pfroute_net_t
*this, host_t
*virtual_ip
, host_t
*iface_ip
)
534 METHOD(kernel_net_t
, del_ip
, status_t
,
535 private_kernel_pfroute_net_t
*this, host_t
*virtual_ip
)
540 METHOD(kernel_net_t
, add_route
, status_t
,
541 private_kernel_pfroute_net_t
*this, chunk_t dst_net
, u_int8_t prefixlen
,
542 host_t
*gateway
, host_t
*src_ip
, char *if_name
)
547 METHOD(kernel_net_t
, del_route
, status_t
,
548 private_kernel_pfroute_net_t
*this, chunk_t dst_net
, u_int8_t prefixlen
,
549 host_t
*gateway
, host_t
*src_ip
, char *if_name
)
555 * Initialize a list of local addresses.
557 static status_t
init_address_list(private_kernel_pfroute_net_t
*this)
559 struct ifaddrs
*ifap
, *ifa
;
560 iface_entry_t
*iface
, *current
;
562 enumerator_t
*ifaces
, *addrs
;
564 DBG2(DBG_KNL
, "known interfaces and IP addresses:");
566 if (getifaddrs(&ifap
) < 0)
568 DBG1(DBG_KNL
, " failed to get interfaces!");
572 for (ifa
= ifap
; ifa
!= NULL
; ifa
= ifa
->ifa_next
)
574 if (ifa
->ifa_addr
== NULL
)
578 switch(ifa
->ifa_addr
->sa_family
)
584 if (ifa
->ifa_flags
& IFF_LOOPBACK
)
585 { /* ignore loopback interfaces */
590 ifaces
= this->ifaces
->create_enumerator(this->ifaces
);
591 while (ifaces
->enumerate(ifaces
, ¤t
))
593 if (streq(current
->ifname
, ifa
->ifa_name
))
599 ifaces
->destroy(ifaces
);
603 iface
= malloc_thing(iface_entry_t
);
604 memcpy(iface
->ifname
, ifa
->ifa_name
, IFNAMSIZ
);
605 iface
->ifindex
= if_nametoindex(ifa
->ifa_name
);
606 iface
->flags
= ifa
->ifa_flags
;
607 iface
->addrs
= linked_list_create();
608 this->ifaces
->insert_last(this->ifaces
, iface
);
611 if (ifa
->ifa_addr
->sa_family
!= AF_LINK
)
613 addr
= malloc_thing(addr_entry_t
);
614 addr
->ip
= host_create_from_sockaddr(ifa
->ifa_addr
);
615 addr
->virtual = FALSE
;
617 iface
->addrs
->insert_last(iface
->addrs
, addr
);
624 ifaces
= this->ifaces
->create_enumerator(this->ifaces
);
625 while (ifaces
->enumerate(ifaces
, &iface
))
627 if (iface
->flags
& IFF_UP
)
629 DBG2(DBG_KNL
, " %s", iface
->ifname
);
630 addrs
= iface
->addrs
->create_enumerator(iface
->addrs
);
631 while (addrs
->enumerate(addrs
, (void**)&addr
))
633 DBG2(DBG_KNL
, " %H", addr
->ip
);
635 addrs
->destroy(addrs
);
638 ifaces
->destroy(ifaces
);
643 METHOD(kernel_net_t
, destroy
, void,
644 private_kernel_pfroute_net_t
*this)
646 if (this->socket
> 0)
650 if (this->socket_events
)
652 close(this->socket_events
);
654 this->ifaces
->destroy_function(this->ifaces
, (void*)iface_entry_destroy
);
655 this->mutex
->destroy(this->mutex
);
656 this->mutex_pfroute
->destroy(this->mutex_pfroute
);
661 * Described in header.
663 kernel_pfroute_net_t
*kernel_pfroute_net_create()
665 private_kernel_pfroute_net_t
*this;
666 bool register_for_events
= TRUE
;
671 .get_interface
= _get_interface_name
,
672 .create_address_enumerator
= _create_address_enumerator
,
673 .get_source_addr
= _get_source_addr
,
674 .get_nexthop
= _get_nexthop
,
677 .add_route
= _add_route
,
678 .del_route
= _del_route
,
682 .ifaces
= linked_list_create(),
683 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
684 .mutex_pfroute
= mutex_create(MUTEX_TYPE_DEFAULT
),
687 if (streq(hydra
->daemon
, "starter"))
688 { /* starter has no threads, so we do not register for kernel events */
689 register_for_events
= FALSE
;
692 /* create a PF_ROUTE socket to communicate with the kernel */
693 this->socket
= socket(PF_ROUTE
, SOCK_RAW
, AF_UNSPEC
);
694 if (this->socket
< 0)
696 DBG1(DBG_KNL
, "unable to create PF_ROUTE socket");
701 if (register_for_events
)
703 /* create a PF_ROUTE socket to receive events */
704 this->socket_events
= socket(PF_ROUTE
, SOCK_RAW
, AF_UNSPEC
);
705 if (this->socket_events
< 0)
707 DBG1(DBG_KNL
, "unable to create PF_ROUTE event socket");
712 lib
->processor
->queue_job(lib
->processor
,
713 (job_t
*)callback_job_create_with_prio(
714 (callback_job_cb_t
)receive_events
, this, NULL
,
715 (callback_job_cancel_t
)return_false
, JOB_PRIO_CRITICAL
));
718 if (init_address_list(this) != SUCCESS
)
720 DBG1(DBG_KNL
, "unable to get interface list");
725 return &this->public;