diff --git a/conf/plugins/kernel-netlink.opt b/conf/plugins/kernel-netlink.opt index 3d9c4a7a9..211add85c 100644 --- a/conf/plugins/kernel-netlink.opt +++ b/conf/plugins/kernel-netlink.opt @@ -47,6 +47,13 @@ charon.plugins.kernel-netlink.port_bypass = no port based policies use global XFRM bypass policies for the used IKE UDP ports. +charon.plugins.kernel-netlink.process_rules = no + Whether to process changes in routing rules to trigger roam events. + + Whether to process changes in routing rules to trigger roam events. This is + currently only useful if the kernel based route lookup is used (i.e. if + route installation is disabled or an inverted fwmark match is configured). + charon.plugins.kernel-netlink.receive_buffer_size = 0 Maximum Netlink socket receive buffer in bytes. diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c index e4ded3225..c3f92f500 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2016 Tobias Brunner + * Copyright (C) 2008-2018 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -78,6 +78,9 @@ #define ROUTING_TABLE_PRIO 0 #endif +/** multicast groups (for groups > 31 setsockopt has to be used) */ +#define nl_group(group) (1 << (group - 1)) + ENUM(rt_msg_names, RTM_NEWLINK, RTM_GETRULE, "RTM_NEWLINK", "RTM_DELLINK", @@ -472,6 +475,11 @@ struct private_kernel_netlink_net_t { */ bool process_route; + /** + * whether to react to RTM_NEWRULE or RTM_DELRULE events + */ + bool process_rules; + /** * whether to trigger roam events */ @@ -1451,6 +1459,45 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h host->destroy(host); } +/** + * process RTM_NEW|DELRULE from kernel + */ +static void process_rule(private_kernel_netlink_net_t *this, struct nlmsghdr *hdr) +{ +#ifdef HAVE_LINUX_FIB_RULES_H + struct rtmsg* msg = NLMSG_DATA(hdr); + struct rtattr *rta = RTM_RTA(msg); + size_t rtasize = RTM_PAYLOAD(hdr); + uint32_t table = 0; + + /* ignore rules added by us or in the local routing table (local addrs) */ + if (msg->rtm_table && (msg->rtm_table == this->routing_table || + msg->rtm_table == RT_TABLE_LOCAL)) + { + return; + } + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case FRA_TABLE: + if (RTA_PAYLOAD(rta) == sizeof(table)) + { + table = *(uint32_t*)RTA_DATA(rta); + } + break; + } + rta = RTA_NEXT(rta, rtasize); + } + if (table && table == this->routing_table) + { /* also check against extended table ID */ + return; + } + fire_roam_event(this, FALSE); +#endif +} + /** * Receives events from kernel */ @@ -1508,6 +1555,13 @@ static bool receive_events(private_kernel_netlink_net_t *this, int fd, process_route(this, hdr); } break; + case RTM_NEWRULE: + case RTM_DELRULE: + if (this->process_rules) + { + process_rule(this, hdr); + } + break; default: break; } @@ -2985,6 +3039,8 @@ kernel_netlink_net_t *kernel_netlink_net_create() "%s.prefer_temporary_addrs", FALSE, lib->ns), .roam_events = lib->settings->get_bool(lib->settings, "%s.plugins.kernel-netlink.roam_events", TRUE, lib->ns), + .process_rules = lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.process_rules", FALSE, lib->ns), .mtu = lib->settings->get_int(lib->settings, "%s.plugins.kernel-netlink.mtu", 0, lib->ns), .mss = lib->settings->get_int(lib->settings, @@ -3037,8 +3093,19 @@ kernel_netlink_net_t *kernel_netlink_net_create() destroy(this); return NULL; } - addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | - RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_LINK; + addr.nl_groups = nl_group(RTNLGRP_IPV4_IFADDR) | + nl_group(RTNLGRP_IPV6_IFADDR) | + nl_group(RTNLGRP_LINK); + if (this->process_route) + { + addr.nl_groups |= nl_group(RTNLGRP_IPV4_ROUTE) | + nl_group(RTNLGRP_IPV6_ROUTE); + } + if (this->process_rules) + { + addr.nl_groups |= nl_group(RTNLGRP_IPV4_RULE) | + nl_group(RTNLGRP_IPV6_RULE); + } if (bind(this->socket_events, (struct sockaddr*)&addr, sizeof(addr))) { DBG1(DBG_KNL, "unable to bind RT event socket: %s (%d)", diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 91da4d3e6..a01ee9e4d 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2017 Tobias Brunner + * Copyright (C) 2006-2018 Tobias Brunner * Copyright (C) 2016 Andreas Steffen * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger @@ -1249,17 +1249,6 @@ METHOD(child_sa_t, install_policies, status_t, enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) { - /* install outbound drop policy to avoid packets leaving unencrypted - * when updating policies */ - if (priority == POLICY_PRIORITY_DEFAULT && manual_prio == 0 && - require_policy_update() && install_outbound) - { - status |= install_policies_outbound(this, this->my_addr, - this->other_addr, my_ts, other_ts, - &my_sa, &other_sa, POLICY_DROP, - POLICY_PRIORITY_FALLBACK, 0); - } - status |= install_policies_inbound(this, this->my_addr, this->other_addr, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, @@ -1350,15 +1339,6 @@ METHOD(child_sa_t, install_outbound, status_t, enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) { - /* install outbound drop policy to avoid packets leaving unencrypted - * when updating policies */ - if (manual_prio == 0 && require_policy_update()) - { - status |= install_policies_outbound(this, this->my_addr, - this->other_addr, my_ts, other_ts, - &my_sa, &other_sa, POLICY_DROP, - POLICY_PRIORITY_FALLBACK, 0); - } status |= install_policies_outbound(this, this->my_addr, this->other_addr, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, @@ -1407,12 +1387,6 @@ METHOD(child_sa_t, remove_outbound, void, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, POLICY_PRIORITY_DEFAULT, manual_prio); - if (manual_prio == 0 && require_policy_update()) - { - del_policies_outbound(this, this->my_addr, this->other_addr, - my_ts, other_ts, &my_sa, &other_sa, - POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0); - } } enumerator->destroy(enumerator); } @@ -1458,8 +1432,65 @@ CALLBACK(reinstall_vip, void, } } +/** + * Update addresses and encap state of IPsec SAs in the kernel + */ +static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other, + bool encap) +{ + /* update our (initiator) SA */ + if (this->my_spi) + { + kernel_ipsec_sa_id_t id = { + .src = this->other_addr, + .dst = this->my_addr, + .spi = this->my_spi, + .proto = proto_ike2ip(this->protocol), + .mark = mark_in_sa(this), + }; + kernel_ipsec_update_sa_t sa = { + .cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, + .new_src = other, + .new_dst = me, + .encap = this->encap, + .new_encap = encap, + }; + if (charon->kernel->update_sa(charon->kernel, &id, + &sa) == NOT_SUPPORTED) + { + return NOT_SUPPORTED; + } + } + + /* update his (responder) SA */ + if (this->other_spi) + { + kernel_ipsec_sa_id_t id = { + .src = this->my_addr, + .dst = this->other_addr, + .spi = this->other_spi, + .proto = proto_ike2ip(this->protocol), + .mark = this->mark_out, + }; + kernel_ipsec_update_sa_t sa = { + .cpi = this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, + .new_src = me, + .new_dst = other, + .encap = this->encap, + .new_encap = encap, + }; + if (charon->kernel->update_sa(charon->kernel, &id, + &sa) == NOT_SUPPORTED) + { + return NOT_SUPPORTED; + } + } + /* we currently ignore the actual return values above */ + return SUCCESS; +} + METHOD(child_sa_t, update, status_t, - private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips, + private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips, bool encap) { child_sa_state_t old; @@ -1478,84 +1509,50 @@ METHOD(child_sa_t, update, status_t, this->config->has_option(this->config, OPT_PROXY_MODE); - if (!transport_proxy_mode) - { - /* update our (initiator) SA */ - if (this->my_spi) - { - kernel_ipsec_sa_id_t id = { - .src = this->other_addr, - .dst = this->my_addr, - .spi = this->my_spi, - .proto = proto_ike2ip(this->protocol), - .mark = mark_in_sa(this), - }; - kernel_ipsec_update_sa_t sa = { - .cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, - .new_src = other, - .new_dst = me, - .encap = this->encap, - .new_encap = encap, - }; - if (charon->kernel->update_sa(charon->kernel, &id, - &sa) == NOT_SUPPORTED) - { - set_state(this, old); - return NOT_SUPPORTED; - } - } - - /* update his (responder) SA */ - if (this->other_spi) - { - kernel_ipsec_sa_id_t id = { - .src = this->my_addr, - .dst = this->other_addr, - .spi = this->other_spi, - .proto = proto_ike2ip(this->protocol), - .mark = this->mark_out, - }; - kernel_ipsec_update_sa_t sa = { - .cpi = this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, - .new_src = me, - .new_dst = other, - .encap = this->encap, - .new_encap = encap, - }; - if (charon->kernel->update_sa(charon->kernel, &id, - &sa) == NOT_SUPPORTED) - { - set_state(this, old); - return NOT_SUPPORTED; - } - } - } - if (!this->config->has_option(this->config, OPT_NO_POLICIES) && require_policy_update()) { - if (!me->ip_equals(me, this->my_addr) || - !other->ip_equals(other, this->other_addr)) + ipsec_sa_cfg_t my_sa, other_sa; + enumerator_t *enumerator; + traffic_selector_t *my_ts, *other_ts; + uint32_t manual_prio; + status_t state; + + prepare_sa_cfg(this, &my_sa, &other_sa); + manual_prio = this->config->get_manual_prio(this->config); + + enumerator = create_policy_enumerator(this); + while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) { - ipsec_sa_cfg_t my_sa, other_sa; - enumerator_t *enumerator; - traffic_selector_t *my_ts, *other_ts; - uint32_t manual_prio; + /* install drop policy to avoid traffic leaks, acquires etc. */ + install_policies_outbound(this, this->my_addr, this->other_addr, + my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, + POLICY_PRIORITY_DEFAULT, manual_prio); - prepare_sa_cfg(this, &my_sa, &other_sa); - manual_prio = this->config->get_manual_prio(this->config); + /* remove old policies */ + del_policies_internal(this, this->my_addr, this->other_addr, + my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, + POLICY_PRIORITY_DEFAULT, manual_prio); + } + enumerator->destroy(enumerator); - /* always use high priorities, as hosts getting updated are INSTALLED */ - enumerator = create_policy_enumerator(this); - while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) + /* update the IPsec SAs */ + state = update_sas(this, me, other, encap); + + enumerator = create_policy_enumerator(this); + while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) + { + traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL; + + /* reinstall the previous policies if we can't update the SAs */ + if (state == NOT_SUPPORTED) + { + install_policies_internal(this, this->my_addr, this->other_addr, + my_ts, other_ts, &my_sa, &other_sa, + POLICY_IPSEC, POLICY_PRIORITY_DEFAULT, manual_prio); + } + else { - traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL; - - /* remove old policies first */ - del_policies_internal(this, this->my_addr, this->other_addr, - my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, - POLICY_PRIORITY_DEFAULT, manual_prio); - /* check if we have to update a "dynamic" traffic selector */ if (!me->ip_equals(me, this->my_addr) && my_ts->is_host(my_ts, this->my_addr)) @@ -1578,23 +1575,32 @@ METHOD(child_sa_t, update, status_t, install_policies_internal(this, me, other, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, POLICY_PRIORITY_DEFAULT, manual_prio); - - /* update fallback policies after the new policy is in place */ - if (manual_prio == 0) - { - del_policies_outbound(this, this->my_addr, this->other_addr, - old_my_ts ?: my_ts, - old_other_ts ?: other_ts, - &my_sa, &other_sa, POLICY_DROP, - POLICY_PRIORITY_FALLBACK, 0); - install_policies_outbound(this, me, other, my_ts, other_ts, - &my_sa, &other_sa, POLICY_DROP, - POLICY_PRIORITY_FALLBACK, 0); - } - DESTROY_IF(old_my_ts); - DESTROY_IF(old_other_ts); } - enumerator->destroy(enumerator); + /* remove the drop policy */ + del_policies_outbound(this, this->my_addr, this->other_addr, + old_my_ts ?: my_ts, + old_other_ts ?: other_ts, + &my_sa, &other_sa, POLICY_DROP, + POLICY_PRIORITY_DEFAULT, 0); + + DESTROY_IF(old_my_ts); + DESTROY_IF(old_other_ts); + } + enumerator->destroy(enumerator); + + if (state == NOT_SUPPORTED) + { + set_state(this, old); + return NOT_SUPPORTED; + } + + } + else if (!transport_proxy_mode) + { + if (update_sas(this, me, other, encap) == NOT_SUPPORTED) + { + set_state(this, old); + return NOT_SUPPORTED; } } @@ -1655,13 +1661,6 @@ METHOD(child_sa_t, destroy, void, del_policies_inbound(this, this->my_addr, this->other_addr, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, priority, manual_prio); - if (!this->trap && manual_prio == 0 && require_policy_update() && - del_outbound) - { - del_policies_outbound(this, this->my_addr, this->other_addr, - my_ts, other_ts, &my_sa, &other_sa, - POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0); - } } enumerator->destroy(enumerator); } diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 4123cc49d..e1f4ec95a 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -231,11 +231,6 @@ struct private_ike_sa_t { */ chunk_t nat_detection_dest; - /** - * number pending UPDATE_SA_ADDRESS (MOBIKE) - */ - uint32_t pending_updates; - /** * NAT keep alive interval */ @@ -734,8 +729,11 @@ METHOD(ike_sa_t, set_condition, void, switch (condition) { case COND_NAT_HERE: - case COND_NAT_FAKE: case COND_NAT_THERE: + DBG1(DBG_IKE, "%s host is not behind NAT anymore", + condition == COND_NAT_HERE ? "local" : "remote"); + /* fall-through */ + case COND_NAT_FAKE: set_condition(this, COND_NAT_ANY, has_condition(this, COND_NAT_HERE) || has_condition(this, COND_NAT_THERE) || @@ -1052,18 +1050,6 @@ METHOD(ike_sa_t, has_mapping_changed, bool, return TRUE; } -METHOD(ike_sa_t, set_pending_updates, void, - private_ike_sa_t *this, uint32_t updates) -{ - this->pending_updates = updates; -} - -METHOD(ike_sa_t, get_pending_updates, uint32_t, - private_ike_sa_t *this) -{ - return this->pending_updates; -} - METHOD(ike_sa_t, float_ports, void, private_ike_sa_t *this) { @@ -2970,8 +2956,6 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .supports_extension = _supports_extension, .set_condition = _set_condition, .has_condition = _has_condition, - .set_pending_updates = _set_pending_updates, - .get_pending_updates = _get_pending_updates, .create_peer_address_enumerator = _create_peer_address_enumerator, .add_peer_address = _add_peer_address, .clear_peer_addresses = _clear_peer_addresses, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index fbc367292..2429fae8a 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -646,20 +646,6 @@ struct ike_sa_t { */ bool (*has_condition) (ike_sa_t *this, ike_condition_t condition); - /** - * Get the number of queued MOBIKE address updates. - * - * @return number of pending updates - */ - uint32_t (*get_pending_updates)(ike_sa_t *this); - - /** - * Set the number of queued MOBIKE address updates. - * - * @param updates number of pending updates - */ - void (*set_pending_updates)(ike_sa_t *this, uint32_t updates); - #ifdef ME /** * Activate mediation server functionality for this IKE_SA. diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 361eb0fe1..2438616db 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2016 Tobias Brunner + * Copyright (C) 2007-2018 Tobias Brunner * Copyright (C) 2007-2010 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -1642,24 +1642,9 @@ METHOD(task_manager_t, process_message, status_t, METHOD(task_manager_t, queue_task_delayed, void, private_task_manager_t *this, task_t *task, uint32_t delay) { - enumerator_t *enumerator; queued_task_t *queued; timeval_t time; - if (task->get_type(task) == TASK_IKE_MOBIKE) - { /* there is no need to queue more than one mobike task */ - enumerator = array_create_enumerator(this->queued_tasks); - while (enumerator->enumerate(enumerator, &queued)) - { - if (queued->task->get_type(queued->task) == TASK_IKE_MOBIKE) - { - enumerator->destroy(enumerator); - task->destroy(task); - return; - } - } - enumerator->destroy(enumerator); - } time_monotonic(&time); if (delay) { @@ -1877,12 +1862,41 @@ METHOD(task_manager_t, queue_ike_delete, void, queue_task(this, (task_t*)ike_delete_create(this->ike_sa, TRUE)); } +/** + * There is no need to queue more than one mobike task, so this either returns + * an already queued task or queues one if there is none yet. + */ +static ike_mobike_t *queue_mobike_task(private_task_manager_t *this) +{ + enumerator_t *enumerator; + queued_task_t *queued; + ike_mobike_t *mobike = NULL; + + enumerator = array_create_enumerator(this->queued_tasks); + while (enumerator->enumerate(enumerator, &queued)) + { + if (queued->task->get_type(queued->task) == TASK_IKE_MOBIKE) + { + mobike = (ike_mobike_t*)queued->task; + break; + } + } + enumerator->destroy(enumerator); + + if (!mobike) + { + mobike = ike_mobike_create(this->ike_sa, TRUE); + queue_task(this, &mobike->task); + } + return mobike; +} + METHOD(task_manager_t, queue_mobike, void, private_task_manager_t *this, bool roam, bool address) { ike_mobike_t *mobike; - mobike = ike_mobike_create(this->ike_sa, TRUE); + mobike = queue_mobike_task(this); if (roam) { enumerator_t *enumerator; @@ -1909,7 +1923,31 @@ METHOD(task_manager_t, queue_mobike, void, { mobike->addresses(mobike); } - queue_task(this, &mobike->task); +} + +METHOD(task_manager_t, queue_dpd, void, + private_task_manager_t *this) +{ + ike_mobike_t *mobike; + + if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE) && + this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE)) + { +#ifdef ME + peer_cfg_t *cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + if (cfg->get_peer_id(cfg) || + this->ike_sa->has_condition(this->ike_sa, COND_ORIGINAL_INITIATOR)) +#else + if (this->ike_sa->has_condition(this->ike_sa, COND_ORIGINAL_INITIATOR)) +#endif + { + /* use mobike enabled DPD to detect NAT mapping changes */ + mobike = queue_mobike_task(this); + mobike->dpd(mobike); + return; + } + } + queue_task(this, (task_t*)ike_dpd_create(TRUE)); } METHOD(task_manager_t, queue_child, void, @@ -1940,32 +1978,6 @@ METHOD(task_manager_t, queue_child_delete, void, protocol, spi, expired)); } -METHOD(task_manager_t, queue_dpd, void, - private_task_manager_t *this) -{ - ike_mobike_t *mobike; - - if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE) && - this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE)) - { -#ifdef ME - peer_cfg_t *cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - if (cfg->get_peer_id(cfg) || - this->ike_sa->has_condition(this->ike_sa, COND_ORIGINAL_INITIATOR)) -#else - if (this->ike_sa->has_condition(this->ike_sa, COND_ORIGINAL_INITIATOR)) -#endif - { - /* use mobike enabled DPD to detect NAT mapping changes */ - mobike = ike_mobike_create(this->ike_sa, TRUE); - mobike->dpd(mobike); - queue_task(this, &mobike->task); - return; - } - } - queue_task(this, (task_t*)ike_dpd_create(TRUE)); -} - METHOD(task_manager_t, adopt_tasks, void, private_task_manager_t *this, task_manager_t *other_public) { diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c index dc0f24fb8..fe41a1cac 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c @@ -1,7 +1,7 @@ /* - * Copyright (C) 2010-2014 Tobias Brunner + * Copyright (C) 2010-2018 Tobias Brunner * Copyright (C) 2007 Martin Willi - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -76,13 +76,35 @@ struct private_ike_mobike_t { * additional addresses got updated */ bool addresses_updated; - - /** - * whether the pending updates counter was increased - */ - bool pending_update; }; +/** + * Check if a newer MOBIKE update task is queued + */ +static bool is_newer_update_queued(private_ike_mobike_t *this) +{ + enumerator_t *enumerator; + private_ike_mobike_t *mobike; + task_t *task; + bool found = FALSE; + + enumerator = this->ike_sa->create_task_enumerator(this->ike_sa, + TASK_QUEUE_QUEUED); + while (enumerator->enumerate(enumerator, &task)) + { + if (task->get_type(task) == TASK_IKE_MOBIKE) + { + mobike = (private_ike_mobike_t*)task; + /* a queued check or update might invalidate the results of the + * current task */ + found = mobike->check || mobike->update; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + /** * read notifys from message and evaluate them */ @@ -526,9 +548,8 @@ METHOD(task_t, process_i, status_t, } else if (message->get_exchange_type(message) == INFORMATIONAL) { - if (this->ike_sa->get_pending_updates(this->ike_sa) > 1) + if (is_newer_update_queued(this)) { - /* newer update queued, ignore this one */ return SUCCESS; } if (this->cookie2.ptr) @@ -553,7 +574,7 @@ METHOD(task_t, process_i, status_t, if (this->natd) { this->natd->task.process(&this->natd->task, message); - if (this->natd->has_mapping_changed(this->natd)) + if (!this->update && this->natd->has_mapping_changed(this->natd)) { /* force an update if mappings have changed */ this->update = this->check = TRUE; @@ -615,25 +636,13 @@ METHOD(ike_mobike_t, addresses, void, private_ike_mobike_t *this) { this->address = TRUE; - if (!this->pending_update) - { - this->pending_update = TRUE; - this->ike_sa->set_pending_updates(this->ike_sa, - this->ike_sa->get_pending_updates(this->ike_sa) + 1); - } } METHOD(ike_mobike_t, roam, void, private_ike_mobike_t *this, bool address) { this->check = TRUE; - this->address = address; - if (!this->pending_update) - { - this->pending_update = TRUE; - this->ike_sa->set_pending_updates(this->ike_sa, - this->ike_sa->get_pending_updates(this->ike_sa) + 1); - } + this->address |= address; } METHOD(ike_mobike_t, dpd, void, @@ -643,12 +652,6 @@ METHOD(ike_mobike_t, dpd, void, { this->natd = ike_natd_create(this->ike_sa, this->initiator); } - if (!this->pending_update) - { - this->pending_update = TRUE; - this->ike_sa->set_pending_updates(this->ike_sa, - this->ike_sa->get_pending_updates(this->ike_sa) + 1); - } } METHOD(ike_mobike_t, is_probing, bool, @@ -678,21 +681,11 @@ METHOD(task_t, migrate, void, { this->natd->task.migrate(&this->natd->task, ike_sa); } - if (this->pending_update) - { - this->ike_sa->set_pending_updates(this->ike_sa, - this->ike_sa->get_pending_updates(this->ike_sa) + 1); - } } METHOD(task_t, destroy, void, private_ike_mobike_t *this) { - if (this->pending_update) - { - this->ike_sa->set_pending_updates(this->ike_sa, - this->ike_sa->get_pending_updates(this->ike_sa) - 1); - } chunk_free(&this->cookie2); if (this->natd) { diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/description.txt b/testing/tests/ikev2/mobike-virtual-ip-nat/description.txt new file mode 100644 index 000000000..6f1837c86 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/description.txt @@ -0,0 +1,9 @@ +The roadwarrior alice is sitting behind the NAT router moon but +at the outset of the scenariou is also directly connected to the 192.168.0.0/24 network +via an additional eth1 interface. alice builds up a tunnel to gateway sun +in order to reach bob in the subnet behind. When the eth1 interface +goes away, alice switches to eth0 and signals the IP address change +via a MOBIKE ADDRESS_UPDATE notification to peer sun. Later the interface +comes back up again and because the best path is preferred (charon.prefer_best_path) +there is another switch to the directly connected path. alice sets +a virtual IP of 10.3.0.3, so that the IPsec policies don't have to be changed. diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/evaltest.dat b/testing/tests/ikev2/mobike-virtual-ip-nat/evaltest.dat new file mode 100644 index 000000000..46df60041 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/evaltest.dat @@ -0,0 +1,31 @@ +alice::ipsec status 2> /dev/null::mobike.*ESTABLISHED.*192.168.0.50.*PH_IP_SUN::YES +sun:: ipsec status 2> /dev/null::mobike.*ESTABLISHED.*PH_IP_SUN.*192.168.0.50::YES +alice::ipsec status 2> /dev/null::mobike.*INSTALLED.*ESP SPIs::YES +sun:: ipsec status 2> /dev/null::mobike.*INSTALLED.*ESP SPIs::YES +alice::ipsec statusall 2> /dev/null::10.3.0.3/32 === 10.2.0.0/16::YES +sun:: ipsec statusall 2> /dev/null::10.2.0.0/16 === 10.3.0.3/32::YES +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES +alice::ifdown eth1::No output expected::NO +alice::sleep 1::No output expected::NO +alice::ipsec status 2> /dev/null::mobike.*ESTABLISHED.*PH_IP_ALICE.*PH_IP_SUN::YES +sun:: ipsec status 2> /dev/null::mobike.*ESTABLISHED.*PH_IP_SUN.*PH_IP_MOON::YES +alice::ipsec status 2> /dev/null::mobike.*INSTALLED.*ESP in UDP SPIs::YES +sun:: ipsec status 2> /dev/null::mobike.*INSTALLED.*ESP in UDP SPIs::YES +alice::ipsec statusall 2> /dev/null::10.3.0.3/32 === 10.2.0.0/16::YES +sun:: ipsec statusall 2> /dev/null::10.2.0.0/16 === 10.3.0.3/32::YES +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES +alice::ifup eth1::No output expected::NO +alice::sleep 1::No output expected::NO +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES +alice::ipsec status 2> /dev/null::mobike.*ESTABLISHED.*192.168.0.50.*PH_IP_SUN::YES +sun:: ipsec status 2> /dev/null::mobike.*ESTABLISHED.*PH_IP_SUN.*192.168.0.50::YES +alice::ipsec status 2> /dev/null::mobike.*INSTALLED.*ESP SPIs::YES +sun:: ipsec status 2> /dev/null::mobike.*INSTALLED.*ESP SPIs::YES +alice::ipsec statusall 2> /dev/null::10.3.0.3/32 === 10.2.0.0/16::YES +sun:: ipsec statusall 2> /dev/null::10.2.0.0/16 === 10.3.0.3/32::YES +sun::tcpdump::alice1.strongswan.org.*sun.strongswan.org: ESP.*seq=0x1::YES +sun::tcpdump::sun.strongswan.org.*alice1.strongswan.org: ESP.*seq=0x1::YES +moon::tcpdump::moon.strongswan.org.*sun.strongswan.org.*: ESP.*seq=0x2::YES +moon::tcpdump::sun.strongswan.org.*moon.strongswan.org.*: ESP.*seq=0x2::YES +bob::tcpdump::10.3.0.3.*bob.strongswan.org.*ICMP echo request::3 +bob::tcpdump::bob.strongswan.org.*10.3.0.3.*ICMP echo reply::3 diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/alice/etc/ipsec.conf b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/alice/etc/ipsec.conf new file mode 100644 index 000000000..6039e5f46 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/alice/etc/ipsec.conf @@ -0,0 +1,19 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + +conn mobike + leftsourceip=%config + leftcert=aliceCert.pem + leftid=alice@strongswan.org + right=PH_IP_SUN + rightid=@sun.strongswan.org + rightsubnet=10.2.0.0/16 + auto=add diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/alice/etc/iptables.rules b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/alice/etc/iptables.rules new file mode 100644 index 000000000..450e7cef6 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/alice/etc/iptables.rules @@ -0,0 +1,42 @@ +*filter + +# default policy is DROP +-P INPUT DROP +-P OUTPUT DROP +-P FORWARD DROP + +# allow traffic on lo as ifup/ifdown call bind's rndc which accesses TCP 953 +-A OUTPUT -o lo -j ACCEPT +-A INPUT -i lo -j ACCEPT + +# allow IPsec tunnel traffic +-A INPUT -m policy --dir in --pol ipsec --proto esp -j ACCEPT +-A OUTPUT -m policy --dir out --pol ipsec --proto esp -j ACCEPT + +# allow ESP +-A INPUT -i eth0 -p 50 -j ACCEPT +-A INPUT -i eth1 -p 50 -j ACCEPT +-A OUTPUT -o eth0 -p 50 -j ACCEPT +-A OUTPUT -o eth1 -p 50 -j ACCEPT + +# allow IKE +-A INPUT -i eth0 -p udp --sport 500 --dport 500 -j ACCEPT +-A INPUT -i eth1 -p udp --sport 500 --dport 500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 500 --sport 500 -j ACCEPT +-A OUTPUT -o eth1 -p udp --dport 500 --sport 500 -j ACCEPT + +# allow MobIKE +-A INPUT -i eth0 -p udp --sport 4500 --dport 4500 -j ACCEPT +-A INPUT -i eth1 -p udp --sport 4500 --dport 4500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 4500 --sport 4500 -j ACCEPT +-A OUTPUT -o eth1 -p udp --dport 4500 --sport 4500 -j ACCEPT + +# allow ssh +-A INPUT -p tcp --dport 22 -j ACCEPT +-A OUTPUT -p tcp --sport 22 -j ACCEPT + +# allow crl fetch from winnetou +-A INPUT -p tcp --sport 80 -s PH_IP_WINNETOU -j ACCEPT +-A OUTPUT -p tcp --dport 80 -d PH_IP_WINNETOU -j ACCEPT + +COMMIT diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/alice/etc/strongswan.conf b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/alice/etc/strongswan.conf new file mode 100644 index 000000000..bd51a50bb --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/alice/etc/strongswan.conf @@ -0,0 +1,12 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac stroke kernel-netlink socket-default + prefer_best_path = yes + + syslog { + daemon { + knl = 2 + } + } +} diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/sun/etc/ipsec.conf b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/sun/etc/ipsec.conf new file mode 100644 index 000000000..e187f9569 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/sun/etc/ipsec.conf @@ -0,0 +1,20 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + +conn mobike + left=PH_IP_SUN + leftcert=sunCert.pem + leftid=@sun.strongswan.org + leftsubnet=10.2.0.0/16 + right=%any + rightsourceip=10.3.0.3 + rightid=alice@strongswan.org + auto=add diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/sun/etc/iptables.rules b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/sun/etc/iptables.rules new file mode 100644 index 000000000..929b1b247 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/sun/etc/iptables.rules @@ -0,0 +1,32 @@ +*filter + +# default policy is DROP +-P INPUT DROP +-P OUTPUT DROP +-P FORWARD DROP + +# allow IPsec tunnel traffic +-A FORWARD -m policy --dir in --pol ipsec --proto esp -j ACCEPT +-A FORWARD -m policy --dir out --pol ipsec --proto esp -j ACCEPT + +# allow ESP +-A INPUT -i eth0 -p 50 -j ACCEPT +-A OUTPUT -o eth0 -p 50 -j ACCEPT + +# allow IKE +-A INPUT -i eth0 -p udp --dport 500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --sport 500 -j ACCEPT + +# allow MobIKE +-A INPUT -i eth0 -p udp --dport 4500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --sport 4500 -j ACCEPT + +# allow ssh +-A INPUT -p tcp --dport 22 -j ACCEPT +-A OUTPUT -p tcp --sport 22 -j ACCEPT + +# allow crl fetch from winnetou +-A INPUT -i eth0 -p tcp --sport 80 -s PH_IP_WINNETOU -j ACCEPT +-A OUTPUT -o eth0 -p tcp --dport 80 -d PH_IP_WINNETOU -j ACCEPT + +COMMIT diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/sun/etc/strongswan.conf b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..9241d28d6 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/hosts/sun/etc/strongswan.conf @@ -0,0 +1,11 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac stroke kernel-netlink socket-default + + syslog { + daemon { + knl = 2 + } + } +} diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/posttest.dat b/testing/tests/ikev2/mobike-virtual-ip-nat/posttest.dat new file mode 100644 index 000000000..0adb75555 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/posttest.dat @@ -0,0 +1,6 @@ +alice::ipsec stop +sun::ipsec stop +alice::ifdown eth1 +alice::iptables-restore < /etc/iptables.flush +sun::iptables-restore < /etc/iptables.flush +moon::iptables -t nat -F diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/pretest.dat b/testing/tests/ikev2/mobike-virtual-ip-nat/pretest.dat new file mode 100644 index 000000000..ece8912b9 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/pretest.dat @@ -0,0 +1,10 @@ +alice::ifup eth1 +alice::iptables-restore < /etc/iptables.rules +sun::iptables-restore < /etc/iptables.rules +moon::iptables -t nat -A POSTROUTING -o eth0 -s 10.1.0.0/16 -p udp -j SNAT --to-source PH_IP_MOON:1024-1100 +moon::iptables -t nat -A POSTROUTING -o eth0 -s 10.1.0.0/16 -p tcp -j SNAT --to-source PH_IP_MOON:2000-2100 +alice::ipsec start +sun::ipsec start +alice::expect-connection mobike +sun::expect-connection mobike +alice::ipsec up mobike diff --git a/testing/tests/ikev2/mobike-virtual-ip-nat/test.conf b/testing/tests/ikev2/mobike-virtual-ip-nat/test.conf new file mode 100644 index 000000000..70c64c503 --- /dev/null +++ b/testing/tests/ikev2/mobike-virtual-ip-nat/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon winnetou sun bob" + +# Corresponding block diagram +# +DIAGRAM="a-m-w-s-b.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="bob moon sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="alice sun"