diff options
Diffstat (limited to 'src')
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); |