From patchwork Fri Oct 25 09:14:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 838580 Received: from mail-wm1-f43.google.com (mail-wm1-f43.google.com [209.85.128.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 407431D9A46 for ; Fri, 25 Oct 2024 09:15:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729847709; cv=none; b=dK5yYKaNeNrLaskwZP3sg5/ooB6AJbkgGyxpt0TA2hkqME5Dgjz7STiROkX4McVYI47+3pNKeVD2ESX44iop/Yq1zT5dmPJ6jNXZbPiTFBJ411hMWeQwsEFuOa0J8h/59HF+2kRfpVL84SXfsWt4DRAHzSpYrIVFewX5bK58/nw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729847709; c=relaxed/simple; bh=ggF5K+58gLgiad5OUSjEn5k2AotXeWRMNMLXDkZhNv0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IusDAwBJTUtLDwKIRx0U8X6fOV+4U6x3jdDX6ZMF6RQtOuXXbeGs6im6PVoDPxOKYFhndggFuJf3jgDHQrcVdjIT+i1PdA67ela+H3NhmA/ZRs6DocXkmYkRl1K7dHBzKYsWikSswT1LHAdpMvP563+oDeIM64VP9vSCbyL2kwQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=openvpn.net; spf=pass smtp.mailfrom=openvpn.com; dkim=pass (2048-bit key) header.d=openvpn.net header.i=@openvpn.net header.b=L5IU6gIU; arc=none smtp.client-ip=209.85.128.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=openvpn.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=openvpn.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=openvpn.net header.i=@openvpn.net header.b="L5IU6gIU" Received: by mail-wm1-f43.google.com with SMTP id 5b1f17b1804b1-43169902057so18114675e9.0 for ; Fri, 25 Oct 2024 02:15:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=openvpn.net; s=google; t=1729847702; x=1730452502; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=ud6K5GOoq3b4lX4KydaG6wKHFrptq5dDMvYiS7l/dAc=; b=L5IU6gIUSUjxYplf8HCftXhB2I8WssNWkCthtEMbLRWSf0eiS93rsrEbONAUJBlC9q jayXUUvScD/haFGJGHksCNyV16II9pyuwrLeCcrhLpvda9f33GFDJjw6y8+7+avDZHR3 SgK74ei1iRSMF/S1Vbjljea/2FY6o9FwZA4cDxZeiNxlkwbcGgWaTEJexnAaRcWMxBLf aShNCNZu8umx0uM+O00GwbZP/6PO/A8QQmHsR2Nu1SOJABBB/4Elad3ODk9u0jxWWPG+ z33BMpeaA5MCDUoWTH9K2uRJfAtDB2Fh6Z0rOB8KIKimp5A7Jm3nsOxA4BUI22a9piXo fJlg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729847702; x=1730452502; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ud6K5GOoq3b4lX4KydaG6wKHFrptq5dDMvYiS7l/dAc=; b=Eym/jzGiIBZ7hM8QtaoNDuZC8pDa+mZyfS9HH/QVWH8yHkIIuZ4nVxjffw2Zsp2mEE yGPxp+j0Qia/MRcxvA9WIwYsMoh7UfLBMXc2W/yh1U4pWApsg8YJmrMmL7eeF5rXBrq7 S8da/2r2jGm77+l8BWgjnAVvfPiKkKqrB0u5++3ic2WePvdL7i6zwxPOaZNR5qQrDdyO Ndbm3twjgLFyTYX8JqMkUVCyjOv3ULDQ+TBk8LcaXMqvyaeyrXWpAp7I7iWVsamjloNR Bs4r1E9r5XIWga/mSQeJxpN2xgjy3WW4azrM4VfdSwi6tLaNk6pUyp8FW/Ufx8PGgQVC XdbQ== X-Forwarded-Encrypted: i=1; AJvYcCXWklRyeegDWLnsrlz/2+qUeDvfLVGGp7XmRNqkGYvmqzMcuBldH/bLC1/z1Ob6lG7xWrmy6mRS9h/yrhmqHHc=@vger.kernel.org X-Gm-Message-State: AOJu0YyENy06CYkX0EfNjN39rKSpBzWjeL/lWLNSXNtA28Y4Rj8sYgAy PWsUQvqblIjmuxSiDmDZPvpH5xV4iltb2CBcAFu7X+xr8Q48pupwdHDUeyl4PyQ= X-Google-Smtp-Source: AGHT+IGmQh2oR5/PzzxC9rCiIMasa4gw6zyeBEGEqeWWEMiA6kqpRDb7/LMBQ77BYdkYlYedD58t4A== X-Received: by 2002:a05:600c:1c9c:b0:431:5187:28dd with SMTP id 5b1f17b1804b1-4318418aab2mr76140315e9.28.1729847702428; Fri, 25 Oct 2024 02:15:02 -0700 (PDT) Received: from serenity.mandelbit.com ([2001:67c:2fbc:1:676b:7d84:55a4:bea5]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4318b55f484sm41981485e9.13.2024.10.25.02.15.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 25 Oct 2024 02:15:01 -0700 (PDT) From: Antonio Quartulli Date: Fri, 25 Oct 2024 11:14:06 +0200 Subject: [PATCH net-next v10 07/23] ovpn: introduce the ovpn_socket object Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241025-b4-ovpn-v10-7-b87530777be7@openvpn.net> References: <20241025-b4-ovpn-v10-0-b87530777be7@openvpn.net> In-Reply-To: <20241025-b4-ovpn-v10-0-b87530777be7@openvpn.net> To: Eric Dumazet , Jakub Kicinski , Paolo Abeni , Donald Hunter , Antonio Quartulli , Shuah Khan , donald.hunter@gmail.com, sd@queasysnail.net, ryazanov.s.a@gmail.com, Andrew Lunn Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=9019; i=antonio@openvpn.net; h=from:subject:message-id; bh=ggF5K+58gLgiad5OUSjEn5k2AotXeWRMNMLXDkZhNv0=; b=owEBbQGS/pANAwAIAQtw5TqgONWHAcsmYgBnG2GZOV57xCIWmVQ5yWp1kbwDsCi8Inf0Oq7Ez LOLQHfoXKCJATMEAAEIAB0WIQSZq9xs+NQS5N5fwPwLcOU6oDjVhwUCZxthmQAKCRALcOU6oDjV h265B/9IrHpYynz6CjtJ3ZMFcEcVgAeSHWdkZgphBlid1dTx4PlP9ZTGLNrX65BGDHXfodo9nW9 ENuayP3iRbtuQGeayHvo02sfsP+8P38P9Pe0Qg7ZYA2gMKDI60ea689BtxkaN569wM1u8mUosuc oMhQsZJtOrp6idOxlpM2JQQFXWH15alE9AN9progRwaMvXnb+1MoKAgBfffoMoNh2I277xo8La0 SoS7jByZ0OE3ySTvGZdIes2iVTvdnDfK0nE3iL7YnS20N1cUm4NHye7eAixxu4RXm6d3NHliBGd Z7U942E2OMAZm5INiuxPNuc5mrpinW0wuPGh03lXBZCurFvb X-Developer-Key: i=antonio@openvpn.net; a=openpgp; fpr=CABDA1282017C267219885C748F0CCB68F59D14C This specific structure is used in the ovpn kernel module to wrap and carry around a standard kernel socket. ovpn takes ownership of passed sockets and therefore an ovpn specific objects is attached to them for status tracking purposes. Initially only UDP support is introduced. TCP will come in a later patch. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/Makefile | 2 + drivers/net/ovpn/socket.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/ovpn/socket.h | 48 +++++++++++++++++++ drivers/net/ovpn/udp.c | 72 ++++++++++++++++++++++++++++ drivers/net/ovpn/udp.h | 17 +++++++ 5 files changed, 259 insertions(+) diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index ce13499b3e1775a7f2a9ce16c6cb0aa088f93685..56bddc9bef83e0befde6af3c3565bb91731d7b22 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -13,3 +13,5 @@ ovpn-y += io.o ovpn-y += netlink.o ovpn-y += netlink-gen.o ovpn-y += peer.o +ovpn-y += socket.o +ovpn-y += udp.o diff --git a/drivers/net/ovpn/socket.c b/drivers/net/ovpn/socket.c new file mode 100644 index 0000000000000000000000000000000000000000..090a3232ab0ec19702110f1a90f45c7f10889f6f --- /dev/null +++ b/drivers/net/ovpn/socket.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OpenVPN data channel offload + * + * Copyright (C) 2020-2024 OpenVPN, Inc. + * + * Author: James Yonan + * Antonio Quartulli + */ + +#include +#include + +#include "ovpnstruct.h" +#include "main.h" +#include "io.h" +#include "peer.h" +#include "socket.h" +#include "udp.h" + +static void ovpn_socket_detach(struct socket *sock) +{ + if (!sock) + return; + + sockfd_put(sock); +} + +/** + * ovpn_socket_release_kref - kref_put callback + * @kref: the kref object + */ +void ovpn_socket_release_kref(struct kref *kref) +{ + struct ovpn_socket *sock = container_of(kref, struct ovpn_socket, + refcount); + + ovpn_socket_detach(sock->sock); + kfree_rcu(sock, rcu); +} + +static bool ovpn_socket_hold(struct ovpn_socket *sock) +{ + return kref_get_unless_zero(&sock->refcount); +} + +static struct ovpn_socket *ovpn_socket_get(struct socket *sock) +{ + struct ovpn_socket *ovpn_sock; + + rcu_read_lock(); + ovpn_sock = rcu_dereference_sk_user_data(sock->sk); + if (!ovpn_socket_hold(ovpn_sock)) { + pr_warn("%s: found ovpn_socket with ref = 0\n", __func__); + ovpn_sock = NULL; + } + rcu_read_unlock(); + + return ovpn_sock; +} + +static int ovpn_socket_attach(struct socket *sock, struct ovpn_peer *peer) +{ + int ret = -EOPNOTSUPP; + + if (!sock || !peer) + return -EINVAL; + + if (sock->sk->sk_protocol == IPPROTO_UDP) + ret = ovpn_udp_socket_attach(sock, peer->ovpn); + + return ret; +} + +/** + * ovpn_socket_new - create a new socket and initialize it + * @sock: the kernel socket to embed + * @peer: the peer reachable via this socket + * + * Return: an openvpn socket on success or a negative error code otherwise + */ +struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) +{ + struct ovpn_socket *ovpn_sock; + int ret; + + ret = ovpn_socket_attach(sock, peer); + if (ret < 0 && ret != -EALREADY) + return ERR_PTR(ret); + + /* if this socket is already owned by this interface, just increase the + * refcounter and use it as expected. + * + * Since UDP sockets can be used to talk to multiple remote endpoints, + * openvpn normally instantiates only one socket and shares it among all + * its peers. For this reason, when we find out that a socket is already + * used for some other peer in *this* instance, we can happily increase + * its refcounter and use it normally. + */ + if (ret == -EALREADY) { + /* caller is expected to increase the sock refcounter before + * passing it to this function. For this reason we drop it if + * not needed, like when this socket is already owned. + */ + ovpn_sock = ovpn_socket_get(sock); + sockfd_put(sock); + return ovpn_sock; + } + + ovpn_sock = kzalloc(sizeof(*ovpn_sock), GFP_KERNEL); + if (!ovpn_sock) + return ERR_PTR(-ENOMEM); + + ovpn_sock->ovpn = peer->ovpn; + ovpn_sock->sock = sock; + kref_init(&ovpn_sock->refcount); + + rcu_assign_sk_user_data(sock->sk, ovpn_sock); + + return ovpn_sock; +} diff --git a/drivers/net/ovpn/socket.h b/drivers/net/ovpn/socket.h new file mode 100644 index 0000000000000000000000000000000000000000..5ad9c5073b085482da95ee8ebf40acf20bf2e4b3 --- /dev/null +++ b/drivers/net/ovpn/socket.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* OpenVPN data channel offload + * + * Copyright (C) 2020-2024 OpenVPN, Inc. + * + * Author: James Yonan + * Antonio Quartulli + */ + +#ifndef _NET_OVPN_SOCK_H_ +#define _NET_OVPN_SOCK_H_ + +#include +#include +#include + +struct ovpn_struct; +struct ovpn_peer; + +/** + * struct ovpn_socket - a kernel socket referenced in the ovpn code + * @ovpn: ovpn instance owning this socket (UDP only) + * @sock: the low level sock object + * @refcount: amount of contexts currently referencing this object + * @rcu: member used to schedule RCU destructor callback + */ +struct ovpn_socket { + struct ovpn_struct *ovpn; + struct socket *sock; + struct kref refcount; + struct rcu_head rcu; +}; + +void ovpn_socket_release_kref(struct kref *kref); + +/** + * ovpn_socket_put - decrease reference counter + * @sock: the socket whose reference counter should be decreased + */ +static inline void ovpn_socket_put(struct ovpn_socket *sock) +{ + kref_put(&sock->refcount, ovpn_socket_release_kref); +} + +struct ovpn_socket *ovpn_socket_new(struct socket *sock, + struct ovpn_peer *peer); + +#endif /* _NET_OVPN_SOCK_H_ */ diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c new file mode 100644 index 0000000000000000000000000000000000000000..c10474d252e19a0626d17a6f5dd328a5e5811551 --- /dev/null +++ b/drivers/net/ovpn/udp.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OpenVPN data channel offload + * + * Copyright (C) 2019-2024 OpenVPN, Inc. + * + * Author: Antonio Quartulli + */ + +#include +#include +#include + +#include "ovpnstruct.h" +#include "main.h" +#include "socket.h" +#include "udp.h" + +/** + * ovpn_udp_socket_attach - set udp-tunnel CBs on socket and link it to ovpn + * @sock: socket to configure + * @ovpn: the openvp instance to link + * + * After invoking this function, the sock will be controlled by ovpn so that + * any incoming packet may be processed by ovpn first. + * + * Return: 0 on success or a negative error code otherwise + */ +int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn) +{ + struct ovpn_socket *old_data; + int ret = 0; + + /* sanity check */ + if (sock->sk->sk_protocol != IPPROTO_UDP) { + DEBUG_NET_WARN_ON_ONCE(1); + return -EINVAL; + } + + /* make sure no pre-existing encapsulation handler exists */ + rcu_read_lock(); + old_data = rcu_dereference_sk_user_data(sock->sk); + if (!old_data) { + /* socket is currently unused - we can take it */ + rcu_read_unlock(); + return 0; + } + + /* socket is in use. We need to understand if it's owned by this ovpn + * instance or by something else. + * In the former case, we can increase the refcounter and happily + * use it, because the same UDP socket is expected to be shared among + * different peers. + * + * Unlikely TCP, a single UDP socket can be used to talk to many remote + * hosts and therefore openvpn instantiates one only for all its peers + */ + if ((READ_ONCE(udp_sk(sock->sk)->encap_type) == UDP_ENCAP_OVPNINUDP) && + old_data->ovpn == ovpn) { + netdev_dbg(ovpn->dev, + "%s: provided socket already owned by this interface\n", + __func__); + ret = -EALREADY; + } else { + netdev_err(ovpn->dev, + "%s: provided socket already taken by other user\n", + __func__); + ret = -EBUSY; + } + rcu_read_unlock(); + + return ret; +} diff --git a/drivers/net/ovpn/udp.h b/drivers/net/ovpn/udp.h new file mode 100644 index 0000000000000000000000000000000000000000..f2507f8f2c71ea9d5e5ac5446801e2d56f86700f --- /dev/null +++ b/drivers/net/ovpn/udp.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* OpenVPN data channel offload + * + * Copyright (C) 2019-2024 OpenVPN, Inc. + * + * Author: Antonio Quartulli + */ + +#ifndef _NET_OVPN_UDP_H_ +#define _NET_OVPN_UDP_H_ + +struct ovpn_struct; +struct socket; + +int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn); + +#endif /* _NET_OVPN_UDP_H_ */