diff options
author | Martin Willi <martin@revosec.ch> | 2013-12-13 15:33:42 +0100 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2014-06-04 16:32:08 +0200 |
commit | b714746ef051ccd4ea899d1bf0fe167485312183 (patch) | |
tree | 278ad37c69b5367a1efe0a9172486c2e5a620498 /src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c | |
parent | 0ef0493b4aae7b19ce5f81a9e376566546ee49f6 (diff) | |
download | strongswan-b714746ef051ccd4ea899d1bf0fe167485312183.tar.bz2 strongswan-b714746ef051ccd4ea899d1bf0fe167485312183.tar.xz |
kernel-wfp: Install appropriate routes for tunnel mode policies
Diffstat (limited to 'src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c')
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c | 209 |
1 files changed, 208 insertions, 1 deletions
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c index a7d8a9839..0f78ef665 100644 --- a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c @@ -57,6 +57,11 @@ struct private_kernel_wfp_ipsec_t { hashtable_t *osas; /** + * Installed routes, as route_t => route_t + */ + hashtable_t *routes; + + /** * Mutex for accessing entries */ mutex_t *mutex; @@ -158,6 +163,56 @@ typedef struct { } entry_t; /** + * Installed route + */ +typedef struct { + /** destination net of route */ + host_t *dst; + /** prefix length of dst */ + u_int8_t mask; + /** source address for route */ + host_t *src; + /** gateway of route, NULL if directly attached */ + host_t *gtw; + /** references for route */ + u_int refs; +} route_t; + +/** + * Destroy a route_t + */ +static void destroy_route(route_t *this) +{ + this->dst->destroy(this->dst); + this->src->destroy(this->src); + DESTROY_IF(this->gtw); + free(this); +} + +/** + * Hashtable equals function for routes + */ +static bool equals_route(route_t *a, route_t *b) +{ + return a->mask == b->mask && + a->dst->ip_equals(a->dst, b->dst) && + a->src->ip_equals(a->src, b->src); +} + +/** + * Hashtable hash function for routes + */ +static u_int hash_route(route_t *route) +{ + return chunk_hash_inc(route->src->get_address(route->src), + chunk_hash_inc(route->dst->get_address(route->dst), route->mask)); +} + +/** forward declaration */ +static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry, + bool add); + +/** * Remove a transport or tunnel policy from kernel */ static void cleanup_policy(private_kernel_wfp_ipsec_t *this, bool transport, @@ -195,6 +250,10 @@ static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry) cleanup_policy(this, entry->mode == MODE_TRANSPORT, entry->policy_out); entry->policy_out = 0; } + if (entry->mode == MODE_TUNNEL) + { + manage_routes(this, entry, FALSE); + } } /** @@ -963,11 +1022,157 @@ static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry) } /** + * Reduce refcount, or uninstall a route if all refs gone + */ +static bool uninstall_route(private_kernel_wfp_ipsec_t *this, + host_t *dst, u_int8_t mask, host_t *src, host_t *gtw) +{ + route_t *route, key = { + .dst = dst, + .mask = mask, + .src = src, + }; + char *name; + bool res = FALSE; + + this->mutex->lock(this->mutex); + route = this->routes->get(this->routes, &key); + if (route) + { + if (--route->refs == 0) + { + if (hydra->kernel_interface->get_interface(hydra->kernel_interface, + src, &name)) + { + res = hydra->kernel_interface->del_route(hydra->kernel_interface, + dst->get_address(dst), mask, gtw, src, name) == SUCCESS; + free(name); + } + route = this->routes->remove(this->routes, route); + if (route) + { + destroy_route(route); + } + } + else + { + res = TRUE; + } + } + this->mutex->unlock(this->mutex); + + return res; +} + +/** + * Install a single route, or refcount if exists + */ +static bool install_route(private_kernel_wfp_ipsec_t *this, + host_t *dst, u_int8_t mask, host_t *src, host_t *gtw) +{ + route_t *route, key = { + .dst = dst, + .mask = mask, + .src = src, + }; + char *name; + bool res = FALSE; + + this->mutex->lock(this->mutex); + route = this->routes->get(this->routes, &key); + if (route) + { + route->refs++; + res = TRUE; + } + else + { + if (hydra->kernel_interface->get_interface(hydra->kernel_interface, + src, &name)) + { + if (hydra->kernel_interface->add_route(hydra->kernel_interface, + dst->get_address(dst), mask, gtw, src, name) == SUCCESS) + { + INIT(route, + .dst = dst->clone(dst), + .mask = mask, + .src = src->clone(src), + .gtw = gtw ? gtw->clone(gtw) : NULL, + .refs = 1, + ); + route = this->routes->put(this->routes, route, route); + if (route) + { + destroy_route(route); + } + res = TRUE; + } + free(name); + } + } + this->mutex->unlock(this->mutex); + + return res; +} + +/** + * (Un)-install routes for IPsec policies + */ +static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry, + bool add) +{ + enumerator_t *enumerator; + host_t *src, *dst, *gtw; + sp_entry_t *sp; + u_int8_t mask; + + enumerator = array_create_enumerator(entry->sps); + while (enumerator->enumerate(enumerator, &sp)) + { + if (!sp->dst->to_subnet(sp->dst, &dst, &mask)) + { + continue; + } + if (hydra->kernel_interface->get_address_by_ts( hydra->kernel_interface, + sp->src, &src, NULL) != SUCCESS) + { + dst->destroy(dst); + continue; + } + gtw = hydra->kernel_interface->get_nexthop(hydra->kernel_interface, + entry->remote, entry->local); + if (add) + { + if (!install_route(this, dst, mask, src, gtw)) + { + DBG1(DBG_KNL, "installing route for policy %R === %R failed", + sp->src, sp->dst); + } + } + else + { + if (!uninstall_route(this, dst, mask, src, gtw)) + { + DBG1(DBG_KNL, "uninstalling route for policy %R === %R failed", + sp->src, sp->dst); + } + } + dst->destroy(dst); + src->destroy(src); + DESTROY_IF(gtw); + } + enumerator->destroy(enumerator); + + return TRUE; +} + +/** * Install a tunnel mode SA/SP set to the kernel */ static bool install_tunnel(private_kernel_wfp_ipsec_t *this, entry_t *entry) { if (install_tunnel_sps(this, entry) && + manage_routes(this, entry, TRUE) && install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TUNNEL)) { return TRUE; @@ -1394,6 +1599,7 @@ METHOD(kernel_ipsec_t, destroy, void, this->tsas->destroy(this->tsas); this->isas->destroy(this->isas); this->osas->destroy(this->osas); + this->routes->destroy(this->routes); this->mutex->destroy(this->mutex); free(this); } @@ -1441,10 +1647,11 @@ kernel_wfp_ipsec_t *kernel_wfp_ipsec_create() { 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }}, }, .nextspi = 0xc0000001, - .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), .tsas = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4), .isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4), .osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4), + .routes = hashtable_create((void*)hash_route, (void*)equals_route, 4), ); res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, |