[Openvpn-devel,v4] route: handle default gateway (net_gateway) and nexthop towards VPN server separately

Message ID 20251119114041.17665-1-gert@greenie.muc.de
State Accepted
Headers show
Series [Openvpn-devel,v4] route: handle default gateway (net_gateway) and nexthop towards VPN server separately | expand

Commit Message

Gert Doering Nov. 19, 2025, 11:40 a.m. UTC
From: Marco Baffo <marco@mandelbit.com>

Right now there is the assumption that the gateway used for net_gateway is the same used to reach the VPN server.
However, these two gateways may be different (i.e. when there is a specific hostroute for the VPN server using a different nexthop).
For this reason we must adapt init_route_list() to fetch the two gateways separately.

Github: fixes OpenVPN/openvpn#890

Change-Id: I16d90221d0a75193035253817ff195f6da9dc0b3
Signed-off-by: Marco Baffo <marco@mandelbit.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1222
---

This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master.

Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1222
This mail reflects revision 4 of this Change.

Acked-by according to Gerrit (reflected above):
Gert Doering <gert@greenie.muc.de>

Comments

Gert Doering Nov. 19, 2025, 1:41 p.m. UTC | #1
In it goes, at last...  I was wondering for a long time if we really need
this, but it turns out that we have users that do stuff like "talk openvpn
via a proxy on 127.0.0.5" plus "--route 1.1.1.1 net_gateway" and this now
broke with 2.7* (see GH #890).  The initial patch fixed $env{net_gateway}
but not "--route net_gateway" - for that we really need to store the 
two different sorts of gateway in the "route_list" / "route_ipv6_list" 
structures.

The log will look a bit redundant now

2025-11-19 12:40:14 net_route_v4_best_gw query: dst 0.0.0.0
2025-11-19 12:40:14 net_route_v4_best_gw result: via 194.97.140.30 dev enp0s18
2025-11-19 12:40:14 net_route_v4_best_gw query: dst 0.0.0.0
2025-11-19 12:40:14 net_route_v4_best_gw result: via 194.97.140.30 dev enp0s18
2025-11-19 12:40:14 net_route_v6_best_gw query: dst ::
2025-11-19 12:40:14 net_route_v6_best_gw result: via 2001:608:0:814::ffff dev enp0s18
2025-11-19 12:40:14 net_route_v6_best_gw query: dst ::1
2025-11-19 12:40:14 net_route_v6_best_gw result: via :: dev lo

(this was "--remote ::1") - but adding another clause to "only call the
other gateway query if the address is different" or more #ifdefs to 
"only do this on Linux and Windows where it makes a difference" would 
have been a bit excessive...

Now let's see if we'll hit even more corner cases :-) - it passes all
my normal tests, plus the things I've learned from #860.

Your patch has been applied to the master branch.

commit 2777cdd3783776a91514804233c5e2b715d11d64 (master)
Author: Marco Baffo
Date:   Wed Nov 19 12:40:35 2025 +0100

     route: handle default gateway (net_gateway) and nexthop towards VPN server separately

     Signed-off-by: Marco Baffo <marco@mandelbit.com>
     Acked-by: Gert Doering <gert@greenie.muc.de>
     Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1222
     Message-Id: <20251119114041.17665-1-gert@greenie.muc.de>
     URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg34529.html
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering

Patch

diff --git a/src/openvpn/route.c b/src/openvpn/route.c
index 7d988da..770300a 100644
--- a/src/openvpn/route.c
+++ b/src/openvpn/route.c
@@ -257,9 +257,9 @@ 
     {
         if (rl)
         {
-            if (rl->rgi.flags & RGI_ADDR_DEFINED)
+            if (rl->ngi.flags & RGI_ADDR_DEFINED)
             {
-                *out = rl->rgi.gateway.addr;
+                *out = rl->ngi.gateway.addr;
             }
             else
             {
@@ -624,10 +624,10 @@ 
         rl->spec.flags |= RTSA_DEFAULT_METRIC;
     }
 
-    get_default_gateway(&rl->rgi, remote_host != IPV4_INVALID_ADDR ? remote_host : INADDR_ANY, ctx);
-    if (rl->rgi.flags & RGI_ADDR_DEFINED)
+    get_default_gateway(&rl->ngi, INADDR_ANY, ctx);
+    if (rl->ngi.flags & RGI_ADDR_DEFINED)
     {
-        setenv_route_addr(es, "net_gateway", rl->rgi.gateway.addr, -1);
+        setenv_route_addr(es, "net_gateway", rl->ngi.gateway.addr, -1);
 #if defined(ENABLE_DEBUG) && !defined(ENABLE_SMALL)
         print_default_gateway(D_ROUTE, &rl->rgi, NULL);
 #endif
@@ -637,6 +637,8 @@ 
         dmsg(D_ROUTE, "ROUTE: default_gateway=UNDEF");
     }
 
+    get_default_gateway(&rl->rgi, remote_host != IPV4_INVALID_ADDR ? remote_host : INADDR_ANY, ctx);
+
     if (rl->spec.flags & RTSA_REMOTE_HOST)
     {
         rl->spec.remote_host_local = test_local_addr(remote_host, &rl->rgi);
@@ -773,10 +775,10 @@ 
     msg(D_ROUTE, "GDG6: remote_host_ipv6=%s",
         remote_host_ipv6 ? print_in6_addr(*remote_host_ipv6, 0, &gc) : "n/a");
 
-    get_default_gateway_ipv6(&rl6->rgi6, remote_host_ipv6, ctx);
-    if (rl6->rgi6.flags & RGI_ADDR_DEFINED)
+    get_default_gateway_ipv6(&rl6->ngi6, NULL, ctx);
+    if (rl6->ngi6.flags & RGI_ADDR_DEFINED)
     {
-        setenv_str(es, "net_gateway_ipv6", print_in6_addr(rl6->rgi6.gateway.addr_ipv6, 0, &gc));
+        setenv_str(es, "net_gateway_ipv6", print_in6_addr(rl6->ngi6.gateway.addr_ipv6, 0, &gc));
 #if defined(ENABLE_DEBUG) && !defined(ENABLE_SMALL)
         print_default_gateway(D_ROUTE, NULL, &rl6->rgi6);
 #endif
@@ -786,6 +788,8 @@ 
         dmsg(D_ROUTE, "ROUTE6: default_gateway=UNDEF");
     }
 
+    get_default_gateway_ipv6(&rl6->rgi6, remote_host_ipv6, ctx);
+
     if (is_route_parm_defined(remote_endpoint))
     {
         if (inet_pton(AF_INET6, remote_endpoint, &rl6->remote_endpoint_ipv6) == 1)
diff --git a/src/openvpn/route.h b/src/openvpn/route.h
index 54fa137..3d19dbd 100644
--- a/src/openvpn/route.h
+++ b/src/openvpn/route.h
@@ -234,7 +234,8 @@ 
 
     struct route_special_addr spec;
     struct route_gateway_info rgi;
-    unsigned int flags; /* RG_x flags */
+    struct route_gateway_info ngi; /* net_gateway */
+    unsigned int flags;            /* RG_x flags */
     struct route_ipv4 *routes;
     struct gc_arena gc;
 };
@@ -249,7 +250,8 @@ 
     int default_metric;
 
     struct route_ipv6_gateway_info rgi6;
-    unsigned int flags; /* RG_x flags, see route_option_list */
+    struct route_ipv6_gateway_info ngi6; /* net_gateway_ipv6 */
+    unsigned int flags;                  /* RG_x flags, see route_option_list */
     struct route_ipv6 *routes_ipv6;
     struct gc_arena gc;
 };