[Openvpn-devel,v3,07/25] dco: add option check - disable DCO if conflict is detected
Commit Message
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
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
@@ -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 \
new file mode 100644
@@ -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) */
@@ -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" />
@@ -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>
@@ -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