Commit 8a6f9ba70e for strongswan.org

commit 8a6f9ba70eae87e66aef1b42b5edd206beaf789c
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Fri Nov 29 14:57:31 2024 +0100

    kernel-netlink: Don't fallback to peer address as gateway/nexthop

    This doesn't really seem useful (perhaps it was before we started to
    configure the outbound interface on our routes). And it can actually
    cause the route installation to fail e.g. for routes over point-to-point
    interfaces where we'd get "Error: Nexthop has invalid gateway" errors.

    Note that we can't return NULL if we find an interface as e.g. the updown
    plugin uses this method to determine the outbound interface (it ignores
    the nexthop), which it passes to the script.  If we returned NULL, it
    would pass "unknown" instead, which would cause the firewall rules to
    mismatch.  While it seems that 0.0.0.0/:: is ignored as nexthop by the
    kernel on the installed route, I still explicitly ignore such addresses
    to avoid any unintended side-effects.

    The automatic route installation in the ikev2/shunt-manual-prio scenario
    had to be disabled on the clients.  The reason is that the route in table
    220 won't have a nexthop set (the peers are directly connected), so when
    trying to reach alice or venus via SSH, which matches the port-specific
    bypass policies for which we don't install throw routes, the hosts will
    do ARP requests for the target IPs instead of routing the packets via
    moon.

    Closes strongswan/strongswan#2548

diff --git a/src/libcharon/kernel/kernel_interface.h b/src/libcharon/kernel/kernel_interface.h
index 21cabe7f09..a8c9d6f410 100644
--- a/src/libcharon/kernel/kernel_interface.h
+++ b/src/libcharon/kernel/kernel_interface.h
@@ -312,6 +312,8 @@ struct kernel_interface_t {
 	 * The returned host is allocated and must be destroyed.
 	 * An optional src address can be used to check if a route is available
 	 * for the given source to dest.
+	 * 0.0.0.0/:: may get returend if an interface but no next hop can be
+	 * determined.
 	 *
 	 * @param dest			target destination address
 	 * @param prefix		prefix length if dest is a subnet, -1 for auto
diff --git a/src/libcharon/kernel/kernel_net.h b/src/libcharon/kernel/kernel_net.h
index 2dc247e908..ece59ad4dd 100644
--- a/src/libcharon/kernel/kernel_net.h
+++ b/src/libcharon/kernel/kernel_net.h
@@ -85,6 +85,8 @@ struct kernel_net_t {
 	 * The returned host is allocated and must be destroyed.
 	 * An optional src address can be used to check if a route is available
 	 * for the given source to dest.
+	 * 0.0.0.0/:: may get returend if an interface but no next hop can be
+	 * determined.
 	 *
 	 * @param dest			target destination address
 	 * @param prefix		prefix length if dest is a subnet, -1 for auto
diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
index dc1486b0fd..810b07e61c 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -3008,7 +3008,7 @@ static void install_route(private_kernel_netlink_ipsec_t *this,
 		policy->route = NULL;
 	}

-	DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", out->dst_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,
diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
index 416ae78116..6742b618d9 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
@@ -2075,9 +2075,9 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
 				*iface = get_interface_name_by_index(this, best->oif);
 			}
 		}
-		if (!addr && !match_net)
-		{	/* fallback to destination address */
-			addr = dest->clone(dest);
+		if (!addr && !match_net && iface && *iface)
+		{
+			addr = host_create_any(dest->get_family(dest));
 		}
 	}
 	else
@@ -2600,7 +2600,8 @@ static status_t manage_srcroute(private_kernel_netlink_net_t *this,
 	{
 		chunk = src_ip->get_address(src_ip);
 		netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request));
-		if (gateway && gateway->get_family(gateway) == src_ip->get_family(src_ip))
+		if (gateway && !gateway->is_anyaddr(gateway) &&
+			gateway->get_family(gateway) == src_ip->get_family(src_ip))
 		{
 			chunk = gateway->get_address(gateway);
 			netlink_add_attribute(hdr, RTA_GATEWAY, chunk, sizeof(request));
diff --git a/testing/tests/ikev2/shunt-manual-prio/hosts/carol/etc/strongswan.conf b/testing/tests/ikev2/shunt-manual-prio/hosts/carol/etc/strongswan.conf
index 203f1a7b9d..0fe180cedb 100755
--- a/testing/tests/ikev2/shunt-manual-prio/hosts/carol/etc/strongswan.conf
+++ b/testing/tests/ikev2/shunt-manual-prio/hosts/carol/etc/strongswan.conf
@@ -6,4 +6,6 @@

 charon-systemd {
   load = random nonce openssl pem pkcs1 revocation curl kernel-netlink socket-default updown vici
+  # conflicts with the route via moon we have installed to reach hosts in its subnet directly
+  install_routes = no
 }
diff --git a/testing/tests/ikev2/shunt-manual-prio/hosts/dave/etc/strongswan.conf b/testing/tests/ikev2/shunt-manual-prio/hosts/dave/etc/strongswan.conf
index 203f1a7b9d..0fe180cedb 100755
--- a/testing/tests/ikev2/shunt-manual-prio/hosts/dave/etc/strongswan.conf
+++ b/testing/tests/ikev2/shunt-manual-prio/hosts/dave/etc/strongswan.conf
@@ -6,4 +6,6 @@

 charon-systemd {
   load = random nonce openssl pem pkcs1 revocation curl kernel-netlink socket-default updown vici
+  # conflicts with the route via moon we have installed to reach hosts in its subnet directly
+  install_routes = no
 }