diff mbox series

[net-next,v2,04/22] ovpn: add basic interface creation/destruction/management routines

Message ID 20240304150914.11444-5-antonio@openvpn.net (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series Introducing OpenVPN Data Channel Offload | expand

Checks

Context Check Description
netdev/series_format fail Series longer than 15 patches (and no cover letter)
netdev/tree_selection success Clearly marked for net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 947 this patch: 946
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 1 maintainers not CCed: openvpn-devel@lists.sourceforge.net
netdev/build_clang success Errors and warnings before: 964 this patch: 962
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 963 this patch: 962
netdev/checkpatch warning WARNING: Missing commit description - Add an appropriate one WARNING: line length of 81 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Antonio Quartulli March 4, 2024, 3:08 p.m. UTC
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
 drivers/net/ovpn/io.c         |  26 +++++++++
 drivers/net/ovpn/io.h         |   1 +
 drivers/net/ovpn/main.c       | 107 ++++++++++++++++++++++++++++++++++
 drivers/net/ovpn/ovpnstruct.h |  16 +++++
 4 files changed, 150 insertions(+)

Comments

Andrew Lunn March 4, 2024, 9:33 p.m. UTC | #1
> +int ovpn_struct_init(struct net_device *dev)
> +{
> +	struct ovpn_struct *ovpn = netdev_priv(dev);
> +	int err;
> +
> +	memset(ovpn, 0, sizeof(*ovpn));

Probably not required. When a netdev is created, it should of zeroed
the priv.

> +int ovpn_iface_create(const char *name, enum ovpn_mode mode, struct net *net)
> +{
> +	struct net_device *dev;
> +	struct ovpn_struct *ovpn;
> +	int ret;
> +
> +	dev = alloc_netdev(sizeof(struct ovpn_struct), name, NET_NAME_USER, ovpn_setup);
> +
> +	dev_net_set(dev, net);
> +
> +	ret = ovpn_struct_init(dev);
> +	if (ret < 0)
> +		goto err;
> +
> +	ovpn = netdev_priv(dev);
> +	ovpn->mode = mode;
> +
> +	rtnl_lock();
> +
> +	ret = register_netdevice(dev);
> +	if (ret < 0) {
> +		netdev_dbg(dev, "cannot register interface %s: %d\n", dev->name, ret);
> +		rtnl_unlock();
> +		goto err;
> +	}
> +	rtnl_unlock();
> +
> +	return ret;
> +
> +err:
> +	free_netdev(dev);
> +	return ret;
> +}
> +
> +void ovpn_iface_destruct(struct ovpn_struct *ovpn, bool unregister_netdev)
> +{
> +	ASSERT_RTNL();
> +
> +	netif_carrier_off(ovpn->dev);

You often see virtual devices turn their carrier off in there
probe/create function, because it is unclear what state it is in after
register_netdevice().

	Andrew
Antonio Quartulli March 5, 2024, 3:51 p.m. UTC | #2
On 04/03/2024 22:33, Andrew Lunn wrote:
>> +int ovpn_struct_init(struct net_device *dev)
>> +{
>> +	struct ovpn_struct *ovpn = netdev_priv(dev);
>> +	int err;
>> +
>> +	memset(ovpn, 0, sizeof(*ovpn));
> 
> Probably not required. When a netdev is created, it should of zeroed
> the priv.

ACK. There is a kvzalloc() involved.

> 
>> +int ovpn_iface_create(const char *name, enum ovpn_mode mode, struct net *net)
>> +{
>> +	struct net_device *dev;
>> +	struct ovpn_struct *ovpn;
>> +	int ret;
>> +
>> +	dev = alloc_netdev(sizeof(struct ovpn_struct), name, NET_NAME_USER, ovpn_setup);
>> +
>> +	dev_net_set(dev, net);
>> +
>> +	ret = ovpn_struct_init(dev);
>> +	if (ret < 0)
>> +		goto err;
>> +
>> +	ovpn = netdev_priv(dev);
>> +	ovpn->mode = mode;
>> +
>> +	rtnl_lock();
>> +
>> +	ret = register_netdevice(dev);
>> +	if (ret < 0) {
>> +		netdev_dbg(dev, "cannot register interface %s: %d\n", dev->name, ret);
>> +		rtnl_unlock();
>> +		goto err;
>> +	}
>> +	rtnl_unlock();
>> +
>> +	return ret;
>> +
>> +err:
>> +	free_netdev(dev);
>> +	return ret;
>> +}
>> +
>> +void ovpn_iface_destruct(struct ovpn_struct *ovpn, bool unregister_netdev)
>> +{
>> +	ASSERT_RTNL();
>> +
>> +	netif_carrier_off(ovpn->dev);
> 
> You often see virtual devices turn their carrier off in there
> probe/create function, because it is unclear what state it is in after
> register_netdevice().

Are you suggesting to turn it off both here and in the create function?
Or should I remove the invocation above?

Regards,
Andrew Lunn March 5, 2024, 4:27 p.m. UTC | #3
> > > +void ovpn_iface_destruct(struct ovpn_struct *ovpn, bool unregister_netdev)
> > > +{
> > > +	ASSERT_RTNL();
> > > +
> > > +	netif_carrier_off(ovpn->dev);
> > 
> > You often see virtual devices turn their carrier off in there
> > probe/create function, because it is unclear what state it is in after
> > register_netdevice().
> 
> Are you suggesting to turn it off both here and in the create function?
> Or should I remove the invocation above?

I noticed it in the _destruct function and went back to look at
create. You probably want it in both, unless as part of destruct, you
first disconnect all peers, which should set the carrier to off when
the last peer disconnects?

    Andrew
Jakub Kicinski March 5, 2024, 7:40 p.m. UTC | #4
On Mon,  4 Mar 2024 16:08:55 +0100 Antonio Quartulli wrote:
> +	dev = alloc_netdev(sizeof(struct ovpn_struct), name, NET_NAME_USER, ovpn_setup);
> +
> +	dev_net_set(dev, net);

the dev allocation never fails? Or the error is handles somewhere else?
Antonio Quartulli March 6, 2024, 2:49 p.m. UTC | #5
On 05/03/2024 17:27, Andrew Lunn wrote:
>>>> +void ovpn_iface_destruct(struct ovpn_struct *ovpn, bool unregister_netdev)
>>>> +{
>>>> +	ASSERT_RTNL();
>>>> +
>>>> +	netif_carrier_off(ovpn->dev);
>>>
>>> You often see virtual devices turn their carrier off in there
>>> probe/create function, because it is unclear what state it is in after
>>> register_netdevice().
>>
>> Are you suggesting to turn it off both here and in the create function?
>> Or should I remove the invocation above?
> 
> I noticed it in the _destruct function and went back to look at
> create. You probably want it in both, unless as part of destruct, you
> first disconnect all peers, which should set the carrier to off when
> the last peer disconnects?

I think keeping the carrier on while no peer is connected is better for 
OpenVPN.

I will then turn the carrier off in the create function as well. Thanks!
Antonio Quartulli March 6, 2024, 2:59 p.m. UTC | #6
On 05/03/2024 20:40, Jakub Kicinski wrote:
> On Mon,  4 Mar 2024 16:08:55 +0100 Antonio Quartulli wrote:
>> +	dev = alloc_netdev(sizeof(struct ovpn_struct), name, NET_NAME_USER, ovpn_setup);
>> +
>> +	dev_net_set(dev, net);
> 
> the dev allocation never fails? Or the error is handles somewhere else?

It definitely can fail and dev_net_set will crash.
My bad. Thanks for reporting this.


Regards,
Andrew Lunn March 6, 2024, 7:31 p.m. UTC | #7
On Wed, Mar 06, 2024 at 03:49:50PM +0100, Antonio Quartulli wrote:
> On 05/03/2024 17:27, Andrew Lunn wrote:
> > > > > +void ovpn_iface_destruct(struct ovpn_struct *ovpn, bool unregister_netdev)
> > > > > +{
> > > > > +	ASSERT_RTNL();
> > > > > +
> > > > > +	netif_carrier_off(ovpn->dev);
> > > > 
> > > > You often see virtual devices turn their carrier off in there
> > > > probe/create function, because it is unclear what state it is in after
> > > > register_netdevice().
> > > 
> > > Are you suggesting to turn it off both here and in the create function?
> > > Or should I remove the invocation above?
> > 
> > I noticed it in the _destruct function and went back to look at
> > create. You probably want it in both, unless as part of destruct, you
> > first disconnect all peers, which should set the carrier to off when
> > the last peer disconnects?
> 
> I think keeping the carrier on while no peer is connected is better for
> OpenVPN.

I then have to wounder what carrier actually means?

Some routing protocols will kick off determining routes when the
carrier goes down. Can you put team/bonding on top of openvpn? If the
peer has gone, you want team to fall over to the active backup?

     Andrew
Antonio Quartulli March 8, 2024, 12:08 a.m. UTC | #8
On 06/03/2024 20:31, Andrew Lunn wrote:
> On Wed, Mar 06, 2024 at 03:49:50PM +0100, Antonio Quartulli wrote:
>> On 05/03/2024 17:27, Andrew Lunn wrote:
>>>>>> +void ovpn_iface_destruct(struct ovpn_struct *ovpn, bool unregister_netdev)
>>>>>> +{
>>>>>> +	ASSERT_RTNL();
>>>>>> +
>>>>>> +	netif_carrier_off(ovpn->dev);
>>>>>
>>>>> You often see virtual devices turn their carrier off in there
>>>>> probe/create function, because it is unclear what state it is in after
>>>>> register_netdevice().
>>>>
>>>> Are you suggesting to turn it off both here and in the create function?
>>>> Or should I remove the invocation above?
>>>
>>> I noticed it in the _destruct function and went back to look at
>>> create. You probably want it in both, unless as part of destruct, you
>>> first disconnect all peers, which should set the carrier to off when
>>> the last peer disconnects?
>>
>> I think keeping the carrier on while no peer is connected is better for
>> OpenVPN.
> 
> I then have to wounder what carrier actually means?
> 
> Some routing protocols will kick off determining routes when the
> carrier goes down. Can you put team/bonding on top of openvpn? If the
> peer has gone, you want team to fall over to the active backup?

IIRC team/bonding requires a L2 interface, but ovpn creates a L3 device.
So I don't think this case applies here.

To be honest we don't have any real concept for the carrier off.
Especially on a server, I hardly believe it would be any useful.

However, on a client or on a p2p link, where there is exactly one remote 
host on the other side, it may make sense to turn the carrier off when 
that remote peer is lost.

There is an extra detail to consider: if the user wants to, the ovpn 
device should remain configured (with all IPs and routes) even if 
openvpn has fully disconnected and it is attempting a reconnection to 
another server. Reason being avoiding data leaks by accidentally 
removing routes to the tunnel (traffic that should go through the tunnel 
would rather go to the local network).

With all this being said, it may make sense to just keep the carrier on 
all time long.

Should we come up with something smarter in the future, we can still 
improve this behaviour.

Regards,

> 
>       Andrew
Andrew Lunn March 8, 2024, 1:13 p.m. UTC | #9
> To be honest we don't have any real concept for the carrier off.
> Especially on a server, I hardly believe it would be any useful.
> 
> However, on a client or on a p2p link, where there is exactly one remote
> host on the other side, it may make sense to turn the carrier off when that
> remote peer is lost.
> 
> There is an extra detail to consider: if the user wants to, the ovpn device
> should remain configured (with all IPs and routes) even if openvpn has fully
> disconnected and it is attempting a reconnection to another server. Reason
> being avoiding data leaks by accidentally removing routes to the tunnel
> (traffic that should go through the tunnel would rather go to the local
> network).
> 
> With all this being said, it may make sense to just keep the carrier on all
> time long.
> 
> Should we come up with something smarter in the future, we can still improve
> this behaviour.

O.K, so..

You already have more patches than recommended for a series, but... I
suggest you make the carrier_on in probe() a patch of its own with a
good commit message based on the above. We then have a clear
explanation in git why the carrier is always on.

	Andrew
Antonio Quartulli March 8, 2024, 2:21 p.m. UTC | #10
On 08/03/2024 14:13, Andrew Lunn wrote:
> You already have more patches than recommended for a series, but... I
> suggest you make the carrier_on in probe() a patch of its own with a
> good commit message based on the above. We then have a clear
> explanation in git why the carrier is always on.

ACK. will do.

Thanks for the recommendation.

Regards,
diff mbox series

Patch

diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index a1e19402e36d..b283449ba479 100644
--- a/drivers/net/ovpn/io.c
+++ b/drivers/net/ovpn/io.c
@@ -8,11 +8,37 @@ 
  */
 
 #include "io.h"
+#include "ovpnstruct.h"
+#include "netlink.h"
 
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 
 
+int ovpn_struct_init(struct net_device *dev)
+{
+	struct ovpn_struct *ovpn = netdev_priv(dev);
+	int err;
+
+	memset(ovpn, 0, sizeof(*ovpn));
+
+	ovpn->dev = dev;
+
+	err = ovpn_nl_init(ovpn);
+	if (err < 0)
+		return err;
+
+	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
+	if (!dev->tstats)
+		return -ENOMEM;
+
+	err = security_tun_dev_alloc_security(&ovpn->security);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
 /* Send user data to the network
  */
 netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
diff --git a/drivers/net/ovpn/io.h b/drivers/net/ovpn/io.h
index 0a076d14f721..e7728718c8a9 100644
--- a/drivers/net/ovpn/io.h
+++ b/drivers/net/ovpn/io.h
@@ -14,6 +14,7 @@ 
 
 struct sk_buff;
 
+int ovpn_struct_init(struct net_device *dev);
 netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev);
 
 #endif /* _NET_OVPN_OVPN_H_ */
diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c
index 3769f99cfe6f..1be0fd50c356 100644
--- a/drivers/net/ovpn/main.c
+++ b/drivers/net/ovpn/main.c
@@ -10,15 +10,20 @@ 
 #include "main.h"
 #include "netlink.h"
 #include "io.h"
+#include "ovpnstruct.h"
+#include "packet.h"
 
 #include <linux/genetlink.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
+#include <linux/lockdep.h>
 #include <linux/net.h>
 #include <linux/inetdevice.h>
 #include <linux/netdevice.h>
 #include <linux/version.h>
+#include <net/ip.h>
+#include <uapi/linux/if_arp.h>
 #include <uapi/linux/ovpn.h>
 
 
@@ -28,6 +33,16 @@ 
 #define DRV_DESCRIPTION	"OpenVPN data channel offload (ovpn)"
 #define DRV_COPYRIGHT	"(C) 2020-2024 OpenVPN, Inc."
 
+static LIST_HEAD(dev_list);
+
+static void ovpn_struct_free(struct net_device *net)
+{
+	struct ovpn_struct *ovpn = netdev_priv(net);
+
+	security_tun_dev_free_security(ovpn->security);
+	free_percpu(net->tstats);
+}
+
 /* Net device open */
 static int ovpn_net_open(struct net_device *dev)
 {
@@ -62,28 +77,120 @@  static const struct net_device_ops ovpn_netdev_ops = {
 	.ndo_get_stats64        = dev_get_tstats64,
 };
 
+static void ovpn_setup(struct net_device *dev)
+{
+	/* compute the overhead considering AEAD encryption */
+	const int overhead = sizeof(u32) + NONCE_WIRE_SIZE + 16 + sizeof(struct udphdr) +
+			     max(sizeof(struct ipv6hdr), sizeof(struct iphdr));
+
+	netdev_features_t feat = NETIF_F_SG | NETIF_F_LLTX |
+				 NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_GSO |
+				 NETIF_F_GSO_SOFTWARE | NETIF_F_HIGHDMA;
+
+	dev->needs_free_netdev = true;
+
+	dev->netdev_ops = &ovpn_netdev_ops;
+
+	dev->priv_destructor = ovpn_struct_free;
+
+	dev->hard_header_len = 0;
+	dev->addr_len = 0;
+	dev->mtu = ETH_DATA_LEN - overhead;
+	dev->min_mtu = IPV4_MIN_MTU;
+	dev->max_mtu = IP_MAX_MTU - overhead;
+
+	dev->type = ARPHRD_NONE;
+	dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+
+	dev->features |= feat;
+	dev->hw_features |= feat;
+	dev->hw_enc_features |= feat;
+
+	dev->needed_headroom = OVPN_HEAD_ROOM;
+	dev->needed_tailroom = OVPN_MAX_PADDING;
+}
+
+int ovpn_iface_create(const char *name, enum ovpn_mode mode, struct net *net)
+{
+	struct net_device *dev;
+	struct ovpn_struct *ovpn;
+	int ret;
+
+	dev = alloc_netdev(sizeof(struct ovpn_struct), name, NET_NAME_USER, ovpn_setup);
+
+	dev_net_set(dev, net);
+
+	ret = ovpn_struct_init(dev);
+	if (ret < 0)
+		goto err;
+
+	ovpn = netdev_priv(dev);
+	ovpn->mode = mode;
+
+	rtnl_lock();
+
+	ret = register_netdevice(dev);
+	if (ret < 0) {
+		netdev_dbg(dev, "cannot register interface %s: %d\n", dev->name, ret);
+		rtnl_unlock();
+		goto err;
+	}
+	rtnl_unlock();
+
+	return ret;
+
+err:
+	free_netdev(dev);
+	return ret;
+}
+
+void ovpn_iface_destruct(struct ovpn_struct *ovpn, bool unregister_netdev)
+{
+	ASSERT_RTNL();
+
+	netif_carrier_off(ovpn->dev);
+
+	list_del(&ovpn->dev_list);
+	ovpn->registered = false;
+
+	if (unregister_netdev)
+		unregister_netdevice(ovpn->dev);
+
+	synchronize_net();
+}
+
 static int ovpn_netdev_notifier_call(struct notifier_block *nb,
 				     unsigned long state, void *ptr)
 {
 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	struct ovpn_struct *ovpn;
 
 	if (!ovpn_dev_is_valid(dev))
 		return NOTIFY_DONE;
 
+	ovpn = netdev_priv(dev);
+
 	switch (state) {
 	case NETDEV_REGISTER:
 		/* add device to internal list for later destruction upon unregistration */
+		list_add(&ovpn->dev_list, &dev_list);
+		ovpn->registered = true;
 		break;
 	case NETDEV_UNREGISTER:
 		/* can be delivered multiple times, so check registered flag, then
 		 * destroy the interface
 		 */
+		if (!ovpn->registered)
+			return NOTIFY_DONE;
+
+		ovpn_iface_destruct(ovpn, false);
 		break;
 	case NETDEV_POST_INIT:
 	case NETDEV_GOING_DOWN:
 	case NETDEV_DOWN:
 	case NETDEV_UP:
 	case NETDEV_PRE_UP:
+		break;
 	default:
 		return NOTIFY_DONE;
 	}
diff --git a/drivers/net/ovpn/ovpnstruct.h b/drivers/net/ovpn/ovpnstruct.h
index 932eb90953e0..c2cdfd6f84b3 100644
--- a/drivers/net/ovpn/ovpnstruct.h
+++ b/drivers/net/ovpn/ovpnstruct.h
@@ -10,11 +10,27 @@ 
 #ifndef _NET_OVPN_OVPNSTRUCT_H_
 #define _NET_OVPN_OVPNSTRUCT_H_
 
+#include <uapi/linux/ovpn.h>
 #include <linux/netdevice.h>
+#include <linux/types.h>
 
 /* Our state per ovpn interface */
 struct ovpn_struct {
 	struct net_device *dev;
+
+	/* whether this device is still registered with netdev or not */
+	bool registered;
+
+	/* device operation mode (i.e. P2P, MP) */
+	enum ovpn_mode mode;
+
+	unsigned int max_tun_queue_len;
+
+	netdev_features_t set_features;
+
+	void *security;
+
+	struct list_head dev_list;
 };
 
 #endif /* _NET_OVPN_OVPNSTRUCT_H_ */