aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2016-06-10 18:15:42 +0200
committerTobias Brunner <tobias@strongswan.org>2016-06-10 18:15:42 +0200
commit96b1fab53ce7f7b4b6c5e2a0bb85c3f3f14be62c (patch)
tree1b19c6494e2142a8faacd3c87c8cb67e67d03fc4 /src
parent436f64d5bcc3946387dd95265d83d8764fe37797 (diff)
parentb52e540f43c8a97ea3343e12a1cc33b6dc3d3fbc (diff)
downloadstrongswan-96b1fab53ce7f7b4b6c5e2a0bb85c3f3f14be62c.tar.bz2
strongswan-96b1fab53ce7f7b4b6c5e2a0bb85c3f3f14be62c.tar.xz
Merge branch 'interface-for-routes'
Changes how the interface for routes installed with policies is determined. In most cases we now use the interface over which we reach the other peer, not the interface on which the local address (or the source IP) is installed. However, that might be the same interface depending on the configuration (i.e. in practice there will often not be a change). Routes are not installed anymore for drop policies and for policies with protocol/port selectors. Fixes #809, #824, #1347.
Diffstat (limited to 'src')
-rw-r--r--src/libcharon/kernel/kernel_interface.c5
-rw-r--r--src/libcharon/kernel/kernel_interface.h4
-rw-r--r--src/libcharon/kernel/kernel_net.h8
-rw-r--r--src/libcharon/plugins/kernel_iph/kernel_iph_net.c7
-rw-r--r--src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c5
-rw-r--r--src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c221
-rw-r--r--src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c63
-rw-r--r--src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c83
-rw-r--r--src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c24
-rw-r--r--src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c2
10 files changed, 256 insertions, 166 deletions
diff --git a/src/libcharon/kernel/kernel_interface.c b/src/libcharon/kernel/kernel_interface.c
index 0a0081c22..7b39a020c 100644
--- a/src/libcharon/kernel/kernel_interface.c
+++ b/src/libcharon/kernel/kernel_interface.c
@@ -524,13 +524,14 @@ METHOD(kernel_interface_t, get_source_addr, host_t*,
}
METHOD(kernel_interface_t, get_nexthop, host_t*,
- private_kernel_interface_t *this, host_t *dest, int prefix, host_t *src)
+ private_kernel_interface_t *this, host_t *dest, int prefix, host_t *src,
+ char **iface)
{
if (!this->net)
{
return NULL;
}
- return this->net->get_nexthop(this->net, dest, prefix, src);
+ return this->net->get_nexthop(this->net, dest, prefix, src, iface);
}
METHOD(kernel_interface_t, get_interface, bool,
diff --git a/src/libcharon/kernel/kernel_interface.h b/src/libcharon/kernel/kernel_interface.h
index 50f6d9829..225b40932 100644
--- a/src/libcharon/kernel/kernel_interface.h
+++ b/src/libcharon/kernel/kernel_interface.h
@@ -285,10 +285,12 @@ struct kernel_interface_t {
* @param dest target destination address
* @param prefix prefix length if dest is a subnet, -1 for auto
* @param src source address to check, or NULL
+ * @param[out] iface allocated name of the interface to reach dest, if
+ * available (optional)
* @return next hop address, NULL if unreachable
*/
host_t* (*get_nexthop)(kernel_interface_t *this, host_t *dest,
- int prefix, host_t *src);
+ int prefix, host_t *src, char **iface);
/**
* Get the interface name of a local address. Interfaces that are down or
diff --git a/src/libcharon/kernel/kernel_net.h b/src/libcharon/kernel/kernel_net.h
index 4f3063deb..1d78d6edd 100644
--- a/src/libcharon/kernel/kernel_net.h
+++ b/src/libcharon/kernel/kernel_net.h
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2008-2012 Tobias Brunner
+ * Copyright (C) 2008-2016 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
@@ -88,10 +88,12 @@ struct kernel_net_t {
* @param dest target destination address
* @param prefix prefix length if dest is a subnet, -1 for auto
* @param src source address to check, or NULL
+ * @param[out] iface allocated name of the interface to reach dest, if
+ * available (optional)
* @return next hop address, NULL if unreachable
*/
host_t* (*get_nexthop)(kernel_net_t *this, host_t *dest, int prefix,
- host_t *src);
+ host_t *src, char **iface);
/**
* Get the interface name of a local address. Interfaces that are down or
diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c
index 3633213f3..efeb98045 100644
--- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c
+++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c
@@ -562,7 +562,8 @@ METHOD(kernel_net_t, get_source_addr, host_t*,
}
METHOD(kernel_net_t, get_nexthop, host_t*,
- private_kernel_iph_net_t *this, host_t *dest, int prefix, host_t *src)
+ private_kernel_iph_net_t *this, host_t *dest, int prefix, host_t *src,
+ char **iface)
{
MIB_IPFORWARD_ROW2 route;
SOCKADDR_INET best, *sai_dst, *sai_src = NULL;
@@ -592,6 +593,10 @@ METHOD(kernel_net_t, get_nexthop, host_t*,
{
if (!nexthop->is_anyaddr(nexthop))
{
+ if (iface)
+ {
+ *iface = NULL;
+ }
return nexthop;
}
nexthop->destroy(nexthop);
diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c
index f1340320a..77e37e249 100644
--- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c
+++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c
@@ -308,7 +308,7 @@ static void add_exclude_route(private_kernel_libipsec_ipsec_t *this,
if (!route->exclude)
{
DBG2(DBG_KNL, "installing new exclude route for %H src %H", dst, src);
- gtw = charon->kernel->get_nexthop(charon->kernel, dst, -1, NULL);
+ gtw = charon->kernel->get_nexthop(charon->kernel, dst, -1, NULL, NULL);
if (gtw)
{
char *if_name = NULL;
@@ -434,7 +434,8 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this,
);
#ifndef __linux__
/* on Linux we cant't install a gateway */
- route->gateway = charon->kernel->get_nexthop(charon->kernel, dst, -1, src);
+ route->gateway = charon->kernel->get_nexthop(charon->kernel, dst, -1, src,
+ NULL);
#endif
if (policy->route)
diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
index e78e13e40..7e60982f7 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -479,7 +479,7 @@ static void ipsec_sa_destroy(private_kernel_netlink_ipsec_t *this,
}
typedef struct policy_sa_t policy_sa_t;
-typedef struct policy_sa_in_t policy_sa_in_t;
+typedef struct policy_sa_out_t policy_sa_out_t;
/**
* Mapping between a policy and an IPsec SA.
@@ -499,10 +499,10 @@ struct policy_sa_t {
};
/**
- * For inbound policies we also cache the traffic selectors in order to install
+ * For outbound policies we also cache the traffic selectors in order to install
* the route.
*/
-struct policy_sa_in_t {
+struct policy_sa_out_t {
/** Generic interface */
policy_sa_t generic;
@@ -523,14 +523,14 @@ static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this,
{
policy_sa_t *policy;
- if (dir == POLICY_IN)
+ if (dir == POLICY_OUT)
{
- policy_sa_in_t *in;
- INIT(in,
+ policy_sa_out_t *out;
+ INIT(out,
.src_ts = src_ts->clone(src_ts),
.dst_ts = dst_ts->clone(dst_ts),
);
- policy = &in->generic;
+ policy = &out->generic;
}
else
{
@@ -547,11 +547,11 @@ static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this,
static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir,
private_kernel_netlink_ipsec_t *this)
{
- if (*dir == POLICY_IN)
+ if (*dir == POLICY_OUT)
{
- policy_sa_in_t *in = (policy_sa_in_t*)policy;
- in->src_ts->destroy(in->src_ts);
- in->dst_ts->destroy(in->dst_ts);
+ policy_sa_out_t *out = (policy_sa_out_t*)policy;
+ out->src_ts->destroy(out->src_ts);
+ out->dst_ts->destroy(out->dst_ts);
}
ipsec_sa_destroy(this, policy->sa);
free(policy);
@@ -2175,6 +2175,104 @@ static void policy_change_done(private_kernel_netlink_ipsec_t *this,
}
/**
+ * Install a route for the given policy if enabled and required
+ */
+static void install_route(private_kernel_netlink_ipsec_t *this,
+ policy_entry_t *policy, policy_sa_t *mapping, ipsec_sa_t *ipsec)
+{
+ policy_sa_out_t *out = (policy_sa_out_t*)mapping;
+ route_entry_t *route;
+ host_t *iface;
+
+ INIT(route,
+ .prefixlen = policy->sel.prefixlen_d,
+ );
+
+ if (charon->kernel->get_address_by_ts(charon->kernel, out->src_ts,
+ &route->src_ip, NULL) == SUCCESS)
+ {
+ if (!ipsec->dst->is_anyaddr(ipsec->dst))
+ {
+ route->gateway = charon->kernel->get_nexthop(charon->kernel,
+ ipsec->dst, -1, ipsec->src,
+ &route->if_name);
+ }
+ else
+ { /* for shunt policies */
+ iface = xfrm2host(policy->sel.family, &policy->sel.daddr, 0);
+ route->gateway = charon->kernel->get_nexthop(charon->kernel,
+ iface, policy->sel.prefixlen_d,
+ route->src_ip, &route->if_name);
+ iface->destroy(iface);
+ }
+ route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
+ memcpy(route->dst_net.ptr, &policy->sel.daddr, route->dst_net.len);
+
+ /* get the interface to install the route for, if we haven't one yet.
+ * If we have a local address, use it. Otherwise (for shunt policies)
+ * use the route's source address. */
+ if (!route->if_name)
+ {
+ iface = ipsec->src;
+ if (iface->is_anyaddr(iface))
+ {
+ iface = route->src_ip;
+ }
+ if (!charon->kernel->get_interface(charon->kernel, iface,
+ &route->if_name))
+ {
+ route_entry_destroy(route);
+ return;
+ }
+ }
+ if (policy->route)
+ {
+ route_entry_t *old = policy->route;
+ if (route_entry_equals(old, route))
+ {
+ route_entry_destroy(route);
+ return;
+ }
+ /* uninstall previously installed route */
+ if (charon->kernel->del_route(charon->kernel, old->dst_net,
+ old->prefixlen, old->gateway,
+ old->src_ip, old->if_name) != SUCCESS)
+ {
+ DBG1(DBG_KNL, "error uninstalling route installed with policy "
+ "%R === %R %N", out->src_ts, out->dst_ts, policy_dir_names,
+ policy->direction);
+ }
+ route_entry_destroy(old);
+ policy->route = NULL;
+ }
+
+ DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", out->dst_ts,
+ route->gateway, route->src_ip, route->if_name);
+ switch (charon->kernel->add_route(charon->kernel, route->dst_net,
+ route->prefixlen, route->gateway,
+ route->src_ip, route->if_name))
+ {
+ default:
+ DBG1(DBG_KNL, "unable to install source route for %H",
+ route->src_ip);
+ /* FALL */
+ case ALREADY_DONE:
+ /* route exists, do not uninstall */
+ route_entry_destroy(route);
+ break;
+ case SUCCESS:
+ /* cache the installed route */
+ policy->route = route;
+ break;
+ }
+ }
+ else
+ {
+ free(route);
+ }
+}
+
+/**
* Add or update a policy in the kernel.
*
* Note: The mutex has to be locked when entering this function
@@ -2298,103 +2396,18 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
return FAILED;
}
/* install a route, if:
- * - this is a inbound policy (to just get one for each child)
- * - we are in tunnel/BEET mode or install a bypass policy
+ * - this is an outbound policy (to just get one for each child)
* - routing is not disabled via strongswan.conf
+ * - the selector is not for a specific protocol/port
+ * - we are in tunnel/BEET mode or install a bypass policy
*/
- if (policy->direction == POLICY_IN && this->install_routes &&
- (mapping->type != POLICY_IPSEC || ipsec->cfg.mode != MODE_TRANSPORT))
+ if (policy->direction == POLICY_OUT && this->install_routes &&
+ !policy->sel.proto && !policy->sel.dport && !policy->sel.sport)
{
- policy_sa_in_t *in = (policy_sa_in_t*)mapping;
- route_entry_t *route;
- host_t *iface;
-
- INIT(route,
- .prefixlen = policy->sel.prefixlen_s,
- );
-
- if (charon->kernel->get_address_by_ts(charon->kernel, in->dst_ts,
- &route->src_ip, NULL) == SUCCESS)
- {
- /* get the nexthop to src (src as we are in POLICY_IN) */
- if (!ipsec->src->is_anyaddr(ipsec->src))
- {
- route->gateway = charon->kernel->get_nexthop(charon->kernel,
- ipsec->src, -1, ipsec->dst);
- }
- else
- { /* for shunt policies */
- iface = xfrm2host(policy->sel.family, &policy->sel.saddr, 0);
- route->gateway = charon->kernel->get_nexthop(charon->kernel,
- iface, policy->sel.prefixlen_s,
- route->src_ip);
- iface->destroy(iface);
- }
- route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
- memcpy(route->dst_net.ptr, &policy->sel.saddr, route->dst_net.len);
-
- /* get the interface to install the route for. If we have a local
- * address, use it. Otherwise (for shunt policies) use the
- * routes source address. */
- iface = ipsec->dst;
- if (iface->is_anyaddr(iface))
- {
- iface = route->src_ip;
- }
- /* install route via outgoing interface */
- if (!charon->kernel->get_interface(charon->kernel, iface,
- &route->if_name))
- {
- policy_change_done(this, policy);
- route_entry_destroy(route);
- return SUCCESS;
- }
-
- if (policy->route)
- {
- route_entry_t *old = policy->route;
- if (route_entry_equals(old, route))
- {
- policy_change_done(this, policy);
- route_entry_destroy(route);
- return SUCCESS;
- }
- /* uninstall previously installed route */
- if (charon->kernel->del_route(charon->kernel, old->dst_net,
- old->prefixlen, old->gateway,
- old->src_ip, old->if_name) != SUCCESS)
- {
- DBG1(DBG_KNL, "error uninstalling route installed with "
- "policy %R === %R %N", in->src_ts, in->dst_ts,
- policy_dir_names, policy->direction);
- }
- route_entry_destroy(old);
- policy->route = NULL;
- }
-
- DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s",
- in->src_ts, route->gateway, route->src_ip, route->if_name);
- switch (charon->kernel->add_route(charon->kernel, route->dst_net,
- route->prefixlen, route->gateway,
- route->src_ip, route->if_name))
- {
- default:
- DBG1(DBG_KNL, "unable to install source route for %H",
- route->src_ip);
- /* FALL */
- case ALREADY_DONE:
- /* route exists, do not uninstall */
- route_entry_destroy(route);
- break;
- case SUCCESS:
- /* cache the installed route */
- policy->route = route;
- break;
- }
- }
- else
+ if (mapping->type == POLICY_PASS ||
+ (mapping->type == POLICY_IPSEC && ipsec->cfg.mode != MODE_TRANSPORT))
{
- free(route);
+ install_route(this, policy, mapping, ipsec);
}
}
policy_change_done(this, policy);
diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
index bde0e775d..93c2ccccb 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2008-2014 Tobias Brunner
+ * Copyright (C) 2008-2016 Tobias Brunner
* Copyright (C) 2005-2008 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
@@ -1502,6 +1502,32 @@ static int get_interface_index(private_kernel_netlink_net_t *this, char* name)
}
/**
+ * get the name of an interface by index (allocated)
+ */
+static char *get_interface_name_by_index(private_kernel_netlink_net_t *this,
+ int index)
+{
+ iface_entry_t *iface;
+ char *name = NULL;
+
+ DBG2(DBG_KNL, "getting iface name for index %d", index);
+
+ this->lock->read_lock(this->lock);
+ if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index,
+ (void**)&iface, &index) == SUCCESS)
+ {
+ name = strdup(iface->ifname);
+ }
+ this->lock->unlock(this->lock);
+
+ if (!name)
+ {
+ DBG1(DBG_KNL, "unable to get interface name for %d", index);
+ }
+ return name;
+}
+
+/**
* check if an address or net (addr with prefix net bits) is in
* subnet (net with net_len net bits)
*/
@@ -1659,7 +1685,7 @@ static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route)
*/
static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
int prefix, bool nexthop, host_t *candidate,
- u_int recursion)
+ char **iface, u_int recursion)
{
netlink_buf_t request;
struct nlmsghdr *hdr, *out, *current;
@@ -1861,7 +1887,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
if (gtw && !gtw->ip_equals(gtw, dest))
{
route->src_host = get_route(this, gtw, -1, FALSE, candidate,
- recursion + 1);
+ iface, recursion + 1);
}
DESTROY_IF(gtw);
if (route->src_host)
@@ -1879,10 +1905,18 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
enumerator->destroy(enumerator);
if (nexthop)
- { /* nexthop lookup, return gateway if any */
+ { /* nexthop lookup, return gateway and oif if any */
+ if (iface)
+ {
+ *iface = NULL;
+ }
if (best || routes->get_first(routes, (void**)&best) == SUCCESS)
{
addr = host_create_from_chunk(msg->rtm_family, best->gtw, 0);
+ if (iface && route->oif)
+ {
+ *iface = get_interface_name_by_index(this, route->oif);
+ }
}
if (!addr && !match_net)
{ /* fallback to destination address */
@@ -1902,8 +1936,16 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
if (addr)
{
- DBG2(DBG_KNL, "using %H as %s to reach %H/%d", addr,
- nexthop ? "nexthop" : "address", dest, prefix);
+ if (nexthop && iface && *iface)
+ {
+ DBG2(DBG_KNL, "using %H as nexthop and %s as dev to reach %H/%d",
+ addr, *iface, dest, prefix);
+ }
+ else
+ {
+ DBG2(DBG_KNL, "using %H as %s to reach %H/%d", addr,
+ nexthop ? "nexthop" : "address", dest, prefix);
+ }
}
else if (!recursion)
{
@@ -1916,13 +1958,14 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
METHOD(kernel_net_t, get_source_addr, host_t*,
private_kernel_netlink_net_t *this, host_t *dest, host_t *src)
{
- return get_route(this, dest, -1, FALSE, src, 0);
+ return get_route(this, dest, -1, FALSE, src, NULL, 0);
}
METHOD(kernel_net_t, get_nexthop, host_t*,
- private_kernel_netlink_net_t *this, host_t *dest, int prefix, host_t *src)
+ private_kernel_netlink_net_t *this, host_t *dest, int prefix, host_t *src,
+ char **iface)
{
- return get_route(this, dest, prefix, TRUE, src, 0);
+ return get_route(this, dest, prefix, TRUE, src, iface, 0);
}
/**
diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
index a0fd42995..516a15abe 100644
--- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
+++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
@@ -400,7 +400,7 @@ static void ipsec_sa_destroy(private_kernel_pfkey_ipsec_t *this,
}
typedef struct policy_sa_t policy_sa_t;
-typedef struct policy_sa_in_t policy_sa_in_t;
+typedef struct policy_sa_out_t policy_sa_out_t;
/**
* Mapping between a policy and an IPsec SA.
@@ -420,10 +420,10 @@ struct policy_sa_t {
};
/**
- * For input policies we also cache the traffic selectors in order to install
+ * For outbound policies we also cache the traffic selectors in order to install
* the route.
*/
-struct policy_sa_in_t {
+struct policy_sa_out_t {
/** Generic interface */
policy_sa_t generic;
@@ -443,14 +443,14 @@ static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this,
{
policy_sa_t *policy;
- if (dir == POLICY_IN)
+ if (dir == POLICY_OUT)
{
- policy_sa_in_t *in;
- INIT(in,
+ policy_sa_out_t *out;
+ INIT(out,
.src_ts = src_ts->clone(src_ts),
.dst_ts = dst_ts->clone(dst_ts),
);
- policy = &in->generic;
+ policy = &out->generic;
}
else
{
@@ -467,11 +467,11 @@ static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this,
static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir,
private_kernel_pfkey_ipsec_t *this)
{
- if (*dir == POLICY_IN)
+ if (*dir == POLICY_OUT)
{
- policy_sa_in_t *in = (policy_sa_in_t*)policy;
- in->src_ts->destroy(in->src_ts);
- in->dst_ts->destroy(in->dst_ts);
+ policy_sa_out_t *out = (policy_sa_out_t*)policy;
+ out->src_ts->destroy(out->src_ts);
+ out->dst_ts->destroy(out->dst_ts);
}
ipsec_sa_destroy(this, policy->sa);
free(policy);
@@ -2199,7 +2199,7 @@ static void add_exclude_route(private_kernel_pfkey_ipsec_t *this,
if (!route->exclude)
{
DBG2(DBG_KNL, "installing new exclude route for %H src %H", dst, src);
- gtw = charon->kernel->get_nexthop(charon->kernel, dst, -1, NULL);
+ gtw = charon->kernel->get_nexthop(charon->kernel, dst, -1, NULL, NULL);
if (gtw)
{
char *if_name = NULL;
@@ -2287,56 +2287,58 @@ static void remove_exclude_route(private_kernel_pfkey_ipsec_t *this,
}
/**
- * Try to install a route to the given inbound policy
+ * Try to install a route to the given outbound policy
*/
static bool install_route(private_kernel_pfkey_ipsec_t *this,
- policy_entry_t *policy, policy_sa_in_t *in)
+ policy_entry_t *policy, policy_sa_out_t *out)
{
route_entry_t *route, *old;
host_t *host, *src, *dst;
bool is_virtual;
- if (charon->kernel->get_address_by_ts(charon->kernel, in->dst_ts, &host,
+ if (charon->kernel->get_address_by_ts(charon->kernel, out->src_ts, &host,
&is_virtual) != SUCCESS)
{
return FALSE;
}
- /* switch src/dst, as we handle an IN policy */
- src = in->generic.sa->dst;
- dst = in->generic.sa->src;
-
INIT(route,
- .prefixlen = policy->src.mask,
+ .prefixlen = policy->dst.mask,
.src_ip = host,
- .dst_net = chunk_clone(policy->src.net->get_address(policy->src.net)),
+ .dst_net = chunk_clone(policy->dst.net->get_address(policy->dst.net)),
);
+ src = out->generic.sa->src;
+ dst = out->generic.sa->dst;
+
if (!dst->is_anyaddr(dst))
{
route->gateway = charon->kernel->get_nexthop(charon->kernel, dst, -1,
- src);
+ src, &route->if_name);
/* if the IP is virtual, we install the route over the interface it has
* been installed on. Otherwise we use the interface we use for IKE, as
* this is required for example on Linux. */
if (is_virtual)
{
+ free(route->if_name);
+ route->if_name = NULL;
src = route->src_ip;
}
}
else
{ /* for shunt policies */
route->gateway = charon->kernel->get_nexthop(charon->kernel,
- policy->src.net, policy->src.mask,
- route->src_ip);
+ policy->dst.net, policy->dst.mask,
+ route->src_ip, &route->if_name);
/* we don't have a source address, use the address we found */
src = route->src_ip;
}
/* get interface for route, using source address */
- if (!charon->kernel->get_interface(charon->kernel, src, &route->if_name))
+ if (!route->if_name &&
+ !charon->kernel->get_interface(charon->kernel, src, &route->if_name))
{
route_entry_destroy(route);
return FALSE;
@@ -2357,7 +2359,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
old->src_ip, old->if_name) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with policy "
- "%R === %R %N", in->src_ts, in->dst_ts,
+ "%R === %R %N", out->src_ts, out->dst_ts,
policy_dir_names, policy->direction);
}
route_entry_destroy(old);
@@ -2367,22 +2369,22 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
/* if remote traffic selector covers the IKE peer, add an exclude route */
if (charon->kernel->get_features(charon->kernel) & KERNEL_REQUIRE_EXCLUDE_ROUTE)
{
- if (in->src_ts->is_host(in->src_ts, dst))
+ if (out->dst_ts->is_host(out->dst_ts, dst))
{
DBG1(DBG_KNL, "can't install route for %R === %R %N, conflicts "
- "with IKE traffic", in->src_ts, in->dst_ts, policy_dir_names,
+ "with IKE traffic", out->src_ts, out->dst_ts, policy_dir_names,
policy->direction);
route_entry_destroy(route);
return FALSE;
}
- if (in->src_ts->includes(in->src_ts, dst))
+ if (out->dst_ts->includes(out->dst_ts, dst))
{
- add_exclude_route(this, route, in->generic.sa->dst, dst);
+ add_exclude_route(this, route, out->generic.sa->src, dst);
}
}
DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s",
- in->src_ts, route->gateway, route->src_ip, route->if_name);
+ out->dst_ts, route->gateway, route->src_ip, route->if_name);
switch (charon->kernel->add_route(charon->kernel, route->dst_net,
route->prefixlen, route->gateway,
@@ -2399,7 +2401,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
return TRUE;
default:
DBG1(DBG_KNL, "installing route failed: %R via %H src %H dev %s",
- in->src_ts, route->gateway, route->src_ip, route->if_name);
+ out->dst_ts, route->gateway, route->src_ip, route->if_name);
remove_exclude_route(this, route);
route_entry_destroy(route);
return FALSE;
@@ -2556,14 +2558,21 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this,
free(out);
/* install a route, if:
- * - this is an inbound policy (to just get one for each child)
- * - we are in tunnel mode or install a bypass policy
+ * - this is an outbound policy (to just get one for each child)
* - routing is not disabled via strongswan.conf
+ * - the selector is not for a specific protocol/port
+ * - we are in tunnel mode or install a bypass policy
*/
- if (policy->direction == POLICY_IN && this->install_routes &&
- (mapping->type != POLICY_IPSEC || ipsec->cfg.mode != MODE_TRANSPORT))
+ if (policy->direction == POLICY_OUT && this->install_routes &&
+ policy->src.proto == IPSEC_PROTO_ANY &&
+ !policy->src.net->get_port(policy->src.net) &&
+ !policy->dst.net->get_port(policy->dst.net))
{
- install_route(this, policy, (policy_sa_in_t*)mapping);
+ if (mapping->type == POLICY_PASS ||
+ (mapping->type == POLICY_IPSEC && ipsec->cfg.mode != MODE_TRANSPORT))
+ {
+ install_route(this, policy, (policy_sa_out_t*)mapping);
+ }
}
this->mutex->unlock(this->mutex);
return SUCCESS;
diff --git a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c
index 5ab39bbfe..236e3417f 100644
--- a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c
+++ b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 Tobias Brunner
+ * Copyright (C) 2009-2016 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -1533,7 +1533,7 @@ METHOD(kernel_net_t, del_route, status_t,
* address.
*/
static host_t *get_route(private_kernel_pfroute_net_t *this, bool nexthop,
- host_t *dest, host_t *src)
+ host_t *dest, host_t *src, char **iface)
{
struct {
struct rt_msghdr hdr;
@@ -1612,6 +1612,15 @@ retry:
host = gtw;
}
}
+ if (type == RTAX_IFP && addr->sa_family == AF_LINK)
+ {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl*)addr;
+ if (iface)
+ {
+ free(*iface);
+ *iface = strndup(sdl->sdl_data, sdl->sdl_nlen);
+ }
+ }
}
else
{
@@ -1680,13 +1689,18 @@ retry:
METHOD(kernel_net_t, get_source_addr, host_t*,
private_kernel_pfroute_net_t *this, host_t *dest, host_t *src)
{
- return get_route(this, FALSE, dest, src);
+ return get_route(this, FALSE, dest, src, NULL);
}
METHOD(kernel_net_t, get_nexthop, host_t*,
- private_kernel_pfroute_net_t *this, host_t *dest, int prefix, host_t *src)
+ private_kernel_pfroute_net_t *this, host_t *dest, int prefix, host_t *src,
+ char **iface)
{
- return get_route(this, TRUE, dest, src);
+ if (iface)
+ {
+ *iface = NULL;
+ }
+ return get_route(this, TRUE, dest, src, iface);
}
/**
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
index c12d38430..6ad26b72f 100644
--- a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
+++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
@@ -1489,7 +1489,7 @@ static bool manage_route(private_kernel_wfp_ipsec_t *this,
dst->destroy(dst);
return FALSE;
}
- gtw = charon->kernel->get_nexthop(charon->kernel, remote, -1, local);
+ gtw = charon->kernel->get_nexthop(charon->kernel, remote, -1, local, NULL);
if (add)
{
done = install_route(this, dst, mask, src, gtw);