]> git.ipfire.org Git - people/ms/strongswan.git/commitdiff
kernel-pfkey: Only install exclude route if not routing via outbound interface
authorTobias Brunner <tobias@strongswan.org>
Fri, 11 Feb 2022 16:23:15 +0000 (17:23 +0100)
committerTobias Brunner <tobias@strongswan.org>
Tue, 15 Mar 2022 12:52:24 +0000 (13:52 +0100)
When installing routes based on remote traffic selectors, it can be
necessary to install an exclude route for the remote peer to avoid a
routing loop and continue to be able to reach it via IKE/ESP.

However, such routes are only necessary, if the routes we install don't
go via outbound interface.  That's the case when using VIPs and routing
via TUN devices, or when using internal source IPs and routing via
their interfaces.

Installing such exclude routes if not necessary can cause issues on
FreeBSD (EINVAL when sending packets to the peer).

src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c

index 51a47b9f8564971b6188d2a9906efaf71b1b7205..3e01e1c90fbb2487806c1c9924865ae0ed858a1a 100644 (file)
@@ -2429,6 +2429,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
 {
        route_entry_t *route, *old;
        host_t *host, *src, *dst;
+       char *out_interface = NULL;
        bool is_virtual;
 
        if (charon->kernel->get_address_by_ts(charon->kernel, out->src_ts, &host,
@@ -2456,7 +2457,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
                 * this is required for example on Linux. */
                if (is_virtual || this->route_via_internal)
                {
-                       free(route->if_name);
+                       out_interface = route->if_name;
                        route->if_name = NULL;
                        src = route->src_ip;
                }
@@ -2476,6 +2477,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
                !charon->kernel->get_interface(charon->kernel, src, &route->if_name))
        {
                route_entry_destroy(route);
+               free(out_interface);
                return FALSE;
        }
 
@@ -2486,6 +2488,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
                if (route_entry_equals(old, route))
                {       /* such a route already exists */
                        route_entry_destroy(route);
+                       free(out_interface);
                        return TRUE;
                }
                /* uninstall previously installed route */
@@ -2501,8 +2504,10 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
                policy->route = NULL;
        }
 
-       /* if remote traffic selector covers the IKE peer, add an exclude route */
-       if (charon->kernel->get_features(charon->kernel) & KERNEL_REQUIRE_EXCLUDE_ROUTE)
+       /* if we don't route via outbound interface and the remote traffic selector
+        * covers the IKE peer, add an exclude route */
+       if (!streq(route->if_name, out_interface) &&
+               charon->kernel->get_features(charon->kernel) & KERNEL_REQUIRE_EXCLUDE_ROUTE)
        {
                if (out->dst_ts->is_host(out->dst_ts, dst))
                {
@@ -2510,6 +2515,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
                                 "with IKE traffic", out->src_ts, out->dst_ts, policy_dir_names,
                                 policy->direction);
                        route_entry_destroy(route);
+                       free(out_interface);
                        return FALSE;
                }
                if (out->dst_ts->includes(out->dst_ts, dst))
@@ -2517,6 +2523,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
                        add_exclude_route(this, route, out->generic.sa->src, dst);
                }
        }
+       free(out_interface);
 
        DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s",
                 out->dst_ts, route->gateway, route->src_ip, route->if_name);