diff options
-rw-r--r-- | src/charon/config/traffic_selector.c | 20 | ||||
-rw-r--r-- | src/charon/kernel/kernel_interface.c | 23 | ||||
-rw-r--r-- | src/charon/kernel/kernel_interface.h | 8 | ||||
-rw-r--r-- | src/charon/sa/child_sa.c | 45 | ||||
-rw-r--r-- | src/charon/sa/ike_sa.c | 151 |
5 files changed, 155 insertions, 92 deletions
diff --git a/src/charon/config/traffic_selector.c b/src/charon/config/traffic_selector.c index b399074d1..da39c434d 100644 --- a/src/charon/config/traffic_selector.c +++ b/src/charon/config/traffic_selector.c @@ -175,6 +175,7 @@ static int print(FILE *stream, const struct printf_info *info, bool has_proto; bool has_ports; size_t written = 0; + u_int32_t from[4], to[4]; if (this == NULL) { @@ -193,7 +194,11 @@ static int print(FILE *stream, const struct printf_info *info, return written; } - if (this->dynamic) + memset(from, 0, sizeof(from)); + memset(to, 0xFF, sizeof(to)); + if (this->dynamic && + memeq(this->from, from, this->type == TS_IPV4_ADDR_RANGE ? 4 : 16) && + memeq(this->to, to, this->type == TS_IPV4_ADDR_RANGE ? 4 : 16)) { return fprintf(stream, "dynamic/%d", this->type == TS_IPV4_ADDR_RANGE ? 32 : 128); @@ -341,6 +346,7 @@ static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_ /* we have a match in protocol, port, and address: return it... */ new_ts = traffic_selector_create(protocol, this->type, from_port, to_port); new_ts->type = this->type; + new_ts->dynamic = this->dynamic || other->dynamic; memcpy(new_ts->from, from, size); memcpy(new_ts->to, to, size); @@ -475,11 +481,6 @@ static u_int8_t get_protocol(private_traffic_selector_t *this) */ static bool is_host(private_traffic_selector_t *this, host_t *host) { - if (this->dynamic) - { - return TRUE; - } - if (host) { chunk_t addr; @@ -498,7 +499,12 @@ static bool is_host(private_traffic_selector_t *this, host_t *host) } else { - size_t length = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + size_t length = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + if (this->dynamic) + { + return TRUE; + } if (memeq(this->from, this->to, length)) { diff --git a/src/charon/kernel/kernel_interface.c b/src/charon/kernel/kernel_interface.c index 63e265a94..c290df2ea 100644 --- a/src/charon/kernel/kernel_interface.c +++ b/src/charon/kernel/kernel_interface.c @@ -231,6 +231,9 @@ struct addr_entry_t { /** virtual IP managed by us */ bool virtual; + /** scope of the address */ + u_char scope; + /** Number of times this IP is used, if virtual */ u_int refcount; }; @@ -695,6 +698,7 @@ static void process_addr(private_kernel_interface_t *this, addr->ip = host->clone(host); addr->virtual = FALSE; addr->refcount = 1; + addr->scope = msg->ifa_scope; iface->addrs->insert_last(iface->addrs, addr); if (event) @@ -1078,6 +1082,10 @@ static hook_result_t addr_hook(private_kernel_interface_t *this, { /* skip virtual interfaces added by us */ return HOOK_SKIP; } + if (in->scope >= RT_SCOPE_LINK) + { /* skip addresses with a unusable scope */ + return HOOK_SKIP; + } *out = in->ip; return HOOK_NEXT; } @@ -1497,6 +1505,7 @@ static status_t add_ip(private_kernel_interface_t *this, addr->ip = virtual_ip->clone(virtual_ip); addr->refcount = 1; addr->virtual = TRUE; + addr->scope = RT_SCOPE_UNIVERSE; pthread_mutex_lock(&this->mutex); iface->addrs->insert_last(iface->addrs, addr); pthread_mutex_unlock(&this->mutex); @@ -2001,8 +2010,7 @@ static status_t add_policy(private_kernel_interface_t *this, traffic_selector_t *src_ts, traffic_selector_t *dst_ts, policy_dir_t direction, protocol_id_t protocol, - u_int32_t reqid, bool high_prio, mode_t mode, - bool update) + u_int32_t reqid, bool high_prio, mode_t mode) { iterator_t *iterator; policy_entry_t *current, *policy; @@ -2026,12 +2034,9 @@ static status_t add_policy(private_kernel_interface_t *this, policy->direction == current->direction) { /* use existing policy */ - if (!update) - { - current->refcount++; - DBG2(DBG_KNL, "policy %R===%R already exists, increasing ", - "refcount", src_ts, dst_ts); - } + current->refcount++; + DBG2(DBG_KNL, "policy %R===%R already exists, increasing ", + "refcount", src_ts, dst_ts); free(policy); policy = current; found = TRUE; @@ -2318,7 +2323,7 @@ kernel_interface_t *kernel_interface_create() this->public.update_sa = (status_t(*)(kernel_interface_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_t*,host_t*))update_sa; this->public.query_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t*))query_sa; this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa; - this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,mode_t,bool))add_policy; + this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,mode_t))add_policy; this->public.query_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy; this->public.del_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy; this->public.get_interface = (char*(*)(kernel_interface_t*,host_t*))get_interface_name; diff --git a/src/charon/kernel/kernel_interface.h b/src/charon/kernel/kernel_interface.h index 4474e7058..a5d2d071e 100644 --- a/src/charon/kernel/kernel_interface.h +++ b/src/charon/kernel/kernel_interface.h @@ -185,10 +185,6 @@ struct kernel_interface_t { * * A policy is always associated to an SA. Traffic which matches a * policy is handled by the SA with the same reqid. - * If the update flag is set, the policy is updated with the new - * src/dst addresses. - * If the update flag is not set, but a such policy is already in the - * kernel, the reference count to this policy is increased. * * @param this calling object * @param src source address of SA @@ -200,7 +196,6 @@ struct kernel_interface_t { * @param reqid uniqe ID of an SA to use to enforce policy * @param high_prio if TRUE, uses a higher priority than any with FALSE * @param mode mode of SA (tunnel, transport) - * @param update update an existing policy, if TRUE * @return * - SUCCESS * - FAILED if kernel comm failed @@ -210,8 +205,7 @@ struct kernel_interface_t { traffic_selector_t *src_ts, traffic_selector_t *dst_ts, policy_dir_t direction, protocol_id_t protocol, - u_int32_t reqid, bool high_prio, - mode_t mode, bool update); + u_int32_t reqid, bool high_prio, mode_t mode); /** * @brief Query the use time of a policy. diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index 9fe19bcc7..fa62c27c7 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -369,6 +369,7 @@ static void updown(private_child_sa_t *this, bool up) free(other_client); free(virtual_ip); + DBG3(DBG_CHD, "running updown script: %s", command); shell = popen(command, "r"); if (shell == NULL) @@ -676,15 +677,15 @@ static status_t add_policies(private_child_sa_t *this, /* install 3 policies: out, in and forward */ status = charon->kernel_interface->add_policy(charon->kernel_interface, this->me.addr, this->other.addr, my_ts, other_ts, POLICY_OUT, - this->protocol, this->reqid, high_prio, mode, FALSE); + this->protocol, this->reqid, high_prio, mode); status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->other.addr, this->me.addr, other_ts, my_ts, POLICY_IN, - this->protocol, this->reqid, high_prio, mode, FALSE); + this->protocol, this->reqid, high_prio, mode); status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->other.addr, this->me.addr, other_ts, my_ts, POLICY_FWD, - this->protocol, this->reqid, high_prio, mode, FALSE); + this->protocol, this->reqid, high_prio, mode); if (status != SUCCESS) { @@ -780,6 +781,9 @@ static status_t update_hosts(private_child_sa_t *this, return SUCCESS; } + /* run updown script to remove iptables rules */ + updown(this, FALSE); + /* update our (initator) SAs */ if (charon->kernel_interface->update_sa( charon->kernel_interface, this->me.spi, this->protocol, @@ -808,20 +812,39 @@ static status_t update_hosts(private_child_sa_t *this, iterator = this->policies->create_iterator(this->policies, TRUE); while (iterator->iterate(iterator, (void**)&policy)) { + /* remove old policies first */ + charon->kernel_interface->del_policy(charon->kernel_interface, + policy->my_ts, policy->other_ts, POLICY_OUT); + charon->kernel_interface->del_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, POLICY_IN); + charon->kernel_interface->del_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, POLICY_FWD); + + /* check wether we have to update a "dynamic" traffic selector */ + if (!me->ip_equals(me, this->me.addr) && + policy->my_ts->is_host(policy->my_ts, this->me.addr)) + { + policy->my_ts->set_address(policy->my_ts, me); + } + if (!other->ip_equals(other, this->other.addr) && + policy->other_ts->is_host(policy->other_ts, this->other.addr)) + { + policy->other_ts->set_address(policy->other_ts, other); + } + + /* reinstall updated policies */ status = charon->kernel_interface->add_policy( charon->kernel_interface, me, other, policy->my_ts, policy->other_ts, POLICY_OUT, - this->protocol, this->reqid, TRUE, this->mode, TRUE); - + this->protocol, this->reqid, TRUE, this->mode); status |= charon->kernel_interface->add_policy( charon->kernel_interface, other, me, policy->other_ts, policy->my_ts, POLICY_IN, - this->protocol, this->reqid, TRUE, this->mode, TRUE); - + this->protocol, this->reqid, TRUE, this->mode); status |= charon->kernel_interface->add_policy( charon->kernel_interface, other, me, policy->other_ts, policy->my_ts, POLICY_FWD, - this->protocol, this->reqid, TRUE, this->mode, TRUE); + this->protocol, this->reqid, TRUE, this->mode); if (status != SUCCESS) { @@ -832,7 +855,7 @@ static status_t update_hosts(private_child_sa_t *this, iterator->destroy(iterator); } - /* finally apply hosts */ + /* apply hosts */ if (!me->equals(me, this->me.addr)) { this->me.addr->destroy(this->me.addr); @@ -843,6 +866,10 @@ static status_t update_hosts(private_child_sa_t *this, this->other.addr->destroy(this->other.addr); this->other.addr = other->clone(other); } + + /* install new iptables rules */ + updown(this, TRUE); + return SUCCESS; } diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index b422e01d0..dc4786aa1 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -1686,15 +1686,48 @@ static status_t reestablish(private_ike_sa_t *this) return this->task_manager->initiate(this->task_manager); } + +/** + * get a priority for a src/dst connection path + */ +static int get_path_prio(host_t *me, host_t *other) +{ + chunk_t a, b; + int prio = 1; + a = me->get_address(me); + b = other->get_address(other); + + while (a.len > 0 && b.len > 0) + { + if (a.ptr[0] == b.ptr[0]) + { + prio++; + } + else + { + break; + } + a = chunk_skip(a, 1); + b = chunk_skip(b, 1); + } + if (me->get_family(me) == AF_INET) + { + prio *= 4; + } + return prio; +} + + /** * Implementation of ike_sa_t.roam. */ static status_t roam(private_ike_sa_t *this) { iterator_t *iterator; - host_t *me, *other; + host_t *me, *other, *cand_me, *cand_other; ike_mobike_t *mobike; + int prio, best = 0; /* only initiator handles address updated actively */ if (!this->ike_sa_id->is_initiator(this->ike_sa_id)) @@ -1702,79 +1735,77 @@ static status_t roam(private_ike_sa_t *this) return SUCCESS; } + /* get best address pair to use */ + other = this->other_host; me = charon->kernel_interface->get_source_addr(charon->kernel_interface, - this->other_host); - if (me && this->my_virtual_ip && me->ip_equals(me, this->my_virtual_ip)) - { /* do not roam to the virtual IP of this IKE_SA */ - me->destroy(me); - me = NULL; - } - + other); if (me) { - set_condition(this, COND_STALE, FALSE); - /* attachment still the same? */ - if (me->ip_equals(me, this->my_host)) + best = get_path_prio(me, other); + } + iterator = create_additional_address_iterator(this); + while (iterator->iterate(iterator, (void**)&cand_other)) + { + cand_me = charon->kernel_interface->get_source_addr( + charon->kernel_interface, cand_other); + if (!cand_me) { - DBG2(DBG_IKE, "%H still reached through %H, no update needed", - this->other_host, me); - me->destroy(me); - return SUCCESS; + continue; } - me->set_port(me, this->my_host->get_port(this->my_host)); - - /* our attachement changed, update if we have mobike */ - if (supports_extension(this, EXT_MOBIKE)) + if (this->my_virtual_ip && + cand_me->ip_equals(cand_me, this->my_virtual_ip)) + { /* never roam IKE_SA to our virtual IP! */ + cand_me->destroy(cand_me); + continue; + } + prio = get_path_prio(cand_me, cand_other); + if (prio > best) { - DBG1(DBG_IKE, "requesting address change using MOBIKE"); - mobike = ike_mobike_create(&this->public, TRUE); - mobike->roam(mobike, me, NULL); - this->task_manager->queue_task(this->task_manager, (task_t*)mobike); - return this->task_manager->initiate(this->task_manager); + best = prio; + DESTROY_IF(me); + me = cand_me; + other = cand_other; + } + else + { + cand_me->destroy(cand_me); } - DBG1(DBG_IKE, "reestablishing IKE_SA due address change"); - /* reestablish if not */ - set_my_host(this, me); - return reestablish(this); } + iterator->destroy(iterator); - /* there is nothing we can do without mobike */ - if (!supports_extension(this, EXT_MOBIKE)) + if (!me) { + /* no route found to host, set to stale, wait for a new route */ set_condition(this, COND_STALE, TRUE); return FAILED; } - - /* we are unable to reach the peer. Try an alternative address */ - iterator = create_additional_address_iterator(this); - while (iterator->iterate(iterator, (void**)&other)) - { - me = charon->kernel_interface->get_source_addr(charon->kernel_interface, - other); - if (me && me->ip_equals(me, this->my_virtual_ip)) - { /* do not roam to the virtual IP of this IKE_SA */ - me->destroy(me); - me = NULL; - } - - if (me) - { - /* good, we have a new route. Use MOBIKE to update */ - set_condition(this, COND_STALE, FALSE); - iterator->destroy(iterator); - me->set_port(me, this->my_host->get_port(this->my_host)); - other->set_port(other, this->other_host->get_port(this->other_host)); - mobike = ike_mobike_create(&this->public, TRUE); - mobike->roam(mobike, me, other); - this->task_manager->queue_task(this->task_manager, (task_t*)mobike); - return this->task_manager->initiate(this->task_manager); - } + + set_condition(this, COND_STALE, FALSE); + if (me->ip_equals(me, this->my_host) && + other->ip_equals(other, this->other_host)) + { + DBG2(DBG_IKE, "%H still reached through %H, no update needed", + this->other_host, me); + me->destroy(me); + return SUCCESS; } - iterator->destroy(iterator); - - /* no route found to host, give up (temporary) */ - set_condition(this, COND_STALE, TRUE); - return FAILED; + me->set_port(me, this->my_host->get_port(this->my_host)); + other = other->clone(other); + other->set_port(other, this->other_host->get_port(this->other_host)); + + /* update addresses with mobike, if supported ... */ + if (supports_extension(this, EXT_MOBIKE)) + { + DBG1(DBG_IKE, "requesting address change using MOBIKE"); + mobike = ike_mobike_create(&this->public, TRUE); + mobike->roam(mobike, me, other); + this->task_manager->queue_task(this->task_manager, (task_t*)mobike); + return this->task_manager->initiate(this->task_manager); + } + DBG1(DBG_IKE, "reestablishing IKE_SA due address change"); + /* ... reestablish if not */ + set_my_host(this, me); + return reestablish(this); } /** |