diff options
author | Tobias Brunner <tobias@strongswan.org> | 2011-06-27 11:00:48 +0200 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2011-07-06 09:43:46 +0200 |
commit | 9f49464d56e4d1b127c6b79b2dd891fa29826ec5 (patch) | |
tree | 2ccb699b60596991a9b2c8e3def02d17173eac5a /src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c | |
parent | a07568cf6a6493c17766a6450647a6e27155cda8 (diff) | |
download | strongswan-9f49464d56e4d1b127c6b79b2dd891fa29826ec5.tar.bz2 strongswan-9f49464d56e4d1b127c6b79b2dd891fa29826ec5.tar.xz |
Reduce memory usage of policy history caching.
Only cache data as needed (e.g. traffic selectors only for forward
policies) and at most once for each IPsec SA.
Diffstat (limited to 'src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c')
-rw-r--r-- | src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c | 401 |
1 files changed, 270 insertions, 131 deletions
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index 6a92c4df6..c2540c20c 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -235,6 +235,63 @@ static char* lookup_algorithm(kernel_algorithm_t *list, int ikev2) return NULL; } +typedef struct private_kernel_netlink_ipsec_t private_kernel_netlink_ipsec_t; + +/** + * Private variables and functions of kernel_netlink class. + */ +struct private_kernel_netlink_ipsec_t { + /** + * Public part of the kernel_netlink_t object. + */ + kernel_netlink_ipsec_t public; + + /** + * mutex to lock access to installed policies + */ + mutex_t *mutex; + + /** + * Hash table of installed policies (policy_entry_t) + */ + hashtable_t *policies; + + /** + * Hash table of IPsec SAs using policies (ipsec_sa_t) + */ + hashtable_t *sas; + + /** + * job receiving netlink events + */ + callback_job_t *job; + + /** + * Netlink xfrm socket (IPsec) + */ + netlink_socket_t *socket_xfrm; + + /** + * netlink xfrm socket to receive acquire and expire events + */ + int socket_xfrm_events; + + /** + * whether to install routes along policies + */ + bool install_routes; + + /** + * Size of the replay window, in packets + */ + u_int32_t replay_window; + + /** + * Size of the replay window bitmap, in bytes + */ + u_int32_t replay_bmp; +}; + typedef struct route_entry_t route_entry_t; /** @@ -280,11 +337,101 @@ 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 policy_sa_t policy_sa_t; +typedef struct ipsec_sa_t ipsec_sa_t; /** * IPsec SA assigned to a policy. */ +struct ipsec_sa_t { + /** source address of this SA */ + host_t *src; + + /** destination address of this SA */ + host_t *dst; + + /** optional mark */ + mark_t mark; + + /** description of this SA */ + ipsec_sa_cfg_t cfg; + + /** reference count for this SA */ + refcount_t refcount; +}; + +/** + * Hash function for ipsec_sa_t objects + */ +static u_int ipsec_sa_hash(ipsec_sa_t *sa) +{ + return chunk_hash_inc(sa->src->get_address(sa->src), + chunk_hash_inc(sa->dst->get_address(sa->dst), + chunk_hash_inc(chunk_from_thing(sa->mark), + chunk_hash(chunk_from_thing(sa->cfg))))); +} + +/** + * Equality function for ipsec_sa_t objects + */ +static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa) +{ + return sa->src->ip_equals(sa->src, other_sa->src) && + sa->dst->ip_equals(sa->dst, other_sa->dst) && + memeq(&sa->mark, &other_sa->mark, sizeof(mark_t)) && + memeq(&sa->cfg, &other_sa->cfg, sizeof(ipsec_sa_cfg_t)); +} + +/** + * allocate or reference an IPsec SA object + */ +static ipsec_sa_t *ipsec_sa_create(private_kernel_netlink_ipsec_t *this, + host_t *src, host_t *dst, mark_t mark, + ipsec_sa_cfg_t *cfg) +{ + ipsec_sa_t *sa, *found; + INIT(sa, + .src = src, + .dst = dst, + .mark = mark, + .cfg = *cfg, + ); + found = this->sas->get(this->sas, sa); + if (!found) + { + sa->src = src->clone(src); + sa->dst = dst->clone(dst); + this->sas->put(this->sas, sa, sa); + } + else + { + free(sa); + sa = found; + } + ref_get(&sa->refcount); + return sa; +} + +/** + * release and destroy an IPsec SA object + */ +static void ipsec_sa_destroy(private_kernel_netlink_ipsec_t *this, + ipsec_sa_t *sa) +{ + if (ref_put(&sa->refcount)) + { + this->sas->remove(this->sas, sa); + DESTROY_IF(sa->src); + DESTROY_IF(sa->dst); + free(sa); + } +} + +typedef struct policy_sa_t policy_sa_t; +typedef struct policy_sa_fwd_t policy_sa_fwd_t; + +/** + * Mapping between a policy and an IPsec SA. + */ struct policy_sa_t { /** priority assigned to the policy when installed with this SA */ u_int32_t priority; @@ -292,32 +439,67 @@ struct policy_sa_t { /** type of the policy */ policy_type_t type; - /** source address of this SA */ - host_t *src; + /** assigned SA */ + ipsec_sa_t *sa; +}; - /** destination address of this SA */ - host_t *dst; +/** + * For forward policies we cache the traffic selectors in order to install + * the route. + */ +struct policy_sa_fwd_t { + /** generic interface */ + policy_sa_t generic; - /** source traffic selector of this SA */ + /** source traffic selector of this policy */ traffic_selector_t *src_ts; - /** destination traffic selector of this SA */ + /** destination traffic selector of this policy */ traffic_selector_t *dst_ts; +}; - /** optional mark */ - mark_t mark; +/** + * create a policy_sa(_fwd)_t object + */ +static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this, + policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, mark_t mark, + ipsec_sa_cfg_t *cfg) +{ + policy_sa_t *policy; - /** description of this SA */ - ipsec_sa_cfg_t cfg; -}; + if (dir == POLICY_FWD) + { + policy_sa_fwd_t *fwd; + INIT(fwd, + .src_ts = src_ts->clone(src_ts), + .dst_ts = dst_ts->clone(dst_ts), + ); + policy = &fwd->generic; + } + else + { + INIT(policy); + } + policy->type = type; + policy->sa = ipsec_sa_create(this, src, dst, mark, cfg); + return policy; +} -static void policy_sa_destroy(policy_sa_t *this) +/** + * destroy a policy_sa(_fwd)_t object + */ +static void policy_sa_destroy(private_kernel_netlink_ipsec_t *this, + policy_dir_t dir, policy_sa_t *policy) { - DESTROY_IF(this->src); - DESTROY_IF(this->dst); - DESTROY_IF(this->src_ts); - DESTROY_IF(this->dst_ts); - free(this); + if (dir == POLICY_FWD) + { + policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)policy; + fwd->src_ts->destroy(fwd->src_ts); + fwd->dst_ts->destroy(fwd->dst_ts); + } + ipsec_sa_destroy(this, policy->sa); + free(policy); } typedef struct policy_entry_t policy_entry_t; @@ -339,18 +521,30 @@ struct policy_entry_t { /** associated route installed for this policy */ route_entry_t *route; - /** the SAs this policy is used by, ordered by priority */ - linked_list_t *sas; + /** list of SAs this policy is used by, ordered by priority */ + linked_list_t *used_by; }; -static void policy_entry_destroy(policy_entry_t *this) +static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this, + policy_entry_t *policy) { - if (this->route) + if (policy->route) { - route_entry_destroy(this->route); + route_entry_destroy(policy->route); } - this->sas->destroy_function(this->sas, (void*)policy_sa_destroy); - free(this); + if (policy->used_by) + { + enumerator_t *enumerator; + policy_sa_t *sa; + enumerator = policy->used_by->create_enumerator(policy->used_by); + while (enumerator->enumerate(enumerator, (void**)&sa)) + { + policy_sa_destroy(this, policy->direction, sa); + } + enumerator->destroy(enumerator); + policy->used_by->destroy(policy->used_by); + } + free(policy); } /** @@ -373,58 +567,6 @@ static bool policy_equals(policy_entry_t *key, policy_entry_t *other_key) key->direction == other_key->direction; } -typedef struct private_kernel_netlink_ipsec_t private_kernel_netlink_ipsec_t; - -/** - * Private variables and functions of kernel_netlink class. - */ -struct private_kernel_netlink_ipsec_t { - /** - * Public part of the kernel_netlink_t object. - */ - kernel_netlink_ipsec_t public; - - /** - * mutex to lock access to installed policies - */ - mutex_t *mutex; - - /** - * Hash table of installed policies (policy_entry_t) - */ - hashtable_t *policies; - - /** - * job receiving netlink events - */ - callback_job_t *job; - - /** - * Netlink xfrm socket (IPsec) - */ - netlink_socket_t *socket_xfrm; - - /** - * netlink xfrm socket to receive acquire and expire events - */ - int socket_xfrm_events; - - /** - * whether to install routes along policies - */ - bool install_routes; - - /** - * Size of the replay window, in packets - */ - u_int32_t replay_window; - - /** - * Size of the replay window bitmap, in bytes - */ - u_int32_t replay_bmp; -}; - /** * convert the general ipsec mode to the one defined in xfrm.h */ @@ -1782,10 +1924,11 @@ failed: * Note: The mutex has to be locked when entering this function. */ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, - policy_entry_t *policy, policy_sa_t *sa, bool update) + policy_entry_t *policy, policy_sa_t *mapping, bool update) { netlink_buf_t request; policy_entry_t clone; + ipsec_sa_t *ipsec = mapping->sa; struct xfrm_userpolicy_info *policy_info; struct nlmsghdr *hdr; int i; @@ -1804,9 +1947,9 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, policy_info->dir = policy->direction; /* calculate priority based on selector size, small size = high prio */ - policy_info->priority = sa->priority; - policy_info->action = sa->type != POLICY_DROP ? XFRM_POLICY_ALLOW - : XFRM_POLICY_BLOCK; + policy_info->priority = mapping->priority; + policy_info->action = mapping->type != POLICY_DROP ? XFRM_POLICY_ALLOW + : XFRM_POLICY_BLOCK; policy_info->share = XFRM_SHARE_ANY; /* policies don't expire */ @@ -1821,18 +1964,18 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_info); - if (sa->type == POLICY_IPSEC) + if (mapping->type == POLICY_IPSEC) { struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr); struct { u_int8_t proto; bool use; } protos[] = { - { IPPROTO_COMP, sa->cfg.ipcomp.transform != IPCOMP_NONE }, - { IPPROTO_ESP, sa->cfg.esp.use }, - { IPPROTO_AH, sa->cfg.ah.use }, + { IPPROTO_COMP, ipsec->cfg.ipcomp.transform != IPCOMP_NONE }, + { IPPROTO_ESP, ipsec->cfg.esp.use }, + { IPPROTO_AH, ipsec->cfg.ah.use }, }; - ipsec_mode_t proto_mode = sa->cfg.mode; + ipsec_mode_t proto_mode = ipsec->cfg.mode; rthdr->rta_type = XFRMA_TMPL; rthdr->rta_len = 0; /* actual length is set below */ @@ -1851,18 +1994,18 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, return FAILED; } - tmpl->reqid = sa->cfg.reqid; + tmpl->reqid = ipsec->cfg.reqid; tmpl->id.proto = protos[i].proto; tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; tmpl->mode = mode2kernel(proto_mode); tmpl->optional = protos[i].proto == IPPROTO_COMP && policy->direction != POLICY_OUT; - tmpl->family = sa->src->get_family(sa->src); + tmpl->family = ipsec->src->get_family(ipsec->src); if (proto_mode == MODE_TUNNEL) { /* only for tunnel mode */ - host2xfrm(sa->src, &tmpl->saddr); - host2xfrm(sa->dst, &tmpl->id.daddr); + host2xfrm(ipsec->src, &tmpl->saddr); + host2xfrm(ipsec->dst, &tmpl->id.daddr); } tmpl++; @@ -1874,7 +2017,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, rthdr = XFRM_RTA_NEXT(rthdr); } - if (sa->mark.value) + if (ipsec->mark.value) { struct xfrm_mark *mrk; @@ -1888,8 +2031,8 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, } mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = sa->mark.value; - mrk->m = sa->mark.mask; + mrk->v = ipsec->mark.value; + mrk->m = ipsec->mark.mask; } this->mutex->unlock(this->mutex); @@ -1902,8 +2045,9 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, this->mutex->lock(this->mutex); policy = this->policies->get(this->policies, &clone); if (!policy || - policy->sas->find_first(policy->sas, NULL, (void**)&sa) != SUCCESS) - { /* policy or sa is already gone, ignore */ + policy->used_by->find_first(policy->used_by, + NULL, (void**)&mapping) != SUCCESS) + { /* policy or mapping is already gone, ignore */ this->mutex->unlock(this->mutex); return SUCCESS; } @@ -1914,19 +2058,20 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, * - routing is not disabled via strongswan.conf */ if (policy->direction == POLICY_FWD && - sa->cfg.mode != MODE_TRANSPORT && this->install_routes) + ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) { route_entry_t *route = malloc_thing(route_entry_t); + policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping; if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - sa->dst_ts, &route->src_ip) == SUCCESS) + fwd->dst_ts, &route->src_ip) == SUCCESS) { - /* get the nexthop to src (src as we are in POLICY_FWD).*/ + /* get the nexthop to src (src as we are in POLICY_FWD) */ route->gateway = hydra->kernel_interface->get_nexthop( - hydra->kernel_interface, sa->src); + hydra->kernel_interface, ipsec->src); /* install route via outgoing interface */ route->if_name = hydra->kernel_interface->get_interface( - hydra->kernel_interface, sa->dst); + hydra->kernel_interface, ipsec->dst); route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); memcpy(route->dst_net.ptr, &policy->sel.saddr, route->dst_net.len); route->prefixlen = policy->sel.prefixlen_s; @@ -1953,8 +2098,8 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, old->src_ip, old->if_name) != SUCCESS) { DBG1(DBG_KNL, "error uninstalling route installed with " - "policy %R === %R %N", sa->src_ts, - sa->dst_ts, policy_dir_names, + "policy %R === %R %N", fwd->src_ts, + fwd->dst_ts, policy_dir_names, policy->direction); } route_entry_destroy(old); @@ -1962,7 +2107,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, } DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", - sa->src_ts, route->gateway, route->src_ip, route->if_name); + fwd->src_ts, route->gateway, route->src_ip, route->if_name); switch (hydra->kernel_interface->add_route( hydra->kernel_interface, route->dst_net, route->prefixlen, route->gateway, @@ -2007,7 +2152,6 @@ METHOD(kernel_ipsec_t, add_policy, status_t, .sel = ts2selector(src_ts, dst_ts), .mark = mark.value & mark.mask, .direction = direction, - .sas = linked_list_create(), ); /* find the policy, which matches EXACTLY */ @@ -2029,25 +2173,19 @@ METHOD(kernel_ipsec_t, add_policy, status_t, "already exists, increasing refcount", src_ts, dst_ts, policy_dir_names, direction); } - policy_entry_destroy(policy); + policy_entry_destroy(this, policy); policy = current; found = TRUE; } else - { /* apply the new one, if we have no such policy */ + { /* use the new one, if we have no such policy */ + policy->used_by = linked_list_create(); this->policies->put(this->policies, policy, policy); } /* cache the assigned IPsec SA */ - INIT(assigned_sa, - .type = type, - .src = src->clone(src), - .dst = dst->clone(dst), - .src_ts = src_ts->clone(src_ts), - .dst_ts = dst_ts->clone(dst_ts), - .mark = mark, - .cfg = *sa, - ); + assigned_sa = policy_sa_create(this, direction, type, src, dst, src_ts, + dst_ts, mark, sa); /* calculate priority based on selector size, small size = high prio */ assigned_sa->priority = routed ? PRIO_LOW : PRIO_HIGH; @@ -2059,7 +2197,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, assigned_sa->priority += policy->sel.proto ? 0 : 1; /* insert the SA according to its priority */ - enumerator = policy->sas->create_enumerator(policy->sas); + enumerator = policy->used_by->create_enumerator(policy->used_by); while (enumerator->enumerate(enumerator, (void**)¤t_sa)) { if (current_sa->priority >= assigned_sa->priority) @@ -2068,7 +2206,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, } update = FALSE; } - policy->sas->insert_before(policy->sas, enumerator, assigned_sa); + policy->used_by->insert_before(policy->used_by, enumerator, assigned_sa); enumerator->destroy(enumerator); if (!update) @@ -2211,7 +2349,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, { policy_entry_t *current, policy; enumerator_t *enumerator; - policy_sa_t *sa; + policy_sa_t *mapping; netlink_buf_t request; struct nlmsghdr *hdr; struct xfrm_userpolicy_id *policy_id; @@ -2255,29 +2393,28 @@ METHOD(kernel_ipsec_t, del_policy, status_t, return NOT_FOUND; } - /* remove cached SA by reqid */ - enumerator = current->sas->create_enumerator(current->sas); - while (enumerator->enumerate(enumerator, (void**)&sa)) + /* remove mapping to SA by reqid */ + enumerator = current->used_by->create_enumerator(current->used_by); + while (enumerator->enumerate(enumerator, (void**)&mapping)) { - if (reqid == sa->cfg.reqid) + if (reqid == mapping->sa->cfg.reqid) { - current->sas->remove_at(current->sas, enumerator); + current->used_by->remove_at(current->used_by, enumerator); + policy_sa_destroy(this, direction, mapping); break; } is_installed = FALSE; } enumerator->destroy(enumerator); - if (current->sas->get_count(current->sas) > 0) + if (current->used_by->get_count(current->used_by) > 0) { /* policy is used by more SAs, keep in kernel */ DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed"); if (!is_installed) { /* no need to update as the policy was not installed for this SA */ this->mutex->unlock(this->mutex); - policy_sa_destroy(sa); return SUCCESS; } - policy_sa_destroy(sa); if (mark.value) { @@ -2291,8 +2428,8 @@ METHOD(kernel_ipsec_t, del_policy, status_t, src_ts, dst_ts, policy_dir_names, direction); } - current->sas->get_first(current->sas, (void**)&sa); - if (add_policy_internal(this, current, sa, TRUE) != SUCCESS) + current->used_by->get_first(current->used_by, (void**)&mapping); + if (add_policy_internal(this, current, mapping, TRUE) != SUCCESS) { DBG1(DBG_KNL, "unable to update policy %R === %R %N", src_ts, dst_ts, policy_dir_names, direction); @@ -2300,7 +2437,6 @@ METHOD(kernel_ipsec_t, del_policy, status_t, } return SUCCESS; } - policy_sa_destroy(sa); memset(&request, 0, sizeof(request)); @@ -2345,7 +2481,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, } this->policies->remove(this->policies, current); - policy_entry_destroy(current); + policy_entry_destroy(this, current); this->mutex->unlock(this->mutex); if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) @@ -2425,10 +2561,11 @@ METHOD(kernel_ipsec_t, destroy, void, enumerator = this->policies->create_enumerator(this->policies); while (enumerator->enumerate(enumerator, &policy, &policy)) { - policy_entry_destroy(policy); + policy_entry_destroy(this, policy); } enumerator->destroy(enumerator); this->policies->destroy(this->policies); + this->sas->destroy(this->sas); this->mutex->destroy(this->mutex); free(this); } @@ -2460,6 +2597,8 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() }, .policies = hashtable_create((hashtable_hash_t)policy_hash, (hashtable_equals_t)policy_equals, 32), + .sas = hashtable_create((hashtable_hash_t)ipsec_sa_hash, + (hashtable_equals_t)ipsec_sa_equals, 32), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .install_routes = lib->settings->get_bool(lib->settings, "%s.install_routes", TRUE, hydra->daemon), |