Merge branch 'kernel-pfroute-mobility'
This improves the behavior of the kernel-pfroute plugin (and sometimes the kernel-pfkey plugin) in case of mobility, mostly when used as as client but also as gateway, if clients are mobile.
This commit is contained in:
commit
6b99e4b7e0
4 files changed
+470
-49
No files matched your search
@@ -610,6 +610,9 @@ Set MTU of ipsecN device
|
||||
.BR charon.plugins.kernel-netlink.roam_events " [yes]"
|
||||
Whether to trigger roam events when interfaces, addresses or routes change
|
||||
.TP
|
||||
.BR charon.plugins.kernel-pfroute.vip_wait " [1000]"
|
||||
Time in ms to wait until virtual IP addresses appear/disappear before failing.
|
||||
.TP
|
||||
.BR charon.plugins.load-tester
|
||||
Section to configure the load-tester plugin, see LOAD TESTS
|
||||
.TP
|
||||
|
||||
@@ -1266,7 +1266,7 @@ METHOD(kernel_net_t, get_interface_name, bool,
|
||||
if (name)
|
||||
{
|
||||
*name = strdup(entry->iface->ifname);
|
||||
DBG2(DBG_KNL, "virtual %H is on interface %s", ip, *name);
|
||||
DBG2(DBG_KNL, "virtual IP %H is on interface %s", ip, *name);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
|
||||
@@ -981,6 +981,10 @@ static traffic_selector_t* sadb_address2ts(struct sadb_address *address)
|
||||
{
|
||||
traffic_selector_t *ts;
|
||||
host_t *host;
|
||||
u_int8_t proto;
|
||||
|
||||
proto = address->sadb_address_proto;
|
||||
proto = proto == IPSEC_PROTO_ANY ? 0 : proto;
|
||||
|
||||
/* The Linux 2.6 kernel does not set the protocol and port information
|
||||
* in the src and dst sadb_address extensions of the SADB_ACQUIRE message.
|
||||
@@ -988,8 +992,7 @@ static traffic_selector_t* sadb_address2ts(struct sadb_address *address)
|
||||
host = host_create_from_sockaddr((sockaddr_t*)&address[1]);
|
||||
ts = traffic_selector_create_from_subnet(host,
|
||||
address->sadb_address_prefixlen,
|
||||
address->sadb_address_proto,
|
||||
host->get_port(host),
|
||||
proto, host->get_port(host),
|
||||
host->get_port(host) ?: 65535);
|
||||
return ts;
|
||||
}
|
||||
@@ -1990,10 +1993,14 @@ static void add_exclude_route(private_kernel_pfkey_ipsec_t *this,
|
||||
dst, NULL);
|
||||
if (gtw)
|
||||
{
|
||||
if (hydra->kernel_interface->add_route(hydra->kernel_interface,
|
||||
char *if_name = NULL;
|
||||
|
||||
if (hydra->kernel_interface->get_interface(
|
||||
hydra->kernel_interface, src, &if_name) &&
|
||||
hydra->kernel_interface->add_route(hydra->kernel_interface,
|
||||
dst->get_address(dst),
|
||||
dst->get_family(dst) == AF_INET ? 32 : 128,
|
||||
gtw, src, NULL) == SUCCESS)
|
||||
gtw, src, if_name) == SUCCESS)
|
||||
{
|
||||
INIT(exclude,
|
||||
.dst = dst->clone(dst),
|
||||
@@ -2009,6 +2016,7 @@ static void add_exclude_route(private_kernel_pfkey_ipsec_t *this,
|
||||
DBG1(DBG_KNL, "installing exclude route for %H failed", dst);
|
||||
}
|
||||
gtw->destroy(gtw);
|
||||
free(if_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2047,18 +2055,24 @@ static void remove_exclude_route(private_kernel_pfkey_ipsec_t *this,
|
||||
|
||||
if (removed)
|
||||
{
|
||||
char *if_name = NULL;
|
||||
|
||||
dst = route->exclude->dst;
|
||||
DBG2(DBG_KNL, "uninstalling exclude route for %H src %H",
|
||||
dst, route->exclude->src);
|
||||
if (hydra->kernel_interface->del_route(hydra->kernel_interface,
|
||||
if (hydra->kernel_interface->get_interface(
|
||||
hydra->kernel_interface,
|
||||
route->exclude->src, &if_name) &&
|
||||
hydra->kernel_interface->del_route(hydra->kernel_interface,
|
||||
dst->get_address(dst),
|
||||
dst->get_family(dst) == AF_INET ? 32 : 128,
|
||||
route->exclude->gtw, route->exclude->src,
|
||||
NULL) != SUCCESS)
|
||||
if_name) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst);
|
||||
}
|
||||
exclude_route_destroy(route->exclude);
|
||||
free(if_name);
|
||||
}
|
||||
route->exclude = NULL;
|
||||
}
|
||||
@@ -2476,9 +2490,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
struct sadb_msg *msg, *out;
|
||||
struct sadb_x_policy *pol;
|
||||
policy_entry_t *policy, *found = NULL;
|
||||
policy_sa_t *mapping;
|
||||
policy_sa_t *mapping, *to_remove = NULL;
|
||||
enumerator_t *enumerator;
|
||||
bool is_installed = TRUE;
|
||||
bool first = TRUE, is_installed = TRUE;
|
||||
u_int32_t priority;
|
||||
size_t len;
|
||||
|
||||
@@ -2508,19 +2522,26 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
policy_entry_destroy(policy, this);
|
||||
policy = found;
|
||||
|
||||
/* remove mapping to SA by reqid and priority */
|
||||
/* remove mapping to SA by reqid and priority, if multiple match, which
|
||||
* could happen when rekeying due to an address change, remove the oldest */
|
||||
priority = get_priority(policy, prio);
|
||||
enumerator = policy->used_by->create_enumerator(policy->used_by);
|
||||
while (enumerator->enumerate(enumerator, (void**)&mapping))
|
||||
{
|
||||
if (reqid == mapping->sa->cfg.reqid && priority == mapping->priority)
|
||||
{
|
||||
policy->used_by->remove_at(policy->used_by, enumerator);
|
||||
to_remove = mapping;
|
||||
is_installed = first;
|
||||
}
|
||||
else if (priority < mapping->priority)
|
||||
{
|
||||
break;
|
||||
}
|
||||
is_installed = FALSE;
|
||||
first = FALSE;
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
policy->used_by->remove(policy->used_by, to_remove, NULL);
|
||||
mapping = to_remove;
|
||||
|
||||
if (policy->used_by->get_count(policy->used_by) > 0)
|
||||
{ /* policy is used by more SAs, keep in kernel */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 Tobias Brunner
|
||||
* Copyright (C) 2009-2013 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -53,6 +53,9 @@
|
||||
/** 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;
|
||||
|
||||
/**
|
||||
@@ -133,6 +136,9 @@ struct addr_map_entry_t {
|
||||
/** The IP address */
|
||||
host_t *ip;
|
||||
|
||||
/** The address entry for this IP address */
|
||||
addr_entry_t *addr;
|
||||
|
||||
/** The interface this address is installed on */
|
||||
iface_entry_t *iface;
|
||||
};
|
||||
@@ -163,8 +169,17 @@ static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b)
|
||||
static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a,
|
||||
addr_map_entry_t *b)
|
||||
{
|
||||
return iface_entry_up_and_usable(b->iface) &&
|
||||
a->ip->ip_equals(a->ip, b->ip);
|
||||
return !b->addr->virtual && iface_entry_up_and_usable(b->iface) &&
|
||||
a->ip->ip_equals(a->ip, b->ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used with get_match this finds an address entry if it is installed as virtual
|
||||
* IP address
|
||||
*/
|
||||
static bool addr_map_entry_match_virtual(addr_map_entry_t *a, addr_map_entry_t *b)
|
||||
{
|
||||
return b->addr->virtual && a->ip->ip_equals(a->ip, b->ip);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,7 +188,112 @@ static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a,
|
||||
*/
|
||||
static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b)
|
||||
{
|
||||
return iface_entry_up(b->iface) && a->ip->ip_equals(a->ip, b->ip);
|
||||
return !b->addr->virtual && iface_entry_up(b->iface) &&
|
||||
a->ip->ip_equals(a->ip, b->ip);
|
||||
}
|
||||
|
||||
typedef struct route_entry_t route_entry_t;
|
||||
|
||||
/**
|
||||
* Installed routing entry
|
||||
*/
|
||||
struct route_entry_t {
|
||||
/** Name of the interface the route is bound to */
|
||||
char *if_name;
|
||||
|
||||
/** Gateway for this route */
|
||||
host_t *gateway;
|
||||
|
||||
/** Destination net */
|
||||
chunk_t dst_net;
|
||||
|
||||
/** Destination net prefixlen */
|
||||
u_int8_t prefixlen;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone a route_entry_t object.
|
||||
*/
|
||||
static route_entry_t *route_entry_clone(route_entry_t *this)
|
||||
{
|
||||
route_entry_t *route;
|
||||
|
||||
INIT(route,
|
||||
.if_name = strdup(this->if_name),
|
||||
.gateway = this->gateway ? this->gateway->clone(this->gateway) : NULL,
|
||||
.dst_net = chunk_clone(this->dst_net),
|
||||
.prefixlen = this->prefixlen,
|
||||
);
|
||||
return route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a route_entry_t object
|
||||
*/
|
||||
static void route_entry_destroy(route_entry_t *this)
|
||||
{
|
||||
free(this->if_name);
|
||||
DESTROY_IF(this->gateway);
|
||||
chunk_free(&this->dst_net);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a route_entry_t object
|
||||
*/
|
||||
static u_int route_entry_hash(route_entry_t *this)
|
||||
{
|
||||
return chunk_hash_inc(chunk_from_thing(this->prefixlen),
|
||||
chunk_hash(this->dst_net));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two route_entry_t objects
|
||||
*/
|
||||
static bool route_entry_equals(route_entry_t *a, route_entry_t *b)
|
||||
{
|
||||
if (a->if_name && b->if_name && streq(a->if_name, b->if_name) &&
|
||||
chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen)
|
||||
{
|
||||
return (!a->gateway && !b->gateway) || (a->gateway && b->gateway &&
|
||||
a->gateway->ip_equals(a->gateway, b->gateway));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy a net_change_t object
|
||||
*/
|
||||
static void net_change_destroy(net_change_t *this)
|
||||
{
|
||||
free(this->if_name);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a net_change_t object
|
||||
*/
|
||||
static u_int net_change_hash(net_change_t *this)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
typedef struct private_kernel_pfroute_net_t private_kernel_pfroute_net_t;
|
||||
@@ -218,6 +338,31 @@ struct private_kernel_pfroute_net_t
|
||||
*/
|
||||
condvar_t *condvar;
|
||||
|
||||
/**
|
||||
* installed routes
|
||||
*/
|
||||
hashtable_t *routes;
|
||||
|
||||
/**
|
||||
* mutex for routes
|
||||
*/
|
||||
mutex_t *routes_lock;
|
||||
|
||||
/**
|
||||
* interface 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;
|
||||
|
||||
/**
|
||||
* pid to send PF_ROUTE messages with
|
||||
*/
|
||||
@@ -247,8 +392,108 @@ struct private_kernel_pfroute_net_t
|
||||
* time of last roam event
|
||||
*/
|
||||
timeval_t last_roam;
|
||||
|
||||
/**
|
||||
* Time in ms to wait for IP addresses to appear/disappear
|
||||
*/
|
||||
int vip_wait;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Forward declaration
|
||||
*/
|
||||
static status_t manage_route(private_kernel_pfroute_net_t *this, int op,
|
||||
chunk_t dst_net, u_int8_t prefixlen,
|
||||
host_t *gateway, char *if_name);
|
||||
|
||||
/**
|
||||
* Clear the queued network changes.
|
||||
*/
|
||||
static void net_changes_clear(private_kernel_pfroute_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_pfroute_net_t *this)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
route_entry_t *route;
|
||||
|
||||
this->net_changes_lock->lock(this->net_changes_lock);
|
||||
this->routes_lock->lock(this->routes_lock);
|
||||
|
||||
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 change for the outgoing interface is queued */
|
||||
change = this->net_changes->get(this->net_changes, &lookup);
|
||||
if (change)
|
||||
{
|
||||
manage_route(this, RTM_ADD, route->dst_net, route->prefixlen,
|
||||
route->gateway, route->if_name);
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->routes_lock->unlock(this->routes_lock);
|
||||
|
||||
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.
|
||||
*
|
||||
* 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 is freed.
|
||||
*/
|
||||
static void queue_route_reinstall(private_kernel_pfroute_net_t *this,
|
||||
char *if_name)
|
||||
{
|
||||
net_change_t *update, *found;
|
||||
timeval_t now;
|
||||
job_t *job;
|
||||
|
||||
INIT(update,
|
||||
.if_name = if_name
|
||||
);
|
||||
|
||||
this->net_changes_lock->lock(this->net_changes_lock);
|
||||
found = this->net_changes->put(this->net_changes, update, update);
|
||||
if (found)
|
||||
{
|
||||
net_change_destroy(found);
|
||||
}
|
||||
time_monotonic(&now);
|
||||
if (timercmp(&now, &this->last_route_reinstall, >))
|
||||
{
|
||||
timeval_add_ms(&now, ROUTE_DELAY);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an address map entry
|
||||
*/
|
||||
@@ -257,13 +502,9 @@ static void addr_map_entry_add(private_kernel_pfroute_net_t *this,
|
||||
{
|
||||
addr_map_entry_t *entry;
|
||||
|
||||
if (addr->virtual)
|
||||
{ /* don't map virtual IPs */
|
||||
return;
|
||||
}
|
||||
|
||||
INIT(entry,
|
||||
.ip = addr->ip,
|
||||
.addr = addr,
|
||||
.iface = iface,
|
||||
);
|
||||
entry = this->addrs->put(this->addrs, entry, entry);
|
||||
@@ -279,14 +520,10 @@ static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface,
|
||||
{
|
||||
addr_map_entry_t *entry, lookup = {
|
||||
.ip = addr->ip,
|
||||
.addr = addr,
|
||||
.iface = iface,
|
||||
};
|
||||
|
||||
if (addr->virtual)
|
||||
{ /* these are never mapped, but this check avoid problems if a virtual IP
|
||||
* equals a regular one */
|
||||
return;
|
||||
}
|
||||
entry = this->addrs->remove(this->addrs, &lookup);
|
||||
free(entry);
|
||||
}
|
||||
@@ -364,11 +601,11 @@ METHOD(enumerator_t, rt_enumerate, bool,
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a safe enumerator over sockaddrs in ifa/ifam/rt_msg
|
||||
* Create an enumerator over sockaddrs in rt/if messages
|
||||
*/
|
||||
static enumerator_t *create_rtmsg_enumerator(void *hdr, size_t hdrlen)
|
||||
static enumerator_t *create_rt_enumerator(int types, int remaining,
|
||||
struct sockaddr *addr)
|
||||
{
|
||||
struct rt_msghdr *rthdr = hdr;
|
||||
rt_enumerator_t *this;
|
||||
|
||||
INIT(this,
|
||||
@@ -376,13 +613,31 @@ static enumerator_t *create_rtmsg_enumerator(void *hdr, size_t hdrlen)
|
||||
.enumerate = (void*)_rt_enumerate,
|
||||
.destroy = (void*)free,
|
||||
},
|
||||
.types = rthdr->rtm_addrs,
|
||||
.remaining = rthdr->rtm_msglen - hdrlen,
|
||||
.addr = hdr + hdrlen,
|
||||
.types = types,
|
||||
.remaining = remaining,
|
||||
.addr = addr,
|
||||
);
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a safe enumerator over sockaddrs in rt_msghdr
|
||||
*/
|
||||
static enumerator_t *create_rtmsg_enumerator(struct rt_msghdr *hdr)
|
||||
{
|
||||
return create_rt_enumerator(hdr->rtm_addrs, hdr->rtm_msglen - sizeof(*hdr),
|
||||
(struct sockaddr *)(hdr + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a safe enumerator over sockaddrs in ifa_msghdr
|
||||
*/
|
||||
static enumerator_t *create_ifamsg_enumerator(struct ifa_msghdr *hdr)
|
||||
{
|
||||
return create_rt_enumerator(hdr->ifam_addrs, hdr->ifam_msglen - sizeof(*hdr),
|
||||
(struct sockaddr *)(hdr + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an RTM_*ADDR message from the kernel
|
||||
*/
|
||||
@@ -396,9 +651,10 @@ static void process_addr(private_kernel_pfroute_net_t *this,
|
||||
addr_entry_t *addr;
|
||||
bool found = FALSE, changed = FALSE, roam = FALSE;
|
||||
enumerator_t *enumerator;
|
||||
char *ifname = NULL;
|
||||
int type;
|
||||
|
||||
enumerator = create_rtmsg_enumerator(ifa, sizeof(*ifa));
|
||||
enumerator = create_ifamsg_enumerator(ifa);
|
||||
while (enumerator->enumerate(enumerator, &type, &sockaddr))
|
||||
{
|
||||
if (type == RTAX_IFA)
|
||||
@@ -409,8 +665,9 @@ static void process_addr(private_kernel_pfroute_net_t *this,
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (!host)
|
||||
if (!host || host->is_anyaddr(host))
|
||||
{
|
||||
DESTROY_IF(host);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -448,6 +705,7 @@ static void process_addr(private_kernel_pfroute_net_t *this,
|
||||
.ip = host->clone(host),
|
||||
);
|
||||
changed = TRUE;
|
||||
ifname = strdup(iface->ifname);
|
||||
iface->addrs->insert_last(iface->addrs, addr);
|
||||
addr_map_entry_add(this, addr, iface);
|
||||
if (iface->usable)
|
||||
@@ -467,6 +725,15 @@ static void process_addr(private_kernel_pfroute_net_t *this,
|
||||
this->lock->unlock(this->lock);
|
||||
host->destroy(host);
|
||||
|
||||
if (roam && ifname)
|
||||
{
|
||||
queue_route_reinstall(this, ifname);
|
||||
}
|
||||
else
|
||||
{
|
||||
free(ifname);
|
||||
}
|
||||
|
||||
if (roam)
|
||||
{
|
||||
fire_roam_event(this, TRUE);
|
||||
@@ -521,7 +788,7 @@ static void process_link(private_kernel_pfroute_net_t *this,
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
iface_entry_t *iface;
|
||||
bool roam = FALSE, found = FALSE;
|
||||
bool roam = FALSE, found = FALSE, update_routes = FALSE;
|
||||
|
||||
this->lock->write_lock(this->lock);
|
||||
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
||||
@@ -533,7 +800,7 @@ static void process_link(private_kernel_pfroute_net_t *this,
|
||||
{
|
||||
if (!(iface->flags & IFF_UP) && (msg->ifm_flags & IFF_UP))
|
||||
{
|
||||
roam = TRUE;
|
||||
roam = update_routes = TRUE;
|
||||
DBG1(DBG_KNL, "interface %s activated", iface->ifname);
|
||||
}
|
||||
else if ((iface->flags & IFF_UP) && !(msg->ifm_flags & IFF_UP))
|
||||
@@ -564,6 +831,10 @@ static void process_link(private_kernel_pfroute_net_t *this,
|
||||
hydra->kernel_interface, iface->ifname);
|
||||
repopulate_iface(this, iface);
|
||||
this->ifaces->insert_last(this->ifaces, iface);
|
||||
if (iface->usable)
|
||||
{
|
||||
roam = update_routes = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -572,6 +843,11 @@ static void process_link(private_kernel_pfroute_net_t *this,
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
if (update_routes)
|
||||
{
|
||||
queue_route_reinstall(this, strdup(iface->ifname));
|
||||
}
|
||||
|
||||
if (roam)
|
||||
{
|
||||
fire_roam_event(this, TRUE);
|
||||
@@ -812,6 +1088,19 @@ METHOD(kernel_net_t, get_interface_name, bool,
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
/* check if it is a virtual IP */
|
||||
entry = this->addrs->get_match(this->addrs, &lookup,
|
||||
(void*)addr_map_entry_match_virtual);
|
||||
if (entry)
|
||||
{
|
||||
if (name)
|
||||
{
|
||||
*name = strdup(entry->iface->ifname);
|
||||
DBG2(DBG_KNL, "virtual IP %H is on interface %s", ip, *name);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
/* maybe it is installed on an ignored interface */
|
||||
entry = this->addrs->get_match(this->addrs, &lookup,
|
||||
(void*)addr_map_entry_match_up);
|
||||
@@ -852,7 +1141,8 @@ METHOD(kernel_net_t, add_ip, status_t,
|
||||
this->mutex->lock(this->mutex);
|
||||
while (!timeout && !get_interface_name(this, vip, NULL))
|
||||
{
|
||||
timeout = this->condvar->timed_wait(this->condvar, this->mutex, 1000);
|
||||
timeout = this->condvar->timed_wait(this->condvar, this->mutex,
|
||||
this->vip_wait);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (timeout)
|
||||
@@ -880,11 +1170,16 @@ METHOD(kernel_net_t, add_ip, status_t,
|
||||
}
|
||||
}
|
||||
addrs->destroy(addrs);
|
||||
/* during IKEv1 reauthentication, children get moved from
|
||||
* old the new SA before the virtual IP is available. This
|
||||
* kills the route for our virtual IP, reinstall. */
|
||||
queue_route_reinstall(this, strdup(iface->ifname));
|
||||
break;
|
||||
}
|
||||
}
|
||||
ifaces->destroy(ifaces);
|
||||
/* lets do this while holding the lock, thus preventing another thread
|
||||
* from deleting the TUN device concurrently, hopefully listeneres are quick
|
||||
* from deleting the TUN device concurrently, hopefully listeners are quick
|
||||
* and cause no deadlocks */
|
||||
hydra->kernel_interface->tun(hydra->kernel_interface, tun, TRUE);
|
||||
this->lock->unlock(this->lock);
|
||||
@@ -929,7 +1224,8 @@ METHOD(kernel_net_t, del_ip, status_t,
|
||||
this->mutex->lock(this->mutex);
|
||||
while (!timeout && get_interface_name(this, vip, NULL))
|
||||
{
|
||||
timeout = this->condvar->timed_wait(this->condvar, this->mutex, 1000);
|
||||
timeout = this->condvar->timed_wait(this->condvar, this->mutex,
|
||||
this->vip_wait);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (timeout)
|
||||
@@ -1008,7 +1304,7 @@ static status_t manage_route(private_kernel_pfroute_net_t *this, int op,
|
||||
.rtm_type = op,
|
||||
.rtm_flags = RTF_UP | RTF_STATIC,
|
||||
.rtm_pid = this->pid,
|
||||
.rtm_seq = ++this->seq,
|
||||
.rtm_seq = ref_get(&this->seq),
|
||||
},
|
||||
};
|
||||
host_t *dst;
|
||||
@@ -1076,6 +1372,10 @@ static status_t manage_route(private_kernel_pfroute_net_t *this, int op,
|
||||
|
||||
if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) != msg.hdr.rtm_msglen)
|
||||
{
|
||||
if (errno == EEXIST)
|
||||
{
|
||||
return ALREADY_DONE;
|
||||
}
|
||||
DBG1(DBG_KNL, "%s PF_ROUTE route failed: %s",
|
||||
op == RTM_ADD ? "adding" : "deleting", strerror(errno));
|
||||
return FAILED;
|
||||
@@ -1087,14 +1387,53 @@ METHOD(kernel_net_t, add_route, status_t,
|
||||
private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
|
||||
host_t *gateway, host_t *src_ip, char *if_name)
|
||||
{
|
||||
return manage_route(this, RTM_ADD, dst_net, prefixlen, gateway, if_name);
|
||||
status_t status;
|
||||
route_entry_t *found, route = {
|
||||
.dst_net = dst_net,
|
||||
.prefixlen = prefixlen,
|
||||
.gateway = gateway,
|
||||
.if_name = if_name,
|
||||
};
|
||||
|
||||
this->routes_lock->lock(this->routes_lock);
|
||||
found = this->routes->get(this->routes, &route);
|
||||
if (found)
|
||||
{
|
||||
this->routes_lock->unlock(this->routes_lock);
|
||||
return ALREADY_DONE;
|
||||
}
|
||||
found = route_entry_clone(&route);
|
||||
this->routes->put(this->routes, found, found);
|
||||
status = manage_route(this, RTM_ADD, dst_net, prefixlen, gateway, if_name);
|
||||
this->routes_lock->unlock(this->routes_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
METHOD(kernel_net_t, del_route, status_t,
|
||||
private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
|
||||
host_t *gateway, host_t *src_ip, char *if_name)
|
||||
{
|
||||
return manage_route(this, RTM_DELETE, dst_net, prefixlen, gateway, if_name);
|
||||
status_t status;
|
||||
route_entry_t *found, route = {
|
||||
.dst_net = dst_net,
|
||||
.prefixlen = prefixlen,
|
||||
.gateway = gateway,
|
||||
.if_name = if_name,
|
||||
};
|
||||
|
||||
this->routes_lock->lock(this->routes_lock);
|
||||
found = this->routes->get(this->routes, &route);
|
||||
if (!found)
|
||||
{
|
||||
this->routes_lock->unlock(this->routes_lock);
|
||||
return NOT_FOUND;
|
||||
}
|
||||
this->routes->remove(this->routes, found);
|
||||
route_entry_destroy(found);
|
||||
status = manage_route(this, RTM_DELETE, dst_net, prefixlen, gateway,
|
||||
if_name);
|
||||
this->routes_lock->unlock(this->routes_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1112,14 +1451,16 @@ static host_t *get_route(private_kernel_pfroute_net_t *this, bool nexthop,
|
||||
.rtm_version = RTM_VERSION,
|
||||
.rtm_type = RTM_GET,
|
||||
.rtm_pid = this->pid,
|
||||
.rtm_seq = ++this->seq,
|
||||
.rtm_seq = ref_get(&this->seq),
|
||||
},
|
||||
};
|
||||
host_t *host = NULL;
|
||||
enumerator_t *enumerator;
|
||||
struct sockaddr *addr;
|
||||
bool failed = FALSE;
|
||||
int type;
|
||||
|
||||
retry:
|
||||
msg.hdr.rtm_msglen = sizeof(struct rt_msghdr);
|
||||
for (type = 0; type < RTAX_MAX; type++)
|
||||
{
|
||||
@@ -1161,8 +1502,7 @@ static host_t *get_route(private_kernel_pfroute_net_t *this, bool nexthop,
|
||||
{
|
||||
continue;
|
||||
}
|
||||
enumerator = create_rtmsg_enumerator(this->reply,
|
||||
sizeof(*this->reply));
|
||||
enumerator = create_rtmsg_enumerator(this->reply);
|
||||
while (enumerator->enumerate(enumerator, &type, &addr))
|
||||
{
|
||||
if (nexthop)
|
||||
@@ -1196,18 +1536,47 @@ static host_t *get_route(private_kernel_pfroute_net_t *this, bool nexthop,
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_KNL, "PF_ROUTE lookup failed: %s", strerror(errno));
|
||||
failed = TRUE;
|
||||
}
|
||||
/* signal completion of query to a waiting thread */
|
||||
this->waiting_seq = 0;
|
||||
this->condvar->signal(this->condvar);
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
if (host)
|
||||
if (failed)
|
||||
{
|
||||
DBG2(DBG_KNL, "using %H as %s to reach %H", host,
|
||||
nexthop ? "nexthop" : "address", dest);
|
||||
if (src)
|
||||
{ /* the given source address might be gone, try again without */
|
||||
src = NULL;
|
||||
msg.hdr.rtm_seq = ref_get(&this->seq);
|
||||
msg.hdr.rtm_addrs = 0;
|
||||
memset(msg.buf, sizeof(msg.buf), 0);
|
||||
goto retry;
|
||||
}
|
||||
DBG1(DBG_KNL, "PF_ROUTE lookup failed: %s", strerror(errno));
|
||||
}
|
||||
if (!host)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!nexthop)
|
||||
{ /* make sure the source address is not virtual and usable */
|
||||
addr_entry_t *entry, lookup = {
|
||||
.ip = host,
|
||||
};
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
entry = this->addrs->get_match(this->addrs, &lookup,
|
||||
(void*)addr_map_entry_match_up_and_usable);
|
||||
this->lock->unlock(this->lock);
|
||||
if (!entry)
|
||||
{
|
||||
host->destroy(host);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
DBG2(DBG_KNL, "using %H as %s to reach %H", host,
|
||||
nexthop ? "nexthop" : "address", dest);
|
||||
return host;
|
||||
}
|
||||
|
||||
@@ -1314,12 +1683,29 @@ METHOD(kernel_net_t, destroy, void,
|
||||
private_kernel_pfroute_net_t *this)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
route_entry_t *route;
|
||||
addr_entry_t *addr;
|
||||
|
||||
enumerator = this->routes->create_enumerator(this->routes);
|
||||
while (enumerator->enumerate(enumerator, NULL, (void**)&route))
|
||||
{
|
||||
manage_route(this, RTM_DELETE, route->dst_net, route->prefixlen,
|
||||
route->gateway, route->if_name);
|
||||
route_entry_destroy(route);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->routes->destroy(this->routes);
|
||||
this->routes_lock->destroy(this->routes_lock);
|
||||
|
||||
if (this->socket != -1)
|
||||
{
|
||||
close(this->socket);
|
||||
}
|
||||
|
||||
net_changes_clear(this);
|
||||
this->net_changes->destroy(this->net_changes);
|
||||
this->net_changes_lock->destroy(this->net_changes_lock);
|
||||
|
||||
enumerator = this->addrs->create_enumerator(this->addrs);
|
||||
while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
|
||||
{
|
||||
@@ -1363,11 +1749,22 @@ kernel_pfroute_net_t *kernel_pfroute_net_create()
|
||||
.addrs = hashtable_create(
|
||||
(hashtable_hash_t)addr_map_entry_hash,
|
||||
(hashtable_equals_t)addr_map_entry_equals, 16),
|
||||
.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),
|
||||
.tuns = linked_list_create(),
|
||||
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
||||
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
|
||||
.routes_lock = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.net_changes_lock = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.vip_wait = lib->settings->get_int(lib->settings,
|
||||
"%s.plugins.kernel-pfroute.vip_wait", 1000, hydra->daemon),
|
||||
);
|
||||
timerclear(&this->last_route_reinstall);
|
||||
timerclear(&this->last_roam);
|
||||
|
||||
/* create a PF_ROUTE socket to communicate with the kernel */
|
||||
this->socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
||||
|
||||
Reference in new issue
Block a user