From patchwork Thu Jun 6 11:33:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "ralf_lici (Code Review)" X-Patchwork-Id: 3725 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:4507:b0:57d:b2cb:6cf with SMTP id d7csp280804mae; Thu, 6 Jun 2024 04:34:36 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCXNHG1GVYj58KaPNh5G5/ZjZ/0WU0OW6Akt1/aBSkSGFYMFqXU27MK6Am89EzeWsybvm9KcJ8Rw2Cn7K254HgDtYNVWrk4= X-Google-Smtp-Source: AGHT+IGMZqpU08RFiQhLYHykpdWZsIEP/fi09CSAcNocm8bBh2WhWL7+M3afGRZeUw1WcY2tpNQo X-Received: by 2002:a05:6e02:178f:b0:374:60da:64dd with SMTP id e9e14a558f8ab-374b1ee27b1mr56466285ab.1.1717673676483; Thu, 06 Jun 2024 04:34:36 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1717673676; cv=none; d=google.com; s=arc-20160816; b=WtqJj1NlYELlVIbdf6T3IdK+0nmSMQqVZRv6Gbb6VcRnbW+dGgFO0xPzuZaVyX6j7I Pbz4gjYSLve0+BxxOdypDtp9gUc3XDJKd0V5/YOtQwJdIv/5H8HBqKHJW7ltG5z1wD4S JyyJ6c+D9X34h0n/3V9emBUVKpJvLyaRFeRzWXuAFdj5X1ilbkrzfCMGqyeG5dDlGzfU yzC+A2Q6xRocVkG5FqObQDy/iTdFYKkSyJy60Ry4WcuOSzaynE6w9qf4bXV2zlCcYyez Ca0HRWuW+F6em7o4mrkdQucthTUkTJU/IKnLwo2/dDH+jZRb1Uu1UJ3aDSj8jkVHOGNF QtHA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:cc:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject:user-agent :mime-version:message-id:references:auto-submitted:to:date:from :dkim-signature:dkim-signature:dkim-signature; bh=fKmnmKBDdER5rihWzg9bVvK9y0b2OsC3INgj/k5s8O8=; fh=U7wEyxtwz2o5+UdevFSA47vNeG9knhWH0KV//QhD5a0=; b=jwKOItMrI3OKYxg3IV6cdya4TmoEHBpNf7NNEQAFlgAHR9DFSeBaR76ywjYKdJ06q4 3dRJMaJDkAfpBPooX/BYBWHQuNccTStdZwp7qQbuzfS84evrRZB3Z3OUgKmz+eRurKPw Dc358jnX8WR/b8lnIC6VDO1kg8OP8gw+2sCaoViuofOlCHmlQLCjma/Vj3EnRG1EkLYZ b2Aszw+HbYaWWJdqtprCbK+SaWj9oy3dcGeShBjiarCKqU+i6KGlD5EW35oHhK7qHWF4 ebiR+TVOKKYHXX66X9nWwsdZbBSywOx8pFB7Ylm6fcPtEm7cfl3Fd71MhVyKQpkpsreL zczg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=GMsXlRs2; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=AiB460yk; dkim=neutral (body hash did not verify) header.i=@openvpn.net header.s=google header.b=TrFkzSdS; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=openvpn.net Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 41be03b00d2f7-6de2760ace9si1002853a12.518.2024.06.06.04.34.36 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 06 Jun 2024 04:34:36 -0700 (PDT) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=GMsXlRs2; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=AiB460yk; dkim=neutral (body hash did not verify) header.i=@openvpn.net header.s=google header.b=TrFkzSdS; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=openvpn.net Received: from [127.0.0.1] (helo=sfs-ml-4.v29.lw.sourceforge.com) by sfs-ml-4.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1sFBNc-0003Bk-9l; Thu, 06 Jun 2024 11:34:04 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-4.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1sFBNZ-0003BZ-FQ for openvpn-devel@lists.sourceforge.net; Thu, 06 Jun 2024 11:34:01 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Type:Content-Transfer-Encoding:MIME-Version :Message-ID:Reply-To:References:Subject:List-Unsubscribe:List-Id:Cc:To:Date: From:Sender:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:List-Help: List-Subscribe:List-Post:List-Owner:List-Archive; bh=pIKK9Nq/Rx+f30GQFUekbGTcgs/1sw0HFfWNZazjtHU=; b=GMsXlRs2jwnSy93TQr2FrhVX/3 qCej9xPC30v3tnjTuAm0OKYLXL5RHrmRznARQUO+2UQ9HWKfRnJqEVMIytEDhL8MacpreLActcXFC HUrC8qOgU4h4fbbjM5tKUVcL2K26SGzRoGikJf3D4Z8Y/Iw1rQ64cQOldBxTxrUKRjhc=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Type:Content-Transfer-Encoding:MIME-Version:Message-ID:Reply-To: References:Subject:List-Unsubscribe:List-Id:Cc:To:Date:From:Sender:Content-ID :Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To: Resent-Cc:Resent-Message-ID:In-Reply-To:List-Help:List-Subscribe:List-Post: List-Owner:List-Archive; bh=pIKK9Nq/Rx+f30GQFUekbGTcgs/1sw0HFfWNZazjtHU=; b=A iB460ykXKSgwcQLRoGsyOPyF7RRyl4K0pA+2LDWoCAxmJk/ggAGzBgxuPtre4W83VHnR1CIYmdn2U sI4IOGu6UAiNej8mh4uVxULxrgR0IrSZvYOFomWMCFc3jl2crdEs54r7bu3JXolHv+o3UzvopdpuO EvpIW7OnpqaZ40w8=; Received: from mail-wm1-f54.google.com ([209.85.128.54]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.95) id 1sFBNU-0003TI-Sl for openvpn-devel@lists.sourceforge.net; Thu, 06 Jun 2024 11:34:01 +0000 Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-4215c65dca2so5954435e9.1 for ; Thu, 06 Jun 2024 04:33:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=openvpn.net; s=google; t=1717673624; x=1718278424; darn=lists.sourceforge.net; h=user-agent:content-disposition:content-transfer-encoding :mime-version:message-id:reply-to:references:subject :list-unsubscribe:list-id:auto-submitted:cc:to:date:from:from:to:cc :subject:date:message-id:reply-to; bh=pIKK9Nq/Rx+f30GQFUekbGTcgs/1sw0HFfWNZazjtHU=; b=TrFkzSdSMscy3Sc8DXIn1zsO/+nfSkFKsI+uiYe/2MLNlMEy1LDmi0Jp1sb1vnNtFx 7RRHKxTKuJWzwCvisMvAMCq9I2J6q3Tz6gWykMU0jRpd7zUI8lpGxnU4hsLDDvWysVny vqktsgjMOBKweIjzcxelebjyRNg7JhZv9vuGuKqJcMy/EjVXnbUQtAOAzS2I/g1u0Ere Vxp+o1Y8liClSr6VPC4VwFB8c+xjsJmA8FoEvRpRj6KBD3g0u9XOa5Bdv79PIGpGc//z HYb/yVbhPc7QIlMZFm4QbjG8IjiCRXuZ+I1sp3D6ym8nxbq+raX5bhgxvZMoUIi0LkhZ rntw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1717673624; x=1718278424; h=user-agent:content-disposition:content-transfer-encoding :mime-version:message-id:reply-to:references:subject :list-unsubscribe:list-id:auto-submitted:cc:to:date:from :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=pIKK9Nq/Rx+f30GQFUekbGTcgs/1sw0HFfWNZazjtHU=; b=XDp0+7GuTTwcGJpO8n/dS17fs2DSo7Y7aRlyR2i3EgGJLKI7wVfX30toyWsr+QvDFt WBNw2oYE+Mz9FRp6Go+6QNQDVDESckbZuUqsWnqgIhPO2v3rIrgRLUKZX5sXx2m1FBmW YOfDTS1At3B0+q/yBIWZ9X/n9dYLXxOKBMNjBy/dTnBbIsTqwbuQF5XW80LahxQPwUle RvJIP3g0eiQG9URfGz4/HFQky3dAt41mSyu/VJT5tWv1HeJ+ZplkJ7ugcvGEP6nsXkHq RqEksU1p/+sI0uwZ9em+JSMSr41engAXqIJw8f8t+/lMxMYaxvRceSZ2m7Rywh3FYjZg s0KQ== X-Gm-Message-State: AOJu0YyXwVb33YTelZqUthYYJbGcGvUHh1ylhR7iK6stJVzlCVlFYCQX PGjkoHn1Mi5ZvI9ytyVD9mez8CORIBjzDbygj/CqfpvYlNkC4I0OJWDblj3n2Qs= X-Received: by 2002:a05:600c:1c12:b0:416:bc18:2a00 with SMTP id 5b1f17b1804b1-4215633a290mr41800515e9.38.1717673623758; Thu, 06 Jun 2024 04:33:43 -0700 (PDT) Received: from gerrit.openvpn.in (ec2-18-159-0-78.eu-central-1.compute.amazonaws.com. [18.159.0.78]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-35ef5d4ad2esm1301435f8f.43.2024.06.06.04.33.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Jun 2024 04:33:43 -0700 (PDT) From: "its_Giaan (Code Review)" X-Google-Original-From: "its_Giaan (Code Review)" X-Gerrit-PatchSet: 1 Date: Thu, 6 Jun 2024 11:33:42 +0000 To: plaisthos , flichtenheld Auto-Submitted: auto-generated X-Gerrit-MessageType: newchange X-Gerrit-Change-Id: I6662605b60d4859949970cfe256c55cbcbd4a672 X-Gerrit-Change-Number: 662 X-Gerrit-Project: openvpn X-Gerrit-ChangeURL: X-Gerrit-Commit: 8cbf4d63502bbe5fb26e8cf9c7694b6f2e07a228 References: Message-ID: MIME-Version: 1.0 User-Agent: Gerrit/3.8.2 X-Spam-Score: -0.2 (/) X-Spam-Report: Spam detection software, running on the system "util-spamd-1.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: Attention is currently required from: flichtenheld, plaisthos. Hello plaisthos, flichtenheld, I'd like you to do a code review. Please visit Content analysis details: (-0.2 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [URIs: makefile.am] 0.0 RCVD_IN_VALIDITY_RPBL_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [209.85.128.54 listed in bl.score.senderscore.com] 0.0 RCVD_IN_VALIDITY_CERTIFIED_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [209.85.128.54 listed in sa-accredit.habeas.com] -0.0 RCVD_IN_MSPIKE_H2 RBL: Average reputation (+2) [209.85.128.54 listed in wl.mailspike.net] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record 0.0 WEIRD_PORT URI: Uses non-standard port number for HTTP 0.0 HTML_MESSAGE BODY: HTML included in message 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.0 T_KAM_HTML_FONT_INVALID Test for Invalidly Named or Formatted Colors in HTML -0.0 T_SCC_BODY_TEXT_LINE No description available. X-Headers-End: 1sFBNU-0003TI-Sl Subject: [Openvpn-devel] [XL] Change in openvpn[master]: Lay the foundation to support both old and new versions of DCO X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: gianmarco@mandelbit.com, arne-openvpn@rfc2549.org, openvpn-devel@lists.sourceforge.net, frank@lichtenheld.com Cc: openvpn-devel Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: =?utf-8?q?1801111393221856927?= X-GMAIL-MSGID: =?utf-8?q?1801111393221856927?= X-getmail-filter-classifier: gerrit message type newchange Attention is currently required from: flichtenheld, plaisthos. Hello plaisthos, flichtenheld, I'd like you to do a code review. Please visit http://gerrit.openvpn.net/c/openvpn/+/662?usp=email to review the following change. Change subject: Lay the foundation to support both old and new versions of DCO ...................................................................... Lay the foundation to support both old and new versions of DCO Added a new abstraction layer based on the `struct dco_ops`, a structure of function pointers that ensures the appropriate APIs at runtime are used for DCO communication between kernel and user space, selection is based on the available module's family name. The old version, referred to as "ovpn_dco_v2", and its related commands and functions, will be distinguished with the "v2" suffix. Change-Id: I6662605b60d4859949970cfe256c55cbcbd4a672 Signed-off-by: Gianmarco De Gregori --- M CMakeLists.txt M src/openvpn/Makefile.am M src/openvpn/dco.c M src/openvpn/dco.h M src/openvpn/dco_freebsd.c M src/openvpn/dco_linux.c M src/openvpn/dco_linux.h A src/openvpn/dco_linux_v2.c M src/openvpn/dco_win.c M src/openvpn/forward.c 10 files changed, 923 insertions(+), 655 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/62/662/1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 096837d..732be9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -398,6 +398,7 @@ src/openvpn/dco_win.h src/openvpn/dco_linux.c src/openvpn/dco_linux.h + src/openvpn/dco_linux_v2.c src/openvpn/dco_freebsd.c src/openvpn/dco_freebsd.h src/openvpn/dhcp.c diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index 56cce9d..286d3c5 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -54,6 +54,7 @@ dco.c dco.h dco_internal.h \ dco_freebsd.c dco_freebsd.h \ dco_linux.c dco_linux.h \ + dco_linux_v2.c \ dco_win.c dco_win.h \ dhcp.c dhcp.h \ dns.c dns.h \ diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c index 78243b1..80184f8 100644 --- a/src/openvpn/dco.c +++ b/src/openvpn/dco.c @@ -396,7 +396,7 @@ /* now that all options have been confirmed to be supported, check * if DCO is truly available on the system */ - return dco_available(msglevel); + return dco_available(msglevel, NULL); } bool diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h index 50ebb35..fc34da8 100644 --- a/src/openvpn/dco.h +++ b/src/openvpn/dco.h @@ -56,7 +56,7 @@ * @param msglevel level to print messages to * @return true if ovpn-dco is available, false otherwise */ -bool dco_available(int msglevel); +bool dco_available(int msglevel, dco_context_t *dco); /** @@ -254,7 +254,7 @@ typedef void *dco_context_t; static inline bool -dco_available(int msglevel) +dco_available(int msglevel, dco_context_t *dco) { return false; } diff --git a/src/openvpn/dco_freebsd.c b/src/openvpn/dco_freebsd.c index 7c8b29c..93c0813 100644 --- a/src/openvpn/dco_freebsd.c +++ b/src/openvpn/dco_freebsd.c @@ -569,7 +569,7 @@ } bool -dco_available(int msglevel) +dco_available(int msglevel, dco_context_t *dco) { struct if_clonereq ifcr; char *buf = NULL; diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index b2584b9..4feae6d 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -51,32 +51,28 @@ #include -/* libnl < 3.5.0 does not set the NLA_F_NESTED on its own, therefore we - * have to explicitly do it to prevent the kernel from failing upon - * parsing of the message - */ -#define nla_nest_start(_msg, _type) \ - nla_nest_start(_msg, (_type) | NLA_F_NESTED) - -static int ovpn_get_mcast_id(dco_context_t *dco); - -void dco_check_key_ctx(const struct key_ctx_bi *key); - -typedef int (*ovpn_nl_cb)(struct nl_msg *msg, void *arg); - -/** - * @brief resolves the netlink ID for ovpn-dco - * - * This function queries the kernel via a netlink socket - * whether the ovpn-dco netlink namespace is available - * - * This function can be used to determine if the kernel - * supports DCO offloading. - * - * @return ID on success, negative error code on error - */ static int -resolve_ovpn_netlink_id(int msglevel) +do_family_name_resolve(struct nl_sock *nl_sock, int msglevel, const char *family_name, + dco_context_t *dco, const struct dco_ops *ops) +{ + int ret = genl_ctrl_resolve(nl_sock, family_name); + + if (ret < 0) + { + msg(msglevel, "Cannot find %s netlink component: %s", family_name, nl_geterror(ret)); + } + + if (dco) + { + /* Ensures that we will use the proper ops for the dco version available */ + dco->ops = ops; + } + + return ret; +} + +int +resolve_ovpn_netlink_id(int msglevel, dco_context_t *dco) { int ret; struct nl_sock *nl_sock = nl_socket_alloc(); @@ -96,39 +92,14 @@ } set_cloexec(nl_socket_get_fd(nl_sock)); - ret = genl_ctrl_resolve(nl_sock, OVPN_NL_NAME); - if (ret < 0) - { - msg(msglevel, "Cannot find ovpn_dco netlink component: %s", - nl_geterror(ret)); - } + ret = do_family_name_resolve(nl_sock, msglevel, OVPN_NL_NAME, dco, &dco_ops_v2); err_sock: nl_socket_free(nl_sock); return ret; } -static struct nl_msg * -ovpn_dco_nlmsg_create(dco_context_t *dco, enum ovpn_nl_commands cmd) -{ - struct nl_msg *nl_msg = nlmsg_alloc(); - if (!nl_msg) - { - msg(M_ERR, "cannot allocate netlink message"); - return NULL; - } - - genlmsg_put(nl_msg, 0, 0, dco->ovpn_dco_id, 0, 0, cmd, 0); - NLA_PUT_U32(nl_msg, OVPN_ATTR_IFINDEX, dco->ifindex); - - return nl_msg; -nla_put_failure: - nlmsg_free(nl_msg); - msg(M_INFO, "cannot put into netlink message"); - return NULL; -} - -static int +int ovpn_nl_recvmsgs(dco_context_t *dco, const char *prefix) { int ret = nl_recvmsgs(dco->nl_sock, dco->nl_cb); @@ -166,18 +137,7 @@ return ret; } -/** - * Send a prepared netlink message and registers cb as callback if non-null. - * - * The method will also free nl_msg - * @param dco The dco context to use - * @param nl_msg the message to use - * @param cb An optional callback if the caller expects an answer - * @param cb_arg An optional param to pass to the callback - * @param prefix A prefix to report in the error message to give the user context - * @return status of sending the message - */ -static int +int ovpn_nl_msg_send(dco_context_t *dco, struct nl_msg *nl_msg, ovpn_nl_cb cb, void *cb_arg, const char *prefix) { @@ -218,71 +178,6 @@ } int -dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd, - struct sockaddr *localaddr, struct sockaddr *remoteaddr, - struct in_addr *remote_in4, struct in6_addr *remote_in6) -{ - struct gc_arena gc = gc_new(); - const char *remotestr = "[undefined]"; - if (remoteaddr) - { - remotestr = print_sockaddr(remoteaddr, &gc); - } - msg(D_DCO_DEBUG, "%s: peer-id %d, fd %d, remote addr: %s", __func__, - peerid, sd, remotestr); - - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_NEW_PEER); - struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_NEW_PEER); - int ret = -EMSGSIZE; - - NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_PEER_ID, peerid); - NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_SOCKET, sd); - - /* Set the remote endpoint if defined (for UDP) */ - if (remoteaddr) - { - remoteaddr = mapped_v4_to_v6(remoteaddr, &gc); - int alen = af_addr_size(remoteaddr->sa_family); - - NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE, alen, remoteaddr); - } - - if (localaddr) - { - localaddr = mapped_v4_to_v6(localaddr, &gc); - if (localaddr->sa_family == AF_INET) - { - NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_LOCAL_IP, sizeof(struct in_addr), - &((struct sockaddr_in *)localaddr)->sin_addr); - } - else if (localaddr->sa_family == AF_INET6) - { - NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_LOCAL_IP, sizeof(struct in6_addr), - &((struct sockaddr_in6 *)localaddr)->sin6_addr); - } - } - - /* Set the primary VPN IP addresses of the peer */ - if (remote_in4) - { - NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_IPV4, remote_in4->s_addr); - } - if (remote_in6) - { - NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_IPV6, sizeof(struct in6_addr), - remote_in6); - } - nla_nest_end(nl_msg, attr); - - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); - -nla_put_failure: - nlmsg_free(nl_msg); - gc_free(&gc); - return ret; -} - -static int ovpn_nl_cb_finish(struct nl_msg (*msg) __attribute__ ((unused)), void *arg) { int *status = arg; @@ -291,15 +186,7 @@ return NL_SKIP; } -/* This function is used as error callback on the netlink socket. - * When something goes wrong and the kernel returns an error, this function is - * invoked. - * - * We pass the error code to the user by means of a variable pointed by *arg - * (supplied by the user when setting this callback) and we parse the kernel - * reply to see if it contains a human-readable error. If found, it is printed. - */ -static int +int ovpn_nl_cb_error(struct sockaddr_nl (*nla) __attribute__ ((unused)), struct nlmsgerr *err, void *arg) { @@ -342,10 +229,16 @@ return NL_STOP; } -static void +void ovpn_dco_init_netlink(dco_context_t *dco) { - dco->ovpn_dco_id = resolve_ovpn_netlink_id(M_ERR); + struct gc_arena gc = gc_new(); + if (!dco->ops) + { + ALLOC_OBJ_CLEAR_GC(dco->ops, struct dco_ops, &gc); + } + + dco->ovpn_dco_id = resolve_ovpn_netlink_id(M_ERR, dco); dco->nl_sock = nl_socket_alloc(); @@ -388,6 +281,7 @@ * overrun with very fast connecting/disconnecting clients. * TODO: fix this in a better and more reliable way */ ASSERT(!nl_socket_set_buffer_size(dco->nl_sock, 1024*1024, 1024*1024)); + gc_free(&gc); } bool @@ -411,7 +305,7 @@ return true; } -static void +void ovpn_dco_uninit_netlink(dco_context_t *dco) { nl_socket_free(dco->nl_sock); @@ -423,7 +317,7 @@ CLEAR(dco); } -static void +void ovpn_dco_register(dco_context_t *dco) { msg(D_DCO_DEBUG, __func__); @@ -447,205 +341,11 @@ int open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev) { - msg(D_DCO_DEBUG, "%s: %s", __func__, dev); - ASSERT(tt->type == DEV_TYPE_TUN); - - int ret = net_iface_new(ctx, dev, "ovpn-dco", &tt->dco); - if (ret < 0) - { - msg(D_DCO_DEBUG, "Cannot create DCO interface %s: %d", dev, ret); - return ret; - } - - tt->dco.ifindex = if_nametoindex(dev); - if (!tt->dco.ifindex) - { - msg(M_FATAL, "DCO: cannot retrieve ifindex for interface %s", dev); - } - - tt->dco.dco_message_peer_id = -1; - - ovpn_dco_register(&tt->dco); - - return 0; -} - -void -close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx) -{ - msg(D_DCO_DEBUG, __func__); - - net_iface_del(ctx, tt->actual_name); - ovpn_dco_uninit_netlink(&tt->dco); -} - -int -dco_swap_keys(dco_context_t *dco, unsigned int peerid) -{ - msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid); - - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_SWAP_KEYS); - if (!nl_msg) - { - return -ENOMEM; - } - - struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_SWAP_KEYS); - int ret = -EMSGSIZE; - NLA_PUT_U32(nl_msg, OVPN_SWAP_KEYS_ATTR_PEER_ID, peerid); - nla_nest_end(nl_msg, attr); - - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); - -nla_put_failure: - nlmsg_free(nl_msg); - return ret; -} - - -int -dco_del_peer(dco_context_t *dco, unsigned int peerid) -{ - msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid); - - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_DEL_PEER); - if (!nl_msg) - { - return -ENOMEM; - } - - struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_DEL_PEER); - int ret = -EMSGSIZE; - NLA_PUT_U32(nl_msg, OVPN_DEL_PEER_ATTR_PEER_ID, peerid); - nla_nest_end(nl_msg, attr); - - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); - -nla_put_failure: - nlmsg_free(nl_msg); - return ret; -} - - -int -dco_del_key(dco_context_t *dco, unsigned int peerid, - dco_key_slot_t slot) -{ - msg(D_DCO_DEBUG, "%s: peer-id %d, slot %d", __func__, peerid, slot); - - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_DEL_KEY); - if (!nl_msg) - { - return -ENOMEM; - } - - struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_DEL_KEY); - int ret = -EMSGSIZE; - NLA_PUT_U32(nl_msg, OVPN_DEL_KEY_ATTR_PEER_ID, peerid); - NLA_PUT_U8(nl_msg, OVPN_DEL_KEY_ATTR_KEY_SLOT, slot); - nla_nest_end(nl_msg, attr); - - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); - -nla_put_failure: - nlmsg_free(nl_msg); + int ret = tt->dco.ops->open_tun_dco(tt, ctx, dev); return ret; } int -dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, - dco_key_slot_t slot, - const uint8_t *encrypt_key, const uint8_t *encrypt_iv, - const uint8_t *decrypt_key, const uint8_t *decrypt_iv, - const char *ciphername) -{ - msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s", - __func__, slot, keyid, peerid, ciphername); - - const size_t key_len = cipher_kt_key_size(ciphername); - const int nonce_tail_len = 8; - - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_NEW_KEY); - if (!nl_msg) - { - return -ENOMEM; - } - - dco_cipher_t dco_cipher = dco_get_cipher(ciphername); - - int ret = -EMSGSIZE; - struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_NEW_KEY); - NLA_PUT_U32(nl_msg, OVPN_NEW_KEY_ATTR_PEER_ID, peerid); - NLA_PUT_U8(nl_msg, OVPN_NEW_KEY_ATTR_KEY_SLOT, slot); - NLA_PUT_U8(nl_msg, OVPN_NEW_KEY_ATTR_KEY_ID, keyid); - NLA_PUT_U16(nl_msg, OVPN_NEW_KEY_ATTR_CIPHER_ALG, dco_cipher); - - struct nlattr *key_enc = nla_nest_start(nl_msg, - OVPN_NEW_KEY_ATTR_ENCRYPT_KEY); - if (dco_cipher != OVPN_CIPHER_ALG_NONE) - { - NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, key_len, encrypt_key); - NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, nonce_tail_len, - encrypt_iv); - } - nla_nest_end(nl_msg, key_enc); - - struct nlattr *key_dec = nla_nest_start(nl_msg, - OVPN_NEW_KEY_ATTR_DECRYPT_KEY); - if (dco_cipher != OVPN_CIPHER_ALG_NONE) - { - NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, key_len, decrypt_key); - NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, nonce_tail_len, - decrypt_iv); - } - nla_nest_end(nl_msg, key_dec); - - nla_nest_end(nl_msg, attr); - - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); - -nla_put_failure: - nlmsg_free(nl_msg); - return ret; -} - -int -dco_set_peer(dco_context_t *dco, unsigned int peerid, - int keepalive_interval, int keepalive_timeout, int mss) -{ - msg(D_DCO_DEBUG, "%s: peer-id %d, keepalive %d/%d, mss %d", __func__, - peerid, keepalive_interval, keepalive_timeout, mss); - - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_SET_PEER); - if (!nl_msg) - { - return -ENOMEM; - } - - struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_SET_PEER); - int ret = -EMSGSIZE; - NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_PEER_ID, peerid); - NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL, - keepalive_interval); - NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT, - keepalive_timeout); - nla_nest_end(nl_msg, attr); - - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); - -nla_put_failure: - nlmsg_free(nl_msg); - return ret; -} - -/* This function parses the reply provided by the kernel to the CTRL_CMD_GETFAMILY - * message. We parse the reply and we retrieve the multicast group ID associated - * with the "ovpn-dco" netlink family. - * - * The ID is later used to subscribe to the multicast group and be notified - * about any multicast message sent by the ovpn-dco kernel module. - */ -static int mcast_family_handler(struct nl_msg *msg, void *arg) { dco_context_t *dco = arg; @@ -687,12 +387,8 @@ return NL_SKIP; } -/** - * Lookup the multicast id for OpenVPN. This method and its help method currently - * hardcode the lookup to OVPN_NL_NAME and OVPN_NL_MULTICAST_GROUP_PEERS but - * extended in the future if we need to lookup more than one mcast id. - */ -static int + +int ovpn_get_mcast_id(dco_context_t *dco) { dco->ovpn_dco_mcast_id = -ENOENT; @@ -719,299 +415,15 @@ return ret; } -/* This function parses any netlink message sent by ovpn-dco to userspace */ -static int -ovpn_handle_msg(struct nl_msg *msg, void *arg) -{ - dco_context_t *dco = arg; - - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *attrs[OVPN_ATTR_MAX + 1]; - struct nlmsghdr *nlh = nlmsg_hdr(msg); - - if (!genlmsg_valid_hdr(nlh, 0)) - { - msg(D_DCO, "ovpn-dco: invalid header"); - return NL_SKIP; - } - - if (nla_parse(attrs, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL)) - { - msg(D_DCO, "received bogus data from ovpn-dco"); - return NL_SKIP; - } - - /* we must know which interface this message is referring to in order to - * avoid mixing messages for other instances - */ - if (!attrs[OVPN_ATTR_IFINDEX]) - { - msg(D_DCO, "ovpn-dco: Received message without ifindex"); - return NL_SKIP; - } - - uint32_t ifindex = nla_get_u32(attrs[OVPN_ATTR_IFINDEX]); - if (ifindex != dco->ifindex) - { - msg(D_DCO_DEBUG, - "ovpn-dco: ignoring message (type=%d) for foreign ifindex %d", - gnlh->cmd, ifindex); - return NL_SKIP; - } - - /* based on the message type, we parse the subobject contained in the - * message, that stores the type-specific attributes. - * - * the "dco" object is then filled accordingly with the information - * retrieved from the message, so that the rest of the OpenVPN code can - * react as need be. - */ - switch (gnlh->cmd) - { - case OVPN_CMD_DEL_PEER: - { - if (!attrs[OVPN_ATTR_DEL_PEER]) - { - msg(D_DCO, "ovpn-dco: no attributes in OVPN_DEL_PEER message"); - return NL_SKIP; - } - - struct nlattr *dp_attrs[OVPN_DEL_PEER_ATTR_MAX + 1]; - if (nla_parse_nested(dp_attrs, OVPN_DEL_PEER_ATTR_MAX, - attrs[OVPN_ATTR_DEL_PEER], NULL)) - { - msg(D_DCO, "received bogus del peer packet data from ovpn-dco"); - return NL_SKIP; - } - - if (!dp_attrs[OVPN_DEL_PEER_ATTR_REASON]) - { - msg(D_DCO, "ovpn-dco: no reason in DEL_PEER message"); - return NL_SKIP; - } - if (!dp_attrs[OVPN_DEL_PEER_ATTR_PEER_ID]) - { - msg(D_DCO, "ovpn-dco: no peer-id in DEL_PEER message"); - return NL_SKIP; - } - int reason = nla_get_u8(dp_attrs[OVPN_DEL_PEER_ATTR_REASON]); - unsigned int peerid = nla_get_u32(dp_attrs[OVPN_DEL_PEER_ATTR_PEER_ID]); - - msg(D_DCO_DEBUG, "ovpn-dco: received CMD_DEL_PEER, ifindex: %d, peer-id %d, reason: %d", - ifindex, peerid, reason); - dco->dco_message_peer_id = peerid; - dco->dco_del_peer_reason = reason; - dco->dco_message_type = OVPN_CMD_DEL_PEER; - - break; - } - - default: - msg(D_DCO, "ovpn-dco: received unknown command: %d", gnlh->cmd); - dco->dco_message_type = 0; - return NL_SKIP; - } - - return NL_OK; -} - -int -dco_do_read(dco_context_t *dco) -{ - msg(D_DCO_DEBUG, __func__); - nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, dco); - - return ovpn_nl_recvmsgs(dco, __func__); -} - -static void -dco_update_peer_stat(struct context_2 *c2, struct nlattr *tb[], uint32_t id) -{ - if (tb[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES]) - { - c2->dco_read_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES]); - msg(D_DCO_DEBUG, "%s / dco_read_bytes: " counter_format, __func__, - c2->dco_read_bytes); - } - else - { - msg(M_WARN, "%s: no link RX bytes provided in reply for peer %u", - __func__, id); - } - - if (tb[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES]) - { - c2->dco_write_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES]); - msg(D_DCO_DEBUG, "%s / dco_write_bytes: " counter_format, __func__, - c2->dco_write_bytes); - } - else - { - msg(M_WARN, "%s: no link TX bytes provided in reply for peer %u", - __func__, id); - } - - if (tb[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES]) - { - c2->tun_read_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES]); - msg(D_DCO_DEBUG, "%s / tun_read_bytes: " counter_format, __func__, - c2->tun_read_bytes); - } - else - { - msg(M_WARN, "%s: no VPN RX bytes provided in reply for peer %u", - __func__, id); - } - - if (tb[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES]) - { - c2->tun_write_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES]); - msg(D_DCO_DEBUG, "%s / tun_write_bytes: " counter_format, __func__, - c2->tun_write_bytes); - } - else - { - msg(M_WARN, "%s: no VPN TX bytes provided in reply for peer %u", - __func__, id); - } -} - -int -dco_parse_peer_multi(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[OVPN_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - - msg(D_DCO_DEBUG, "%s: parsing message...", __func__); - - nla_parse(tb, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (!tb[OVPN_ATTR_GET_PEER]) - { - return NL_SKIP; - } - - struct nlattr *tb_peer[OVPN_GET_PEER_RESP_ATTR_MAX + 1]; - - nla_parse(tb_peer, OVPN_GET_PEER_RESP_ATTR_MAX, - nla_data(tb[OVPN_ATTR_GET_PEER]), - nla_len(tb[OVPN_ATTR_GET_PEER]), NULL); - - if (!tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]) - { - msg(M_WARN, "%s: no peer-id provided in reply", __func__); - return NL_SKIP; - } - - struct multi_context *m = arg; - uint32_t peer_id = nla_get_u32(tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]); - - if (peer_id >= m->max_clients || !m->instances[peer_id]) - { - msg(M_WARN, "%s: cannot store DCO stats for peer %u", __func__, - peer_id); - return NL_SKIP; - } - - dco_update_peer_stat(&m->instances[peer_id]->context.c2, tb_peer, peer_id); - - return NL_OK; -} - -int -dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m) -{ - msg(D_DCO_DEBUG, "%s", __func__); - - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_GET_PEER); - - nlmsg_hdr(nl_msg)->nlmsg_flags |= NLM_F_DUMP; - - int ret = ovpn_nl_msg_send(dco, nl_msg, dco_parse_peer_multi, m, __func__); - - nlmsg_free(nl_msg); - return ret; -} - -static int -dco_parse_peer(struct nl_msg *msg, void *arg) -{ - struct context *c = arg; - struct nlattr *tb[OVPN_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - - msg(D_DCO_DEBUG, "%s: parsing message...", __func__); - - nla_parse(tb, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (!tb[OVPN_ATTR_GET_PEER]) - { - msg(D_DCO_DEBUG, "%s: malformed reply", __func__); - return NL_SKIP; - } - - struct nlattr *tb_peer[OVPN_GET_PEER_RESP_ATTR_MAX + 1]; - - nla_parse(tb_peer, OVPN_GET_PEER_RESP_ATTR_MAX, - nla_data(tb[OVPN_ATTR_GET_PEER]), - nla_len(tb[OVPN_ATTR_GET_PEER]), NULL); - - if (!tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]) - { - msg(M_WARN, "%s: no peer-id provided in reply", __func__); - return NL_SKIP; - } - - uint32_t peer_id = nla_get_u32(tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]); - if (c->c2.tls_multi->dco_peer_id != peer_id) - { - return NL_SKIP; - } - - dco_update_peer_stat(&c->c2, tb_peer, peer_id); - - return NL_OK; -} - -int -dco_get_peer_stats(struct context *c) -{ - uint32_t peer_id = c->c2.tls_multi->dco_peer_id; - msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peer_id); - - if (!c->c1.tuntap) - { - return 0; - } - - dco_context_t *dco = &c->c1.tuntap->dco; - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_GET_PEER); - struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_GET_PEER); - int ret = -EMSGSIZE; - - NLA_PUT_U32(nl_msg, OVPN_GET_PEER_ATTR_PEER_ID, peer_id); - nla_nest_end(nl_msg, attr); - - ret = ovpn_nl_msg_send(dco, nl_msg, dco_parse_peer, c, __func__); - -nla_put_failure: - nlmsg_free(nl_msg); - return ret; -} - bool -dco_available(int msglevel) +dco_available(int msglevel, dco_context_t *dco) { - if (resolve_ovpn_netlink_id(D_DCO_DEBUG) < 0) + if (resolve_ovpn_netlink_id(D_DCO_DEBUG, dco) < 0) { msg(msglevel, "Note: Kernel support for ovpn-dco missing, disabling data channel offload."); return false; } - return true; } @@ -1043,6 +455,94 @@ return BSTR(&out); } +int +dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd, + struct sockaddr *localaddr, struct sockaddr *remoteaddr, + struct in_addr *remote_in4, struct in6_addr *remote_in6) +{ + int ret = dco->ops->dco_new_peer(dco, peerid, sd, localaddr, remoteaddr, remote_in4, remote_in6); + return ret; +} + +void +close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx) +{ + tt->dco.ops->close_tun_dco(tt, ctx); +} + +int +dco_swap_keys(dco_context_t *dco, unsigned int peerid) +{ + int ret = dco->ops->dco_swap_keys(dco, peerid); + return ret; +} + + +int +dco_del_peer(dco_context_t *dco, unsigned int peerid) +{ + int ret = dco->ops->dco_del_peer(dco, peerid); + return ret; +} + + +int +dco_del_key(dco_context_t *dco, unsigned int peerid, + dco_key_slot_t slot) +{ + int ret = dco->ops->dco_del_key(dco, peerid, slot); + return ret; +} + +int +dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, + dco_key_slot_t slot, + const uint8_t *encrypt_key, const uint8_t *encrypt_iv, + const uint8_t *decrypt_key, const uint8_t *decrypt_iv, + const char *ciphername) +{ + int ret = dco->ops->dco_new_key(dco, peerid, keyid, slot, encrypt_key, encrypt_iv, decrypt_key, decrypt_iv, ciphername); + return ret; +} + +int +dco_set_peer(dco_context_t *dco, unsigned int peerid, + int keepalive_interval, int keepalive_timeout, int mss) +{ + int ret = dco->ops->dco_set_peer(dco, peerid, keepalive_interval, keepalive_timeout, mss); + return ret; +} + +int +dco_do_read(dco_context_t *dco) +{ + int ret = dco->ops->dco_do_read(dco); + return ret; +} + +int +dco_parse_peer_multi(struct nl_msg *msg, void *arg) +{ + struct multi_context *m = arg; + int ret = m->top.c1.tuntap->dco.ops->dco_parse_peer_multi(msg, arg); + + return ret; +} + +int +dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m) +{ + int ret = dco->ops->dco_get_peer_stats_multi(dco, m); + return ret; +} + +int +dco_get_peer_stats(struct context *c) +{ + int ret = c->c1.tuntap->dco.ops->dco_get_peer_stats(c); + return ret; +} + void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) { diff --git a/src/openvpn/dco_linux.h b/src/openvpn/dco_linux.h index 511519a..a3cf528 100644 --- a/src/openvpn/dco_linux.h +++ b/src/openvpn/dco_linux.h @@ -31,9 +31,30 @@ #include #include +#include +#include +#include + +#include "networking.h" +#include "fdmisc.h" + +/* forward declarations (including other headers leads to nasty include + * order problems) + */ +struct multi_context; +struct context_2; +struct key_ctx_bi; + typedef enum ovpn_key_slot dco_key_slot_t; typedef enum ovpn_cipher_alg dco_cipher_t; +/* libnl < 3.5.0 does not set the NLA_F_NESTED on its own, therefore we + * have to explicitly do it to prevent the kernel from failing upon + * parsing of the message + */ +#define nla_nest_start(_msg, _type) \ + nla_nest_start(_msg, (_type) | NLA_F_NESTED) + typedef struct { @@ -53,7 +74,114 @@ int dco_del_peer_reason; uint64_t dco_read_bytes; uint64_t dco_write_bytes; + + const struct dco_ops *ops; } dco_context_t; +struct dco_ops +{ + struct nl_msg *(*ovpn_dco_nlmsg_create)(dco_context_t *dco, int cmd); + int (*dco_new_peer)(dco_context_t *dco, unsigned int peerid, int sd, struct sockaddr *localaddr, struct sockaddr *remoteaddr, struct in_addr *remote_in4, struct in6_addr *remote_in6); + int (*open_tun_dco)(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev); + void (*close_tun_dco)(struct tuntap *tt, openvpn_net_ctx_t *ctx); + int (*dco_swap_keys)(dco_context_t *dco, unsigned int peerid); + int (*dco_del_peer)(dco_context_t *dco, unsigned int peerid); + int (*dco_del_key)(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot); + int (*dco_new_key)(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot, const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key, const uint8_t *decrypt_iv, const char *ciphername); + int (*dco_set_peer)(dco_context_t *dco, unsigned int peerid, int keepalive_interval, int keepalive_timeout, int mss); + int (*ovpn_handle_msg)(struct nl_msg *msg, void *arg); + int (*dco_do_read)(dco_context_t *dco); + void (*dco_update_peer_stat)(struct context_2 *c2, struct nlattr *tb[], uint32_t id); + int (*dco_parse_peer_multi)(struct nl_msg *msg, void *arg); + int (*dco_get_peer_stats_multi)(dco_context_t *dco, struct multi_context *m); + int (*dco_parse_peer)(struct nl_msg *msg, void *arg); + int (*dco_get_peer_stats)(struct context *c); +}; + +extern const struct dco_ops dco_ops_v2; + +int ovpn_get_mcast_id(dco_context_t *dco); + +void dco_check_key_ctx(const struct key_ctx_bi *key); + +typedef int (*ovpn_nl_cb)(struct nl_msg *msg, void *arg); + +/** + * @brief resolves the netlink ID for ovpn-dco + * + * This function queries the kernel via a netlink socket + * whether the ovpn-dco netlink namespace is available + * + * This function can be used to determine if the kernel + * supports DCO offloading. + * + * @return ID on success, negative error code on error + */ +int resolve_ovpn_netlink_id(int msglevel, dco_context_t *dco); + +int ovpn_nl_recvmsgs(dco_context_t *dco, const char *prefix); + +/** + * Send a prepared netlink message and registers cb as callback if non-null. + * + * The method will also free nl_msg + * @param dco The dco context to use + * @param nl_msg the message to use + * @param cb An optional callback if the caller expects an answer + * @param cb_arg An optional param to pass to the callback + * @param prefix A prefix to report in the error message to give the user context + * @return status of sending the message + */ +int ovpn_nl_msg_send(dco_context_t *dco, struct nl_msg *nl_msg, ovpn_nl_cb cb, + void *cb_arg, const char *prefix); + +struct sockaddr * +mapped_v4_to_v6(struct sockaddr *sock, struct gc_arena *gc); + +int ovpn_nl_cb_finish(struct nl_msg (*msg) __attribute__ ((unused)), void *arg); + +/* This function is used as error callback on the netlink socket. + * When something goes wrong and the kernel returns an error, this function is + * invoked. + * + * We pass the error code to the user by means of a variable pointed by *arg + * (supplied by the user when setting this callback) and we parse the kernel + * reply to see if it contains a human-readable error. If found, it is printed. + */ +int ovpn_nl_cb_error(struct sockaddr_nl (*nla) __attribute__ ((unused)), + struct nlmsgerr *err, void *arg); + +void ovpn_dco_init_netlink(dco_context_t *dco); + +bool ovpn_dco_init(int mode, dco_context_t *dco); + +void ovpn_dco_uninit_netlink(dco_context_t *dco); + +void ovpn_dco_register(dco_context_t *dco); + +/* This function parses the reply provided by the kernel to the CTRL_CMD_GETFAMILY + * message. We parse the reply and we retrieve the multicast group ID associated + * with the "ovpn-dco" netlink family. + * + * The ID is later used to subscribe to the multicast group and be notified + * about any multicast message sent by the ovpn-dco kernel module. + */ +int mcast_family_handler(struct nl_msg *msg, void *arg); + +/** + * Lookup the multicast id for OpenVPN. This method and its help method currently + * hardcode the lookup to OVPN_NL_NAME and OVPN_NL_MULTICAST_GROUP_PEERS but + * extended in the future if we need to lookup more than one mcast id. + */ +int ovpn_get_mcast_id(dco_context_t *dco); + +bool dco_available(int msglevel, dco_context_t *dco); + +const char *dco_version_string(struct gc_arena *gc); + +void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg); + +const char *dco_get_supported_ciphers(); + #endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */ #endif /* ifndef DCO_LINUX_H */ diff --git a/src/openvpn/dco_linux_v2.c b/src/openvpn/dco_linux_v2.c new file mode 100644 index 0000000..6fc47fa --- /dev/null +++ b/src/openvpn/dco_linux_v2.c @@ -0,0 +1,636 @@ +/* + * Interface to linux dco networking code + * + * Copyright (C) 2020-2024 Antonio Quartulli + * Copyright (C) 2020-2024 Arne Schwabe + * Copyright (C) 2020-2024 Gianmarco De Gregori + * Copyright (C) 2020-2024 OpenVPN Inc + * + * 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" +#endif + +#if defined(ENABLE_DCO) && defined(TARGET_LINUX) + +#include "syshead.h" + +#include "dco_linux.h" +#include "errlevel.h" +#include "buffer.h" +#include "networking.h" +#include "openvpn.h" + +#include "socket.h" +#include "tun.h" +#include "ssl.h" +#include "fdmisc.h" +#include "multi.h" +#include "ssl_verify.h" + +#include "ovpn_dco_linux.h" + +#include +#include +#include +#include +#include + +int +open_tun_dco_v2(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev) +{ + msg(D_DCO_DEBUG, "%s: %s", __func__, dev); + ASSERT(tt->type == DEV_TYPE_TUN); + + int ret = net_iface_new(ctx, dev, "ovpn-dco", &tt->dco); + if (ret < 0) + { + msg(D_DCO_DEBUG, "Cannot create DCO interface %s: %d", dev, ret); + return ret; + } + + tt->dco.ifindex = if_nametoindex(dev); + if (!tt->dco.ifindex) + { + msg(M_FATAL, "DCO: cannot retrieve ifindex for interface %s", dev); + } + + tt->dco.dco_message_peer_id = -1; + + ovpn_dco_register(&tt->dco); + + return 0; +} + +static struct nl_msg * +ovpn_dco_v2_nlmsg_create(dco_context_t *dco, int cmd) +{ + enum ovpn_nl_commands enum_cmd = (enum ovpn_nl_commands)cmd; + struct nl_msg *nl_msg = nlmsg_alloc(); + if (!nl_msg) + { + msg(M_ERR, "cannot allocate netlink message"); + return NULL; + } + + genlmsg_put(nl_msg, 0, 0, dco->ovpn_dco_id, 0, 0, enum_cmd, 0); + NLA_PUT_U32(nl_msg, OVPN_ATTR_IFINDEX, dco->ifindex); + + return nl_msg; +nla_put_failure: + nlmsg_free(nl_msg); + msg(M_INFO, "cannot put into netlink message"); + return NULL; +} + +int +dco_v2_new_peer(dco_context_t *dco, unsigned int peerid, int sd, + struct sockaddr *localaddr, struct sockaddr *remoteaddr, + struct in_addr *remote_in4, struct in6_addr *remote_in6) +{ + struct gc_arena gc = gc_new(); + const char *remotestr = "[undefined]"; + if (remoteaddr) + { + remotestr = print_sockaddr(remoteaddr, &gc); + } + msg(D_DCO_DEBUG, "%s: peer-id %d, fd %d, remote addr: %s", __func__, + peerid, sd, remotestr); + + struct nl_msg *nl_msg = ovpn_dco_v2_nlmsg_create(dco, OVPN_CMD_NEW_PEER); + struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_NEW_PEER); + int ret = -EMSGSIZE; + + NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_PEER_ID, peerid); + NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_SOCKET, sd); + + /* Set the remote endpoint if defined (for UDP) */ + if (remoteaddr) + { + remoteaddr = mapped_v4_to_v6(remoteaddr, &gc); + int alen = af_addr_size(remoteaddr->sa_family); + + NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE, alen, remoteaddr); + } + + if (localaddr) + { + localaddr = mapped_v4_to_v6(localaddr, &gc); + if (localaddr->sa_family == AF_INET) + { + NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_LOCAL_IP, sizeof(struct in_addr), + &((struct sockaddr_in *)localaddr)->sin_addr); + } + else if (localaddr->sa_family == AF_INET6) + { + NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_LOCAL_IP, sizeof(struct in6_addr), + &((struct sockaddr_in6 *)localaddr)->sin6_addr); + } + } + + /* Set the primary VPN IP addresses of the peer */ + if (remote_in4) + { + NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_IPV4, remote_in4->s_addr); + } + if (remote_in6) + { + NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_IPV6, sizeof(struct in6_addr), + remote_in6); + } + nla_nest_end(nl_msg, attr); + + ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + +nla_put_failure: + nlmsg_free(nl_msg); + gc_free(&gc); + return ret; +} + +void +close_tun_dco_v2(struct tuntap *tt, openvpn_net_ctx_t *ctx) +{ + msg(D_DCO_DEBUG, __func__); + + net_iface_del(ctx, tt->actual_name); + ovpn_dco_uninit_netlink(&tt->dco); +} + +int +dco_v2_swap_keys(dco_context_t *dco, unsigned int peerid) +{ + msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid); + + struct nl_msg *nl_msg = ovpn_dco_v2_nlmsg_create(dco, OVPN_CMD_SWAP_KEYS); + if (!nl_msg) + { + return -ENOMEM; + } + + struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_SWAP_KEYS); + int ret = -EMSGSIZE; + NLA_PUT_U32(nl_msg, OVPN_SWAP_KEYS_ATTR_PEER_ID, peerid); + nla_nest_end(nl_msg, attr); + + ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + +nla_put_failure: + nlmsg_free(nl_msg); + return ret; +} + + +int +dco_v2_del_peer(dco_context_t *dco, unsigned int peerid) +{ + msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid); + + struct nl_msg *nl_msg = ovpn_dco_v2_nlmsg_create(dco, OVPN_CMD_DEL_PEER); + if (!nl_msg) + { + return -ENOMEM; + } + + struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_DEL_PEER); + int ret = -EMSGSIZE; + NLA_PUT_U32(nl_msg, OVPN_DEL_PEER_ATTR_PEER_ID, peerid); + nla_nest_end(nl_msg, attr); + + ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + +nla_put_failure: + nlmsg_free(nl_msg); + return ret; +} + + +int +dco_v2_del_key(dco_context_t *dco, unsigned int peerid, + dco_key_slot_t slot) +{ + msg(D_DCO_DEBUG, "%s: peer-id %d, slot %d", __func__, peerid, slot); + + struct nl_msg *nl_msg = ovpn_dco_v2_nlmsg_create(dco, OVPN_CMD_DEL_KEY); + if (!nl_msg) + { + return -ENOMEM; + } + + struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_DEL_KEY); + int ret = -EMSGSIZE; + NLA_PUT_U32(nl_msg, OVPN_DEL_KEY_ATTR_PEER_ID, peerid); + NLA_PUT_U8(nl_msg, OVPN_DEL_KEY_ATTR_KEY_SLOT, slot); + nla_nest_end(nl_msg, attr); + + ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + +nla_put_failure: + nlmsg_free(nl_msg); + return ret; +} + +int +dco_v2_new_key(dco_context_t *dco, unsigned int peerid, int keyid, + dco_key_slot_t slot, + const uint8_t *encrypt_key, const uint8_t *encrypt_iv, + const uint8_t *decrypt_key, const uint8_t *decrypt_iv, + const char *ciphername) +{ + msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s", + __func__, slot, keyid, peerid, ciphername); + + const size_t key_len = cipher_kt_key_size(ciphername); + const int nonce_tail_len = 8; + + struct nl_msg *nl_msg = ovpn_dco_v2_nlmsg_create(dco, OVPN_CMD_NEW_KEY); + if (!nl_msg) + { + return -ENOMEM; + } + + dco_cipher_t dco_cipher = dco_get_cipher(ciphername); + + int ret = -EMSGSIZE; + struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_NEW_KEY); + NLA_PUT_U32(nl_msg, OVPN_NEW_KEY_ATTR_PEER_ID, peerid); + NLA_PUT_U8(nl_msg, OVPN_NEW_KEY_ATTR_KEY_SLOT, slot); + NLA_PUT_U8(nl_msg, OVPN_NEW_KEY_ATTR_KEY_ID, keyid); + NLA_PUT_U16(nl_msg, OVPN_NEW_KEY_ATTR_CIPHER_ALG, dco_cipher); + + struct nlattr *key_enc = nla_nest_start(nl_msg, + OVPN_NEW_KEY_ATTR_ENCRYPT_KEY); + if (dco_cipher != OVPN_CIPHER_ALG_NONE) + { + NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, key_len, encrypt_key); + NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, nonce_tail_len, + encrypt_iv); + } + nla_nest_end(nl_msg, key_enc); + + struct nlattr *key_dec = nla_nest_start(nl_msg, + OVPN_NEW_KEY_ATTR_DECRYPT_KEY); + if (dco_cipher != OVPN_CIPHER_ALG_NONE) + { + NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, key_len, decrypt_key); + NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, nonce_tail_len, + decrypt_iv); + } + nla_nest_end(nl_msg, key_dec); + + nla_nest_end(nl_msg, attr); + + ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + +nla_put_failure: + nlmsg_free(nl_msg); + return ret; +} + +int +dco_v2_set_peer(dco_context_t *dco, unsigned int peerid, + int keepalive_interval, int keepalive_timeout, int mss) +{ + msg(D_DCO_DEBUG, "%s: peer-id %d, keepalive %d/%d, mss %d", __func__, + peerid, keepalive_interval, keepalive_timeout, mss); + + struct nl_msg *nl_msg = ovpn_dco_v2_nlmsg_create(dco, OVPN_CMD_SET_PEER); + if (!nl_msg) + { + return -ENOMEM; + } + + struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_SET_PEER); + int ret = -EMSGSIZE; + NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_PEER_ID, peerid); + NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL, + keepalive_interval); + NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT, + keepalive_timeout); + nla_nest_end(nl_msg, attr); + + ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + +nla_put_failure: + nlmsg_free(nl_msg); + return ret; +} + +/* This function parses any netlink message sent by ovpn-dco to userspace */ +static int +ovpn_v2_handle_msg(struct nl_msg *msg, void *arg) +{ + dco_context_t *dco = arg; + + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *attrs[OVPN_ATTR_MAX + 1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + + if (!genlmsg_valid_hdr(nlh, 0)) + { + msg(D_DCO, "ovpn-dco: invalid header"); + return NL_SKIP; + } + + if (nla_parse(attrs, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL)) + { + msg(D_DCO, "received bogus data from ovpn-dco"); + return NL_SKIP; + } + + /* we must know which interface this message is referring to in order to + * avoid mixing messages for other instances + */ + if (!attrs[OVPN_ATTR_IFINDEX]) + { + msg(D_DCO, "ovpn-dco: Received message without ifindex"); + return NL_SKIP; + } + + uint32_t ifindex = nla_get_u32(attrs[OVPN_ATTR_IFINDEX]); + if (ifindex != dco->ifindex) + { + msg(D_DCO_DEBUG, + "ovpn-dco: ignoring message (type=%d) for foreign ifindex %d", + gnlh->cmd, ifindex); + return NL_SKIP; + } + + /* based on the message type, we parse the subobject contained in the + * message, that stores the type-specific attributes. + * + * the "dco" object is then filled accordingly with the information + * retrieved from the message, so that the rest of the OpenVPN code can + * react as need be. + */ + switch (gnlh->cmd) + { + case OVPN_CMD_DEL_PEER: + { + if (!attrs[OVPN_ATTR_DEL_PEER]) + { + msg(D_DCO, "ovpn-dco: no attributes in OVPN_DEL_PEER message"); + return NL_SKIP; + } + + struct nlattr *dp_attrs[OVPN_DEL_PEER_ATTR_MAX + 1]; + if (nla_parse_nested(dp_attrs, OVPN_DEL_PEER_ATTR_MAX, + attrs[OVPN_ATTR_DEL_PEER], NULL)) + { + msg(D_DCO, "received bogus del peer packet data from ovpn-dco"); + return NL_SKIP; + } + + if (!dp_attrs[OVPN_DEL_PEER_ATTR_REASON]) + { + msg(D_DCO, "ovpn-dco: no reason in DEL_PEER message"); + return NL_SKIP; + } + if (!dp_attrs[OVPN_DEL_PEER_ATTR_PEER_ID]) + { + msg(D_DCO, "ovpn-dco: no peer-id in DEL_PEER message"); + return NL_SKIP; + } + int reason = nla_get_u8(dp_attrs[OVPN_DEL_PEER_ATTR_REASON]); + unsigned int peerid = nla_get_u32(dp_attrs[OVPN_DEL_PEER_ATTR_PEER_ID]); + + msg(D_DCO_DEBUG, "ovpn-dco: received CMD_DEL_PEER, ifindex: %d, peer-id %d, reason: %d", + ifindex, peerid, reason); + dco->dco_message_peer_id = peerid; + dco->dco_del_peer_reason = reason; + dco->dco_message_type = OVPN_CMD_DEL_PEER; + + break; + } + + default: + msg(D_DCO, "ovpn-dco: received unknown command: %d", gnlh->cmd); + dco->dco_message_type = 0; + return NL_SKIP; + } + + return NL_OK; +} + +static int +dco_v2_do_read(dco_context_t *dco) +{ + msg(D_DCO_DEBUG, __func__); + nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_v2_handle_msg, dco); + + return ovpn_nl_recvmsgs(dco, __func__); +} + +static void +dco_v2_update_peer_stat(struct context_2 *c2, struct nlattr *tb[], uint32_t id) +{ + if (tb[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES]) + { + c2->dco_read_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES]); + msg(D_DCO_DEBUG, "%s / dco_read_bytes: " counter_format, __func__, + c2->dco_read_bytes); + } + else + { + msg(M_WARN, "%s: no link RX bytes provided in reply for peer %u", + __func__, id); + } + + if (tb[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES]) + { + c2->dco_write_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES]); + msg(D_DCO_DEBUG, "%s / dco_write_bytes: " counter_format, __func__, + c2->dco_write_bytes); + } + else + { + msg(M_WARN, "%s: no link TX bytes provided in reply for peer %u", + __func__, id); + } + + if (tb[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES]) + { + c2->tun_read_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES]); + msg(D_DCO_DEBUG, "%s / tun_read_bytes: " counter_format, __func__, + c2->tun_read_bytes); + } + else + { + msg(M_WARN, "%s: no VPN RX bytes provided in reply for peer %u", + __func__, id); + } + + if (tb[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES]) + { + c2->tun_write_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES]); + msg(D_DCO_DEBUG, "%s / tun_write_bytes: " counter_format, __func__, + c2->tun_write_bytes); + } + else + { + msg(M_WARN, "%s: no VPN TX bytes provided in reply for peer %u", + __func__, id); + } +} + +int +dco_v2_parse_peer_multi(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[OVPN_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + msg(D_DCO_DEBUG, "%s: parsing message...", __func__); + + nla_parse(tb, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[OVPN_ATTR_GET_PEER]) + { + return NL_SKIP; + } + + struct nlattr *tb_peer[OVPN_GET_PEER_RESP_ATTR_MAX + 1]; + + nla_parse(tb_peer, OVPN_GET_PEER_RESP_ATTR_MAX, + nla_data(tb[OVPN_ATTR_GET_PEER]), + nla_len(tb[OVPN_ATTR_GET_PEER]), NULL); + + if (!tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]) + { + msg(M_WARN, "%s: no peer-id provided in reply", __func__); + return NL_SKIP; + } + + struct multi_context *m = arg; + uint32_t peer_id = nla_get_u32(tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]); + + if (peer_id >= m->max_clients || !m->instances[peer_id]) + { + msg(M_WARN, "%s: cannot store DCO stats for peer %u", __func__, + peer_id); + return NL_SKIP; + } + + dco_v2_update_peer_stat(&m->instances[peer_id]->context.c2, tb_peer, peer_id); + + return NL_OK; +} + +int +dco_v2_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m) +{ + msg(D_DCO_DEBUG, "%s", __func__); + + struct nl_msg *nl_msg = ovpn_dco_v2_nlmsg_create(dco, OVPN_CMD_GET_PEER); + + nlmsg_hdr(nl_msg)->nlmsg_flags |= NLM_F_DUMP; + + int ret = ovpn_nl_msg_send(dco, nl_msg, dco_v2_parse_peer_multi, m, __func__); + + nlmsg_free(nl_msg); + return ret; +} + +static int +dco_v2_parse_peer(struct nl_msg *msg, void *arg) +{ + struct context *c = arg; + struct nlattr *tb[OVPN_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + msg(D_DCO_DEBUG, "%s: parsing message...", __func__); + + nla_parse(tb, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[OVPN_ATTR_GET_PEER]) + { + msg(D_DCO_DEBUG, "%s: malformed reply", __func__); + return NL_SKIP; + } + + struct nlattr *tb_peer[OVPN_GET_PEER_RESP_ATTR_MAX + 1]; + + nla_parse(tb_peer, OVPN_GET_PEER_RESP_ATTR_MAX, + nla_data(tb[OVPN_ATTR_GET_PEER]), + nla_len(tb[OVPN_ATTR_GET_PEER]), NULL); + + if (!tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]) + { + msg(M_WARN, "%s: no peer-id provided in reply", __func__); + return NL_SKIP; + } + + uint32_t peer_id = nla_get_u32(tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]); + if (c->c2.tls_multi->dco_peer_id != peer_id) + { + return NL_SKIP; + } + + dco_v2_update_peer_stat(&c->c2, tb_peer, peer_id); + + return NL_OK; +} + +int +dco_v2_get_peer_stats(struct context *c) +{ + uint32_t peer_id = c->c2.tls_multi->dco_peer_id; + msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peer_id); + + if (!c->c1.tuntap) + { + return 0; + } + + dco_context_t *dco = &c->c1.tuntap->dco; + struct nl_msg *nl_msg = ovpn_dco_v2_nlmsg_create(dco, OVPN_CMD_GET_PEER); + struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_GET_PEER); + int ret = -EMSGSIZE; + + NLA_PUT_U32(nl_msg, OVPN_GET_PEER_ATTR_PEER_ID, peer_id); + nla_nest_end(nl_msg, attr); + + ret = ovpn_nl_msg_send(dco, nl_msg, dco_v2_parse_peer, c, __func__); + +nla_put_failure: + nlmsg_free(nl_msg); + return ret; +} + +const struct dco_ops dco_ops_v2 = { + .ovpn_dco_nlmsg_create = ovpn_dco_v2_nlmsg_create, + .dco_new_peer = &dco_v2_new_peer, + .open_tun_dco = open_tun_dco_v2, + .close_tun_dco = close_tun_dco_v2, + .dco_swap_keys = &dco_v2_swap_keys, + .dco_del_peer = &dco_v2_del_peer, + .dco_del_key = &dco_v2_del_key, + .dco_new_key = &dco_v2_new_key, + .dco_set_peer = &dco_v2_set_peer, + .ovpn_handle_msg = &ovpn_v2_handle_msg, + .dco_do_read = &dco_v2_do_read, + .dco_update_peer_stat = &dco_v2_update_peer_stat, + .dco_parse_peer_multi = &dco_v2_parse_peer_multi, + .dco_get_peer_stats_multi = &dco_v2_get_peer_stats_multi, + .dco_parse_peer = &dco_v2_parse_peer, + .dco_get_peer_stats = &dco_v2_get_peer_stats +}; + +#endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */ diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c index e3ada76..65be766 100644 --- a/src/openvpn/dco_win.c +++ b/src/openvpn/dco_win.c @@ -357,7 +357,7 @@ } bool -dco_available(int msglevel) +dco_available(int msglevel, dco_context_t *dco) { /* try to open device by symbolic name */ HANDLE h = CreateFile("\\\\.\\ovpn-dco", GENERIC_READ | GENERIC_WRITE, diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 01165b2..118c32a 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -1230,6 +1230,9 @@ process_incoming_dco(struct context *c) { #if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || defined(TARGET_FREEBSD)) + int cmd_del_peer = OVPN_CMD_DEL_PEER; + int cmd_swap_keys = OVPN_CMD_SWAP_KEYS; + dco_context_t *dco = &c->c1.tuntap->dco; dco_do_read(dco); @@ -1245,31 +1248,30 @@ return; } - switch (dco->dco_message_type) + if (dco->dco_message_type == cmd_del_peer) { - case OVPN_CMD_DEL_PEER: - if (dco->dco_del_peer_reason == OVPN_DEL_PEER_REASON_EXPIRED) - { - msg(D_DCO_DEBUG, "%s: received peer expired notification of for peer-id " - "%d", __func__, dco->dco_message_peer_id); - trigger_ping_timeout_signal(c); - return; - } - break; - - case OVPN_CMD_SWAP_KEYS: - msg(D_DCO_DEBUG, "%s: received key rotation notification for peer-id %d", - __func__, dco->dco_message_peer_id); - tls_session_soft_reset(c->c2.tls_multi); - break; - - default: - msg(D_DCO_DEBUG, "%s: received message of type %u - ignoring", __func__, - dco->dco_message_type); + if (dco->dco_del_peer_reason == OVPN_DEL_PEER_REASON_EXPIRED) + { + msg(D_DCO_DEBUG, "%s: received peer expired notification of for peer-id " + "%d", __func__, dco->dco_message_peer_id); + trigger_ping_timeout_signal(c); return; + } + } + else if (dco->dco_message_type == cmd_swap_keys) + { + msg(D_DCO_DEBUG, "%s: received key rotation notification for peer-id %d", + __func__, dco->dco_message_peer_id); + tls_session_soft_reset(c->c2.tls_multi); + } + else + { + msg(D_DCO_DEBUG, "%s: received message of type %u - ignoring", __func__, + dco->dco_message_type); + return; } -#endif /* if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || defined(TARGET_FREEBSD)) */ +#endif /* #if defined(ENABLE_DCO) && defined(TARGET_FREEBSD) */ } /*