[Openvpn-devel] dco: fix source IP selection

Message ID 20250116062714.336410-1-dqfext@gmail.com
State New
Headers show
Series [Openvpn-devel] dco: fix source IP selection | expand

Commit Message

Qingfang Deng Jan. 16, 2025, 6:27 a.m. UTC
When multihome option is enabled, OpenVPN passes ipi_addr to DCO, which
is always 0.0.0.0. It should use ipi_spec_dst instead.
When local option is present, OpenVPN does not pass it to DCO.
As a result, Linux may pick a different IP as the source IP, breaking
the connection.

Signed-off-by: Qingfang Deng <dqfext@gmail.com>
---
Discussions: https://github.com/OpenVPN/openvpn/pull/668

 src/openvpn/dco.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

Patch

diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index b5a21369..f2cff0e4 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -493,6 +493,7 @@  dco_p2p_add_new_peer(struct context *c)
     ASSERT(sock->info.connection_established);
 
     struct sockaddr *remoteaddr = &sock->info.lsa->actual.dest.addr.sa;
+    struct sockaddr *localaddr = NULL;
     struct tls_multi *multi = c->c2.tls_multi;
 #ifdef TARGET_FREEBSD
     /* In Linux in P2P mode the kernel automatically removes an existing peer
@@ -503,8 +504,11 @@  dco_p2p_add_new_peer(struct context *c)
         c->c2.tls_multi->dco_peer_id = -1;
     }
 #endif
+    if (sock->bind_local && sock->info.lsa->bind_local)
+        localaddr = sock->info.lsa->bind_local->ai_addr;
+
     int ret = dco_new_peer(&c->c1.tuntap->dco, multi->peer_id,
-                           c->c2.link_sockets[0]->sd, NULL, remoteaddr, NULL, NULL);
+                           c->c2.link_sockets[0]->sd, localaddr, remoteaddr, NULL, NULL);
     if (ret < 0)
     {
         return ret;
@@ -550,7 +554,7 @@  dco_multi_get_localaddr(struct multi_context *m, struct multi_instance *mi,
         {
             struct sockaddr_in *sock_in4 = (struct sockaddr_in *)local;
 #if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
-            sock_in4->sin_addr = actual->pi.in4.ipi_addr;
+            sock_in4->sin_addr = actual->pi.in4.ipi_spec_dst;
 #elif defined(IP_RECVDSTADDR)
             sock_in4->sin_addr = actual->pi.in4;
 #else
@@ -616,10 +620,15 @@  dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi)
         vpn_addr6 = &c->c2.push_ifconfig_ipv6_local;
     }
 
+    struct link_socket *ls = c->c2.link_sockets[0];
     if (dco_multi_get_localaddr(m, mi, &local))
     {
         localaddr = (struct sockaddr *)&local;
     }
+    else if (ls->bind_local && ls->info.lsa->bind_local)
+    {
+        localaddr = ls->info.lsa->bind_local->ai_addr;
+    }
 
     int ret = dco_new_peer(&c->c1.tuntap->dco, peer_id, sd, localaddr,
                            remoteaddr, vpn_addr4, vpn_addr6);