[Openvpn-devel,v3,07/25] dco: add option check - disable DCO if conflict is detected

Message ID 20220718221757.545-1-a@unstable.cc
State Accepted
Headers show
Series None | expand

Commit Message

Antonio Quartulli July 18, 2022, 12:17 p.m. UTC
Signed-off-by: Antonio Quartulli <a@unstable.cc>
---

** this patch should be applied before 05/27 **

Changes from v2:
* add actual invocation to dco_check_option_conflict() in options.c
* add missing '}' in dco_check_option_conflict_ce()

Changes from v1:
* add 'already existing device check' to dco_check_option_conflict_platform()
  so that DCO can be pre-emptively disabled if the following are true:
  - an iface with the same name as provided by the user exists
  - the iface is non-DCO

 src/openvpn/Makefile.am             |   2 +-
 src/openvpn/dco.c                   | 190 ++++++++++++++++++++++++++++
 src/openvpn/openvpn.vcxproj         |   1 +
 src/openvpn/openvpn.vcxproj.filters |   3 +
 src/openvpn/options.c               |   5 +
 5 files changed, 200 insertions(+), 1 deletion(-)
 create mode 100644 src/openvpn/dco.c

Comments

Gert Doering July 18, 2022, 11:25 p.m. UTC | #1
Acked-by: Gert Doering <gert@greenie.muc.de>

Discussed this at length on IRC.  We put this in first (+08), so
*05* can go in without breaking non-DCO builds - because this one will
ensure "disable_dco" is set there.

Understanding the DCO and non-DCO code is a bit confusing - the whole
dco.c is #ifdef ENABLE_DCO, and dco_check_option_conflict() will
be a "static inline ... return false;" in that case - and the dco.h
bits of this came in via e34437c26b, so not obvious from this patch
how the pieces work together.

This said, I've thrown 07 v3 at the t_server test machinery (Gentoo,
with no "--enable-dco" at configure times) and it passed everything -
unsurprisingly, as the new code is not compiled in.

Testing with --enable-dco also caused no problems - this test basically
is a "will these parts compile correctly?" sanity check - the code
will detect "there is no DCO kernel support here" on my machine and
auto-disable DCO...

2022-07-19 11:06:31 Cannot find ovpn_dco netlink component: Object not found
2022-07-19 11:06:31 Note: Kernel support for ovpn-dco missing, disabling data channel offload.

On a machine *with* kernel support *and* --enable-dco, I assume this will
also do nothing (as open_tun() is not yet DCO'ed - that's 05).


I have only skimmed the actual option check - seems to make sense :-)
(and has an ACK from Arne, on 07 v1).

Whether the linux "fixed" check will work, we'll see after 05 is merged.


Your patch has been applied to the master branch.

commit 8989b0f2833d25c97654c25fa6a49d8fc0ef903d
Author: Antonio Quartulli
Date:   Tue Jul 19 00:17:57 2022 +0200

     dco: add option check - disable DCO if conflict is detected

     Signed-off-by: Antonio Quartulli <a@unstable.cc>
     Acked-by: Gert Doering <gert@greenie.muc.de>
     Message-Id: <20220718221757.545-1-a@unstable.cc>
     URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24701.html
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering

Patch

diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 91635b67..aaa1dbce 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -53,7 +53,7 @@  openvpn_SOURCES = \
 	crypto.c crypto.h crypto_backend.h \
 	crypto_openssl.c crypto_openssl.h \
 	crypto_mbedtls.c crypto_mbedtls.h \
