diff options
author | Tobias Brunner <tobias@strongswan.org> | 2011-12-20 14:59:21 +0100 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2012-05-02 15:24:47 +0200 |
commit | f834249c59ca69b79274f7e10d1e1f44844683d6 (patch) | |
tree | 7bc7fe68c4e2e04a043abc1e46b96190194050cd /src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c | |
parent | 74ba22c99233d183da5e424e55e82382dfa26341 (diff) | |
download | strongswan-f834249c59ca69b79274f7e10d1e1f44844683d6.tar.bz2 strongswan-f834249c59ca69b79274f7e10d1e1f44844683d6.tar.xz |
Reinstall routes in kernel-netlink plugin, if interfaces get reactivated or IPs reappear.
Diffstat (limited to 'src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c')
-rw-r--r-- | src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c | 210 |
1 files changed, 206 insertions, 4 deletions
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index 2835f5b02..4432bec9f 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -52,12 +52,16 @@ #include <threading/thread.h> #include <threading/condvar.h> #include <threading/mutex.h> +#include <utils/hashtable.h> #include <utils/linked_list.h> #include <processing/jobs/callback_job.h> /** delay before firing roam events (ms) */ #define ROAM_DELAY 100 +/** delay before reinstalling routes (ms) */ +#define ROUTE_DELAY 100 + typedef struct addr_entry_t addr_entry_t; /** @@ -187,6 +191,52 @@ static bool route_entry_equals(route_entry_t *a, route_entry_t *b) chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; } +typedef struct net_change_t net_change_t; + +/** + * Queued network changes + */ +struct net_change_t { + /** Name of the interface that got activated (or an IP appeared on) */ + char *if_name; + + /** IP that appeared, if any */ + host_t *ip; +}; + +/** + * Destroy a net_change_t object + */ +static void net_change_destroy(net_change_t *this) +{ + DESTROY_IF(this->ip); + free(this->if_name); + free(this); +} + +/** + * Hash a net_change_t object + */ +static u_int net_change_hash(net_change_t *this) +{ + if (this->ip) + { + return chunk_hash_inc(this->ip->get_address(this->ip), + chunk_hash(chunk_create(this->if_name, + strlen(this->if_name)))); + } + return chunk_hash(chunk_create(this->if_name, strlen(this->if_name))); +} + +/** + * Compare two net_change_t objects + */ +static bool net_change_equals(net_change_t *a, net_change_t *b) +{ + return streq(a->if_name, b->if_name) && ((!a->ip && !b->ip) || + (a->ip && b->ip && a->ip->equals(a->ip, b->ip))); +} + typedef struct private_kernel_netlink_net_t private_kernel_netlink_net_t; /** @@ -249,6 +299,21 @@ struct private_kernel_netlink_net_t { hashtable_t *routes; /** + * interface and IP address changes which may trigger route reinstallation + */ + hashtable_t *net_changes; + + /** + * mutex for route reinstallation triggers + */ + mutex_t *net_changes_lock; + + /** + * time of last route reinstallation + */ + timeval_t last_route_reinstall; + + /** * whether to react to RTM_NEWROUTE or RTM_DELROUTE events */ bool process_route; @@ -265,6 +330,118 @@ struct private_kernel_netlink_net_t { }; /** + * Forward declaration + */ +static status_t manage_srcroute(private_kernel_netlink_net_t *this, + int nlmsg_type, int flags, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, + host_t *src_ip, char *if_name); + +/** + * Clear the queued network changes. + */ +static void net_changes_clear(private_kernel_netlink_net_t *this) +{ + enumerator_t *enumerator; + net_change_t *change; + + enumerator = this->net_changes->create_enumerator(this->net_changes); + while (enumerator->enumerate(enumerator, NULL, (void**)&change)) + { + this->net_changes->remove_at(this->net_changes, enumerator); + net_change_destroy(change); + } + enumerator->destroy(enumerator); +} + +/** + * Act upon queued network changes. + */ +static job_requeue_t reinstall_routes(private_kernel_netlink_net_t *this) +{ + enumerator_t *enumerator; + route_entry_t *route; + + this->net_changes_lock->lock(this->net_changes_lock); + this->mutex->lock(this->mutex); + + enumerator = this->routes->create_enumerator(this->routes); + while (enumerator->enumerate(enumerator, NULL, (void**)&route)) + { + net_change_t *change, lookup = { + .if_name = route->if_name, + }; + /* check if a generic change for this interface is queued */ + change = this->net_changes->get(this->net_changes, &lookup); + if (!change) + { /* check if a specific update for this IP is queued */ + lookup.ip = route->src_ip; + change = this->net_changes->get(this->net_changes, &lookup); + } + if (change) + { + manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + route->dst_net, route->prefixlen, route->gateway, + route->src_ip, route->if_name); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + net_changes_clear(this); + this->net_changes_lock->unlock(this->net_changes_lock); + return JOB_REQUEUE_NONE; +} + +/** + * Queue route reinstallation caused by network changes for a given interface. + * Provide the IP address if the update is caused by an IP address change. + * + * The route reinstallation is delayed for a while and only done once for + * several calls during this delay, in order to avoid doing it too often. + * The interface name and IP address are freed. + */ +static void queue_route_reinstall(private_kernel_netlink_net_t *this, + char *if_name, host_t *ip) +{ + net_change_t *update, *found; + timeval_t now; + job_t *job; + + INIT(update, + .if_name = if_name, + .ip = ip + ); + + this->net_changes_lock->lock(this->net_changes_lock); + found = this->net_changes->get(this->net_changes, update); + if (found) + { + net_change_destroy(update); + } + else + { + this->net_changes->put(this->net_changes, update, update); + } + time_monotonic(&now); + if (timercmp(&now, &this->last_route_reinstall, >)) + { + now.tv_usec += ROUTE_DELAY * 1000; + while (now.tv_usec > 1000000) + { + now.tv_sec++; + now.tv_usec -= 1000000; + } + this->last_route_reinstall = now; + + job = (job_t*)callback_job_create((callback_job_cb_t)reinstall_routes, + this, NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, ROUTE_DELAY); + } + this->net_changes_lock->unlock(this->net_changes_lock); +} + +/** * get the refcount of a virtual ip */ static int get_vip_refcount(private_kernel_netlink_net_t *this, host_t* ip) @@ -382,9 +559,9 @@ static void process_link(private_kernel_netlink_net_t *this, enumerator_t *enumerator; iface_entry_t *current, *entry = NULL; char *name = NULL; - bool update = FALSE; + bool update = FALSE, update_routes = FALSE; - while(RTA_OK(rta, rtasize)) + while (RTA_OK(rta, rtasize)) { switch (rta->rta_type) { @@ -432,7 +609,7 @@ static void process_link(private_kernel_netlink_net_t *this, { if (!(entry->flags & IFF_UP) && (msg->ifi_flags & IFF_UP)) { - update = TRUE; + update = update_routes = TRUE; DBG1(DBG_KNL, "interface %s activated", name); } if ((entry->flags & IFF_UP) && !(msg->ifi_flags & IFF_UP)) @@ -467,6 +644,11 @@ static void process_link(private_kernel_netlink_net_t *this, } this->mutex->unlock(this->mutex); + if (update_routes && event) + { + queue_route_reinstall(this, strdup(name), NULL); + } + /* send an update to all IKE_SAs */ if (update && event) { @@ -488,9 +670,10 @@ static void process_addr(private_kernel_netlink_net_t *this, iface_entry_t *iface; addr_entry_t *addr; chunk_t local = chunk_empty, address = chunk_empty; + char *route_ifname = NULL; bool update = FALSE, found = FALSE, changed = FALSE; - while(RTA_OK(rta, rtasize)) + while (RTA_OK(rta, rtasize)) { switch (rta->rta_type) { @@ -560,6 +743,7 @@ static void process_addr(private_kernel_netlink_net_t *this, { found = TRUE; changed = TRUE; + route_ifname = strdup(iface->ifname); addr = malloc_thing(addr_entry_t); addr->ip = host->clone(host); addr->virtual = FALSE; @@ -582,6 +766,15 @@ static void process_addr(private_kernel_netlink_net_t *this, } ifaces->destroy(ifaces); this->mutex->unlock(this->mutex); + + if (update && event && route_ifname) + { + queue_route_reinstall(this, route_ifname, host->clone(host)); + } + else + { + free(route_ifname); + } host->destroy(host); /* send an update to all IKE_SAs */ @@ -1593,6 +1786,10 @@ METHOD(kernel_net_t, destroy, void, enumerator->destroy(enumerator); this->routes->destroy(this->routes); + net_changes_clear(this); + this->net_changes->destroy(this->net_changes); + this->net_changes_lock->destroy(this->net_changes_lock); + this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy); this->rt_exclude->destroy(this->rt_exclude); this->condvar->destroy(this->condvar); @@ -1628,6 +1825,10 @@ kernel_netlink_net_t *kernel_netlink_net_create() .rt_exclude = linked_list_create(), .routes = hashtable_create((hashtable_hash_t)route_entry_hash, (hashtable_equals_t)route_entry_equals, 16), + .net_changes = hashtable_create( + (hashtable_hash_t)net_change_hash, + (hashtable_equals_t)net_change_equals, 16), + .net_changes_lock = mutex_create(MUTEX_TYPE_DEFAULT), .ifaces = linked_list_create(), .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), @@ -1640,6 +1841,7 @@ kernel_netlink_net_t *kernel_netlink_net_create() .install_virtual_ip = lib->settings->get_bool(lib->settings, "%s.install_virtual_ip", TRUE, hydra->daemon), ); + timerclear(&this->last_route_reinstall); timerclear(&this->last_roam); exclude = lib->settings->get_str(lib->settings, |