Reinstall routes in kernel-netlink plugin, if interfaces get reactivated or IPs reappear.
This commit is contained in:
@@ -52,12 +52,16 @@
|
|||||||
#include <threading/thread.h>
|
#include <threading/thread.h>
|
||||||
#include <threading/condvar.h>
|
#include <threading/condvar.h>
|
||||||
#include <threading/mutex.h>
|
#include <threading/mutex.h>
|
||||||
|
#include <utils/hashtable.h>
|
||||||
#include <utils/linked_list.h>
|
#include <utils/linked_list.h>
|
||||||
#include <processing/jobs/callback_job.h>
|
#include <processing/jobs/callback_job.h>
|
||||||
|
|
||||||
/** delay before firing roam events (ms) */
|
/** delay before firing roam events (ms) */
|
||||||
#define ROAM_DELAY 100
|
#define ROAM_DELAY 100
|
||||||
|
|
||||||
|
/** delay before reinstalling routes (ms) */
|
||||||
|
#define ROUTE_DELAY 100
|
||||||
|
|
||||||
typedef struct addr_entry_t addr_entry_t;
|
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;
|
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;
|
typedef struct private_kernel_netlink_net_t private_kernel_netlink_net_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -248,6 +298,21 @@ struct private_kernel_netlink_net_t {
|
|||||||
*/
|
*/
|
||||||
hashtable_t *routes;
|
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
|
* whether to react to RTM_NEWROUTE or RTM_DELROUTE events
|
||||||
*/
|
*/
|
||||||
@@ -264,6 +329,118 @@ struct private_kernel_netlink_net_t {
|
|||||||
linked_list_t *rt_exclude;
|
linked_list_t *rt_exclude;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* get the refcount of a virtual ip
|
||||||
*/
|
*/
|
||||||
@@ -382,9 +559,9 @@ static void process_link(private_kernel_netlink_net_t *this,
|
|||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
iface_entry_t *current, *entry = NULL;
|
iface_entry_t *current, *entry = NULL;
|
||||||
char *name = 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)
|
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))
|
if (!(entry->flags & IFF_UP) && (msg->ifi_flags & IFF_UP))
|
||||||
{
|
{
|
||||||
update = TRUE;
|
update = update_routes = TRUE;
|
||||||
DBG1(DBG_KNL, "interface %s activated", name);
|
DBG1(DBG_KNL, "interface %s activated", name);
|
||||||
}
|
}
|
||||||
if ((entry->flags & IFF_UP) && !(msg->ifi_flags & IFF_UP))
|
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);
|
this->mutex->unlock(this->mutex);
|
||||||
|
|
||||||
|
if (update_routes && event)
|
||||||
|
{
|
||||||
|
queue_route_reinstall(this, strdup(name), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* send an update to all IKE_SAs */
|
/* send an update to all IKE_SAs */
|
||||||
if (update && event)
|
if (update && event)
|
||||||
{
|
{
|
||||||
@@ -488,9 +670,10 @@ static void process_addr(private_kernel_netlink_net_t *this,
|
|||||||
iface_entry_t *iface;
|
iface_entry_t *iface;
|
||||||
addr_entry_t *addr;
|
addr_entry_t *addr;
|
||||||
chunk_t local = chunk_empty, address = chunk_empty;
|
chunk_t local = chunk_empty, address = chunk_empty;
|
||||||
|
char *route_ifname = NULL;
|
||||||
bool update = FALSE, found = FALSE, changed = FALSE;
|
bool update = FALSE, found = FALSE, changed = FALSE;
|
||||||
|
|
||||||
while(RTA_OK(rta, rtasize))
|
while (RTA_OK(rta, rtasize))
|
||||||
{
|
{
|
||||||
switch (rta->rta_type)
|
switch (rta->rta_type)
|
||||||
{
|
{
|
||||||
@@ -560,6 +743,7 @@ static void process_addr(private_kernel_netlink_net_t *this,
|
|||||||
{
|
{
|
||||||
found = TRUE;
|
found = TRUE;
|
||||||
changed = TRUE;
|
changed = TRUE;
|
||||||
|
route_ifname = strdup(iface->ifname);
|
||||||
addr = malloc_thing(addr_entry_t);
|
addr = malloc_thing(addr_entry_t);
|
||||||
addr->ip = host->clone(host);
|
addr->ip = host->clone(host);
|
||||||
addr->virtual = FALSE;
|
addr->virtual = FALSE;
|
||||||
@@ -582,6 +766,15 @@ static void process_addr(private_kernel_netlink_net_t *this,
|
|||||||
}
|
}
|
||||||
ifaces->destroy(ifaces);
|
ifaces->destroy(ifaces);
|
||||||
this->mutex->unlock(this->mutex);
|
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);
|
host->destroy(host);
|
||||||
|
|
||||||
/* send an update to all IKE_SAs */
|
/* send an update to all IKE_SAs */
|
||||||
@@ -1593,6 +1786,10 @@ METHOD(kernel_net_t, destroy, void,
|
|||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
this->routes->destroy(this->routes);
|
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->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
|
||||||
this->rt_exclude->destroy(this->rt_exclude);
|
this->rt_exclude->destroy(this->rt_exclude);
|
||||||
this->condvar->destroy(this->condvar);
|
this->condvar->destroy(this->condvar);
|
||||||
@@ -1628,6 +1825,10 @@ kernel_netlink_net_t *kernel_netlink_net_create()
|
|||||||
.rt_exclude = linked_list_create(),
|
.rt_exclude = linked_list_create(),
|
||||||
.routes = hashtable_create((hashtable_hash_t)route_entry_hash,
|
.routes = hashtable_create((hashtable_hash_t)route_entry_hash,
|
||||||
(hashtable_equals_t)route_entry_equals, 16),
|
(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(),
|
.ifaces = linked_list_create(),
|
||||||
.mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
|
.mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
|
||||||
.condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
|
.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,
|
.install_virtual_ip = lib->settings->get_bool(lib->settings,
|
||||||
"%s.install_virtual_ip", TRUE, hydra->daemon),
|
"%s.install_virtual_ip", TRUE, hydra->daemon),
|
||||||
);
|
);
|
||||||
|
timerclear(&this->last_route_reinstall);
|
||||||
timerclear(&this->last_roam);
|
timerclear(&this->last_roam);
|
||||||
|
|
||||||
exclude = lib->settings->get_str(lib->settings,
|
exclude = lib->settings->get_str(lib->settings,
|
||||||
|
|||||||
Reference in New Issue
Block a user