-	dco.h dco_internal.h \
+	dco.c dco.h dco_internal.h \
 	dco_linux.c dco_linux.h \
 	dhcp.c dhcp.h \
 	dns.c dns.h \
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
new file mode 100644
index 00000000..b3fd135f
--- /dev/null
+++ b/src/openvpn/dco.c
@@ -0,0 +1,190 @@ 
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2021-2022 Arne Schwabe <arne@rfc2549.org>
+ *  Copyright (C) 2021-2022 Antonio Quartulli <a@unstable.cc>
+ *  Copyright (C) 2021-2022 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#if defined(ENABLE_DCO)
+
+#include "syshead.h"
+#include "dco.h"
+#include "networking.h"
+#include "options.h"
+#include "ssl_ncp.h"
+#include "tun.h"
+
+static bool
+dco_check_option_conflict_platform(int msglevel, const struct options *o)
+{
+#if defined(TARGET_LINUX)
+    /* if the device name is fixed, we need to check if an interface with this
+     * name already exists. IF it does, it must be a DCO interface, otherwise
+     * DCO has to be disabled in order to continue.
+     */
+    if (tun_name_is_fixed(o->dev))
+    {
+        char iftype[IFACE_TYPE_LEN_MAX];
+        /* we pass NULL as net_ctx because using DCO on Linux implies that we
+         * are using SITNL and the latter does not need any context. This way we
+         * don't need to have the net_ctx percolate all the way here
+         */
+        int ret = net_iface_type(NULL, o->dev, iftype);
+        if ((ret == 0) && (strcmp(iftype, "ovpn-dco") != 0))
+        {
+            msg(msglevel, "Interface %s exists and is non-DCO. Disabling data channel offload",
+                o->dev);
+            return false;
+        }
+        else if ((ret < 0) && (ret != -ENODEV))
+        {
+            msg(msglevel, "Cannot retrieve type of device %s: %s (%d)", o->dev,
+                strerror(-ret), ret);
+        }
+    }
+#endif /* if defined(TARGET_LINUX) */
+    return true;
+}
+
+static bool
+dco_check_option_conflict_ce(const struct connection_entry *ce, int msglevel)
+{
+    if (ce->fragment)
+    {
+        msg(msglevel, "Note: --fragment disables data channel offload.");
+        return false;
+    }
+
+    if (ce->http_proxy_options)
+    {
+        msg(msglevel, "Note: --http-proxy disables data channel offload.");
+        return false;
+    }
+
+    if (ce->socks_proxy_server)
+    {
+        msg(msglevel, "Note: --socks-proxy disables data channel offload.");
+        return false;
+    }
+
+    return true;
+}
+
+bool
+dco_check_option_conflict(int msglevel, const struct options *o)
+{
+    if (o->tuntap_options.disable_dco)
+    {
+        /* already disabled by --disable-dco, no need to print warnings */
+        return false;
+    }
+
+    if (!dco_available(msglevel))
+    {
+        return false;
+    }
+
+    if (!dco_check_option_conflict_platform(msglevel, o))
+    {
+        return false;
+    }
+
+    if (dev_type_enum(o->dev, o->dev_type) != DEV_TYPE_TUN)
+    {
+        msg(msglevel, "Note: dev-type not tun, disabling data channel offload.");
+        return false;
+    }
+
+    /* At this point the ciphers have already been normalised */
+    if (o->enable_ncp_fallback
+        && !tls_item_in_cipher_list(o->ciphername, DCO_SUPPORTED_CIPHERS))
+    {
+        msg(msglevel, "Note: --data-cipher-fallback with cipher '%s' "
+            "disables data channel offload.", o->ciphername);
+        return false;
+    }
+
+    if (o->connection_list)
+    {
+        const struct connection_list *l = o->connection_list;
+        for (int i = 0; i < l->len; ++i)
+        {
+            if (!dco_check_option_conflict_ce(l->array[i], msglevel))
+            {
+                return false;
+            }
+        }
+    }
+    else
+    {
+        if (!dco_check_option_conflict_ce(&o->ce, msglevel))
+        {
+            return false;
+        }
+    }
+
+    if (o->mode == MODE_SERVER && o->topology != TOP_SUBNET)
+    {
+        msg(msglevel, "Note: NOT using '--topology subnet' disables data channel offload.");
+        return false;
+    }
+
+#if defined(USE_COMP)
+    if (o->comp.alg != COMP_ALG_UNDEF)
+    {
+        msg(msglevel, "Note: Using compression disables data channel offload.");
+
+        if (o->mode == MODE_SERVER && !(o->comp.flags & COMP_F_MIGRATE))
+        {
+            /* We can end up here from the multi.c call, only print the
+             * note if it is not already enabled */
+            msg(msglevel, "Consider using the '--compress migrate' option.");
+        }
+        return false;
+    }
+#endif
+
+    struct gc_arena gc = gc_new();
+    char *tmp_ciphers = string_alloc(o->ncp_ciphers, &gc);
+    const char *token;
+    while ((token = strsep(&tmp_ciphers, ":")))
+    {
+        if (!tls_item_in_cipher_list(token, DCO_SUPPORTED_CIPHERS))
+        {
+            msg(msglevel, "Note: cipher '%s' in --data-ciphers is not supported "
+                "by ovpn-dco, disabling data channel offload.", token);
+            gc_free(&gc);
+            return false;
+        }
+    }
+    gc_free(&gc);
+
+    return true;
+}
+
+#endif /* defined(ENABLE_DCO) */
diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj
index bc1a0300..0b3db7c7 100644
--- a/src/openvpn/openvpn.vcxproj
+++ b/src/openvpn/openvpn.vcxproj
@@ -276,6 +276,7 @@ 
     <ClCompile Include="crypto.c" />
     <ClCompile Include="crypto_openssl.c" />
     <ClCompile Include="cryptoapi.c" />
+    <ClCompile Include="dco.c" />
     <ClCompile Include="dco_linux.c" />
     <ClCompile Include="dhcp.c" />
     <ClCompile Include="dns.c" />
diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters
index 3c21a4c6..16905079 100644
--- a/src/openvpn/openvpn.vcxproj.filters
+++ b/src/openvpn/openvpn.vcxproj.filters
@@ -36,6 +36,9 @@ 
     <ClCompile Include="cryptoapi.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="dco.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="dco_linux.c">
       <Filter>Source Files</Filter>
     </ClCompile>
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 95d4008a..7b919a1e 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3645,6 +3645,11 @@  options_postprocess_mutate(struct options *o, struct env_set *es)
         o->verify_hash_no_ca = true;
     }
 
+    /* check if any option should force disabling DCO */
+#if defined(TARGET_LINUX)
+    o->tuntap_options.disable_dco = !dco_check_option_conflict(D_DCO, o);
+#endif
+
     /*
      * Save certain parms before modifying options during connect, especially
      * when using --pull