Merge branch 'interface-for-routes'
authorTobias Brunner <tobias@strongswan.org>
Fri, 10 Jun 2016 16:15:42 +0000 (18:15 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 10 Jun 2016 16:15:42 +0000 (18:15 +0200)
Changes how the interface for routes installed with policies is
determined.  In most cases we now use the interface over which we reach the
other peer, not the interface on which the local address (or the source IP) is
installed.  However, that might be the same interface depending on the
configuration (i.e. in practice there will often not be a change).

Routes are not installed anymore for drop policies and for policies with
protocol/port selectors.

Fixes #809, #824, #1347.

src/libcharon/kernel/kernel_interface.c
src/libcharon/kernel/kernel_interface.h
src/libcharon/kernel/kernel_net.h
src/libcharon/plugins/kernel_iph/kernel_iph_net.c
src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c
src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c
src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c

index 0a0081c..7b39a02 100644 (file)
@@ -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,
index 50f6d98..225b409 100644 (file)
@@ -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
index 4f3063d..1d78d6e 100644 (file)
@@ -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
index 3633213..efeb980 100644 (file)
@@ -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);
index f134032..77e37e2 100644 (file)
@@ -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)
index e78e13e..7e60982 100644 (file)
@@ -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);
index bde0e77..93c2ccc 100644 (file)
@@ -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);
 }
 
 /**
index a0fd429..516a15a 100644 (file)
@@ -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;
index 5ab39bb..236e341 100644 (file)
@@ -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);
 }
 
 /**
index c12d384..6ad26b7 100644 (file)
@@ -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);