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).
This commit is contained in:
Tobias Brunner 2022-02-11 17:23:15 +01:00
parent 012e4cd902
commit 5de6ab82a6

View 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);