From 5de6ab82a652716ea03882f6efce87c871bcdef1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 11 Feb 2022 17:23:15 +0100 Subject: [PATCH 1/2] kernel-pfkey: Only install exclude route if not routing via outbound interface 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). --- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 51a47b9f85..3e01e1c90f 100644 --- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -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); From 8a76c5af8a3c3dd5fd6d9dc301069a8bdae018ec Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 16 Feb 2022 14:47:40 +0100 Subject: [PATCH 2/2] kernel-pfkey: Don't install exclude routes for locally connected peers Such routes with a gateway that equals the peer's address are problematic on FreeBSD. And since there is most likely a narrow route for the local subnet anyway, the exclude routes would be redundant. --- src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 3e01e1c90f..f32c4f83d8 100644 --- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -2339,8 +2339,13 @@ static void add_exclude_route(private_kernel_pfkey_ipsec_t *this, { char *if_name = NULL; - if (charon->kernel->get_interface(charon->kernel, src, &if_name) && - charon->kernel->add_route(charon->kernel, + if (gtw->ip_equals(gtw, dst)) + { + DBG1(DBG_KNL, "not installing exclude route for directly " + "connected peer %H", dst); + } + else if (charon->kernel->get_interface(charon->kernel, src, &if_name) && + charon->kernel->add_route(charon->kernel, dst->get_address(dst), dst->get_family(dst) == AF_INET ? 32 : 128, gtw, src, if_name, FALSE) == SUCCESS)