aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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);