From 99a57aa5ee15affa9e38595d90971031977e34be Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 11 Mar 2016 18:54:31 +0100 Subject: kernel-net: Let get_nexthop() return an optional interface name The returned name should be the interface over which the destination address/net is reachable. --- src/libcharon/kernel/kernel_interface.c | 5 +++-- src/libcharon/kernel/kernel_interface.h | 4 +++- src/libcharon/kernel/kernel_net.h | 8 +++++--- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 7 ++++++- .../plugins/kernel_libipsec/kernel_libipsec_ipsec.c | 5 +++-- .../plugins/kernel_netlink/kernel_netlink_ipsec.c | 4 ++-- src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c | 15 ++++++++++----- src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 6 +++--- src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c | 7 ++++++- src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c | 2 +- 10 files changed, 42 insertions(+), 21 deletions(-) (limited to 'src') 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..be0756d6f 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -2320,14 +2320,14 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, if (!ipsec->src->is_anyaddr(ipsec->src)) { route->gateway = charon->kernel->get_nexthop(charon->kernel, - ipsec->src, -1, ipsec->dst); + ipsec->src, -1, ipsec->dst, NULL); } 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); + route->src_ip, NULL); iface->destroy(iface); } route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c index bde0e775d..e9308690f 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c @@ -1659,7 +1659,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 +1861,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) @@ -1880,6 +1880,10 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, if (nexthop) { /* nexthop lookup, return gateway 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); @@ -1916,13 +1920,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..d54a96eb5 100644 --- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -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; @@ -2315,7 +2315,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, if (!dst->is_anyaddr(dst)) { route->gateway = charon->kernel->get_nexthop(charon->kernel, dst, -1, - src); + src, NULL); /* 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 @@ -2329,7 +2329,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, { /* for shunt policies */ route->gateway = charon->kernel->get_nexthop(charon->kernel, policy->src.net, policy->src.mask, - route->src_ip); + route->src_ip, NULL); /* we don't have a source address, use the address we found */ src = route->src_ip; diff --git a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c index 5ab39bbfe..b2b6d9758 100644 --- a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -1684,8 +1684,13 @@ METHOD(kernel_net_t, get_source_addr, host_t*, } 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) { + if (iface) + { + *iface = NULL; + } return get_route(this, TRUE, dest, src); } 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); -- cgit v1.2.3 From 66e9165bc686b7b0328b0ffe1424620653a034b8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 11 Mar 2016 19:07:10 +0100 Subject: kernel-netlink: Return outbound interface in get_nexthop() --- .../plugins/kernel_netlink/kernel_netlink_net.c | 48 +++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c index e9308690f..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 @@ -1501,6 +1501,32 @@ static int get_interface_index(private_kernel_netlink_net_t *this, char* name) return ifindex; } +/** + * 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) @@ -1879,7 +1905,7 @@ 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; @@ -1887,6 +1913,10 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, 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 */ @@ -1906,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) { -- cgit v1.2.3 From 4ba5ea407bb8a4afc295d0fb3e80c8e1cff410bb Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 11 Mar 2016 19:09:54 +0100 Subject: kernel-netlink: Use interface to next hop for shunt policies Using the source address to determine the interface is not correct for net-to-net shunts between two interfaces on which the host has IP addresses for each subnet. --- .../plugins/kernel_netlink/kernel_netlink_ipsec.c | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index be0756d6f..46f94bdb0 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -2335,19 +2335,22 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, /* 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. */ + * route's 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; + iface = ipsec->dst; + if (iface->is_anyaddr(iface)) + { + iface = route->src_ip; + } + 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) -- cgit v1.2.3 From c158331bfc0c39c3ea548ebd83545ae46d074482 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 11 Mar 2016 19:17:03 +0100 Subject: kernel-pfkey: Use interface to next hop for shunt policies --- src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index d54a96eb5..82abc76fa 100644 --- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -2329,14 +2329,15 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, { /* for shunt policies */ route->gateway = charon->kernel->get_nexthop(charon->kernel, policy->src.net, policy->src.mask, - route->src_ip, NULL); + 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; -- cgit v1.2.3 From d1f8628420004512ccc8f08d57b76a9ada5f49d2 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 6 Jun 2016 16:01:43 +0200 Subject: kernel-netlink: Also use interface returned by get_nexthop() for IPsec policies --- src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 46f94bdb0..12cad8198 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -2320,7 +2320,8 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, if (!ipsec->src->is_anyaddr(ipsec->src)) { route->gateway = charon->kernel->get_nexthop(charon->kernel, - ipsec->src, -1, ipsec->dst, NULL); + ipsec->src, -1, ipsec->dst, + &route->if_name); } else { /* for shunt policies */ -- cgit v1.2.3 From 50798628c5530ac3b35d3376cbfd89f326ac286f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 6 Jun 2016 16:20:34 +0200 Subject: kernel-pfkey: Also use interface returned by get_nexthop() for IPsec policies An exception is if the local address is virtual, in which case we want the route to be via TUN device. --- src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 82abc76fa..a5d3c0a4b 100644 --- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -2315,13 +2315,15 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, if (!dst->is_anyaddr(dst)) { route->gateway = charon->kernel->get_nexthop(charon->kernel, dst, -1, - src, NULL); + 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; } } -- cgit v1.2.3 From e7369a9dc50d7a81f03c5b0d3418ac66a01e3f05 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 9 Jun 2016 15:38:37 +0200 Subject: kernel-netlink: Don't install routes for drop policies and if protocol/ports are in the selector We don't need them for drop policies and they might even mess with other routes we install. Routes for policies with protocol/ports in the selector will always be too broad and might conflict with other routes we install. --- .../plugins/kernel_netlink/kernel_netlink_ipsec.c | 202 +++++++++++---------- 1 file changed, 106 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 12cad8198..06aa73133 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -2174,6 +2174,105 @@ static void policy_change_done(private_kernel_netlink_ipsec_t *this, this->mutex->unlock(this->mutex); } +/** + * 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_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, + &route->if_name); + } + 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, &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.saddr, 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->dst; + 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", 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 + { + free(route); + } +} + /** * Add or update a policy in the kernel. * @@ -2298,107 +2397,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 inbound 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)) + !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, - &route->if_name); - } - 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, NULL); - 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 - * route's source address. */ - iface = ipsec->dst; - if (iface->is_anyaddr(iface)) - { - iface = ipsec->dst; - if (iface->is_anyaddr(iface)) - { - iface = route->src_ip; - } - 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); -- cgit v1.2.3 From 85fed13c18ca0a6913e90f0426e73caea17da79a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 9 Jun 2016 15:46:32 +0200 Subject: kernel-pfkey: Don't install routes for drop policies and if protocol/ports are in the selector --- src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index a5d3c0a4b..b92a6e541 100644 --- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -2560,13 +2560,20 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, /* 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 * - 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)) + 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_in_t*)mapping); + } } this->mutex->unlock(this->mutex); return SUCCESS; -- cgit v1.2.3 From aea3c1052e9a8b976ac5c6e05fb8cca54d41d511 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 10 Jun 2016 10:10:09 +0200 Subject: kernel-netlink: Install routes with OUT policies This is the direction we actually need routes in and makes the code easier to read. --- .../plugins/kernel_netlink/kernel_netlink_ipsec.c | 49 +++++++++++----------- 1 file changed, 24 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 06aa73133..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); @@ -2180,41 +2180,40 @@ static void policy_change_done(private_kernel_netlink_ipsec_t *this, static void install_route(private_kernel_netlink_ipsec_t *this, policy_entry_t *policy, policy_sa_t *mapping, ipsec_sa_t *ipsec) { - policy_sa_in_t *in = (policy_sa_in_t*)mapping; + policy_sa_out_t *out = (policy_sa_out_t*)mapping; route_entry_t *route; host_t *iface; INIT(route, - .prefixlen = policy->sel.prefixlen_s, + .prefixlen = policy->sel.prefixlen_d, ); - if (charon->kernel->get_address_by_ts(charon->kernel, in->dst_ts, + if (charon->kernel->get_address_by_ts(charon->kernel, out->src_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)) + if (!ipsec->dst->is_anyaddr(ipsec->dst)) { route->gateway = charon->kernel->get_nexthop(charon->kernel, - ipsec->src, -1, ipsec->dst, + ipsec->dst, -1, ipsec->src, &route->if_name); } else { /* for shunt policies */ - iface = xfrm2host(policy->sel.family, &policy->sel.saddr, 0); + iface = xfrm2host(policy->sel.family, &policy->sel.daddr, 0); route->gateway = charon->kernel->get_nexthop(charon->kernel, - iface, policy->sel.prefixlen_s, + 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.saddr, route->dst_net.len); + 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->dst; + iface = ipsec->src; if (iface->is_anyaddr(iface)) { iface = route->src_ip; @@ -2240,14 +2239,14 @@ static void install_route(private_kernel_netlink_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, policy_dir_names, + "%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", in->src_ts, + 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, @@ -2397,12 +2396,12 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, return FAILED; } /* install a route, if: - * - this is an inbound policy (to just get one for each child) + * - 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 && + if (policy->direction == POLICY_OUT && this->install_routes && !policy->sel.proto && !policy->sel.dport && !policy->sel.sport) { if (mapping->type == POLICY_PASS || -- cgit v1.2.3 From b98afc0a37dbb2360e5ffee580cf9f57175f2e5f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 10 Jun 2016 10:30:00 +0200 Subject: kernel-pfkey: Install routes with OUT policies --- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 61 +++++++++++----------- 1 file changed, 30 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index b92a6e541..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); @@ -2287,31 +2287,30 @@ 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, @@ -2330,7 +2329,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, else { /* for shunt policies */ route->gateway = charon->kernel->get_nexthop(charon->kernel, - policy->src.net, policy->src.mask, + policy->dst.net, policy->dst.mask, route->src_ip, &route->if_name); /* we don't have a source address, use the address we found */ @@ -2360,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); @@ -2370,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, @@ -2402,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; @@ -2559,12 +2558,12 @@ 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) + * - 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 && + 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)) @@ -2572,7 +2571,7 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, if (mapping->type == POLICY_PASS || (mapping->type == POLICY_IPSEC && ipsec->cfg.mode != MODE_TRANSPORT)) { - install_route(this, policy, (policy_sa_in_t*)mapping); + install_route(this, policy, (policy_sa_out_t*)mapping); } } this->mutex->unlock(this->mutex); -- cgit v1.2.3 From b52e540f43c8a97ea3343e12a1cc33b6dc3d3fbc Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 10 Jun 2016 17:13:22 +0200 Subject: kernel-pfroute: Return interface to reach destination from get_nexthop() --- .../plugins/kernel_pfroute/kernel_pfroute_net.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c index b2b6d9758..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,7 +1689,7 @@ 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*, @@ -1691,7 +1700,7 @@ METHOD(kernel_net_t, get_nexthop, host_t*, { *iface = NULL; } - return get_route(this, TRUE, dest, src); + return get_route(this, TRUE, dest, src, iface); } /** -- cgit v1.2.3