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

Message ID 20220712221341.16175-1-a@unstable.cc
State Changes Requested
Headers show
Series None | expand

Commit Message

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

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                   | 189 ++++++++++++++++++++++++++++
 src/openvpn/openvpn.vcxproj         |   1 +
 src/openvpn/openvpn.vcxproj.filters |   3 +
 4 files changed, 194 insertions(+), 1 deletion(-)
 create mode 100644 src/openvpn/dco.c

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..8d1ca37e
--- /dev/null
+++ b/src/openvpn/dco.c
@@ -0,0 +1,189 @@ 
+/*
+ *  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>