Files
Ralf Lici e9714db804 ovpn: run deferred work on a module-owned workqueue
ovpn queues several work items whose callbacks execute module text.
These works currently run on the global system workqueues, so module
exit has no driver-owned drain point that guarantees the callbacks have
fully returned before the module text can be freed.

Object references protect the objects used by the callbacks, but they do
not prove that a workqueue function has returned. In particular, a
worker can drop the final reference that unblocks device teardown while
it is still executing ovpn code.

Add a module-owned workqueue and queue all ovpn work items on it. During
module exit, unregister rtnl and netlink first, flush the workqueue so
ordinary ovpn workers finish, run the final RCU barrier, and destroy the
workqueue last. This keeps the workqueue available for cleanup work
queued from RCU callbacks, while ensuring no ovpn work item can outlive
the module text.

The per-device delayed keepalive work remains explicitly disabled during
netdev teardown (disable_delayed_work_sync in ndo_uninit), since
flush_workqueue does not flush delayed work that is still only pending
on its timer.

Fixes: 3ecfd9349f ("ovpn: implement keepalive mechanism")
Fixes: 11851cbd60 ("ovpn: implement TCP transport")
Signed-off-by: Ralf Lici <[email protected]>
Signed-off-by: Antonio Quartulli <[email protected]>
2026-08-09 22:47:57 +02:00

317 lines
7.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* OpenVPN data channel offload
*
* Copyright (C) 2020-2025 OpenVPN, Inc.
*
* Author: Antonio Quartulli <[email protected]>
* James Yonan <[email protected]>
*/
#include <linux/ethtool.h>
#include <linux/genetlink.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/workqueue.h>
#include <net/gro_cells.h>
#include <net/ip.h>
#include <net/rtnetlink.h>
#include <uapi/linux/if_arp.h>
#include "ovpnpriv.h"
#include "main.h"
#include "netlink.h"
#include "io.h"
#include "peer.h"
#include "proto.h"
#include "tcp.h"
#include "udp.h"
/* module-owned workqueue on which all ovpn-specific work is queued */
struct workqueue_struct *ovpn_wq;
static void ovpn_priv_free(struct net_device *net)
{
struct ovpn_priv *ovpn = netdev_priv(net);
kfree(ovpn->peers);
}
static int ovpn_mp_alloc(struct ovpn_priv *ovpn)
{
int i;
if (ovpn->mode != OVPN_MODE_MP)
return 0;
/* the peer container is fairly large, therefore we allocate it only in
* MP mode
*/
ovpn->peers = kzalloc_obj(*ovpn->peers);
if (!ovpn->peers)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(ovpn->peers->by_id); i++) {
INIT_HLIST_HEAD(&ovpn->peers->by_id[i]);
INIT_HLIST_NULLS_HEAD(&ovpn->peers->by_vpn_addr4[i], i);
INIT_HLIST_NULLS_HEAD(&ovpn->peers->by_vpn_addr6[i], i);
INIT_HLIST_NULLS_HEAD(&ovpn->peers->by_transp_addr[i], i);
}
return 0;
}
static int ovpn_net_init(struct net_device *dev)
{
struct ovpn_priv *ovpn = netdev_priv(dev);
int err = gro_cells_init(&ovpn->gro_cells, dev);
if (err < 0)
return err;
err = ovpn_mp_alloc(ovpn);
if (err < 0) {
gro_cells_destroy(&ovpn->gro_cells);
return err;
}
return 0;
}
static void ovpn_net_uninit(struct net_device *dev)
{
struct ovpn_priv *ovpn = netdev_priv(dev);
disable_delayed_work_sync(&ovpn->keepalive_work);
ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN);
gro_cells_destroy(&ovpn->gro_cells);
}
static int ovpn_net_open(struct net_device *dev)
{
struct ovpn_priv *ovpn = netdev_priv(dev);
struct in_device *dev_v4;
/* the IPv4 in_device (and thus its config) is recreated whenever the
* interface is moved to a new netns, so redirects must be disabled on
* every bring-up rather than once at creation time, otherwise the
* setting is silently lost after such a move
*/
if (ovpn->mode == OVPN_MODE_MP) {
dev_v4 = __in_dev_get_rtnl(dev);
if (dev_v4) {
/* disable redirects as Linux gets confused by ovpn
* handling same-LAN routing.
* This happens because a multipeer interface is used as
* relay point between hosts in the same subnet, while
* in a classic LAN this would not be needed because the
* two hosts would be able to talk directly.
*/
IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false);
IPV4_DEVCONF_ALL(dev_net(dev), SEND_REDIRECTS) = false;
}
}
return 0;
}
static const struct net_device_ops ovpn_netdev_ops = {
.ndo_init = ovpn_net_init,
.ndo_uninit = ovpn_net_uninit,
.ndo_open = ovpn_net_open,
.ndo_start_xmit = ovpn_net_xmit,
};
static const struct device_type ovpn_type = {
.name = OVPN_FAMILY_NAME,
};
static const struct nla_policy ovpn_policy[IFLA_OVPN_MAX + 1] = {
[IFLA_OVPN_MODE] = NLA_POLICY_RANGE(NLA_U8, OVPN_MODE_P2P,
OVPN_MODE_MP),
};
/**
* ovpn_dev_is_valid - check if the netdevice is of type 'ovpn'
* @dev: the interface to check
*
* Return: whether the netdevice is of type 'ovpn'
*/
bool ovpn_dev_is_valid(const struct net_device *dev)
{
return dev->netdev_ops == &ovpn_netdev_ops;
}
static void ovpn_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strscpy(info->driver, "ovpn", sizeof(info->driver));
strscpy(info->bus_info, "ovpn", sizeof(info->bus_info));
}
static const struct ethtool_ops ovpn_ethtool_ops = {
.get_drvinfo = ovpn_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_ts_info = ethtool_op_get_ts_info,
};
static void ovpn_setup(struct net_device *dev)
{
netdev_features_t feat = NETIF_F_SG | NETIF_F_GSO |
NETIF_F_GSO_SOFTWARE | NETIF_F_HIGHDMA;
dev->needs_free_netdev = true;
dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
dev->ethtool_ops = &ovpn_ethtool_ops;
dev->netdev_ops = &ovpn_netdev_ops;
dev->priv_destructor = ovpn_priv_free;
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->mtu = ETH_DATA_LEN - OVPN_HEAD_ROOM;
dev->min_mtu = IPV4_MIN_MTU;
dev->max_mtu = IP_MAX_MTU - OVPN_HEAD_ROOM;
dev->type = ARPHRD_NONE;
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
dev->priv_flags |= IFF_NO_QUEUE;
/* when routing packets to a LAN behind a client, we rely on the
* route entry that originally brought the packet into ovpn, so
* don't release it
*/
netif_keep_dst(dev);
dev->lltx = true;
dev->features |= feat;
dev->hw_features |= feat;
dev->hw_enc_features |= feat;
dev->needed_headroom = ALIGN(OVPN_HEAD_ROOM, 4);
dev->needed_tailroom = OVPN_MAX_PADDING;
SET_NETDEV_DEVTYPE(dev, &ovpn_type);
}
static int ovpn_newlink(struct net_device *dev,
struct rtnl_newlink_params *params,
struct netlink_ext_ack *extack)
{
struct ovpn_priv *ovpn = netdev_priv(dev);
struct nlattr **data = params->data;
enum ovpn_mode mode = OVPN_MODE_P2P;
int ret;
if (data && data[IFLA_OVPN_MODE]) {
mode = nla_get_u8(data[IFLA_OVPN_MODE]);
netdev_dbg(dev, "setting device mode: %u\n", mode);
}
ovpn->dev = dev;
ovpn->mode = mode;
spin_lock_init(&ovpn->lock);
INIT_DELAYED_WORK(&ovpn->keepalive_work, ovpn_peer_keepalive_work);
/* Set carrier explicitly after registration, this way state is
* clearly defined.
*
* In case of MP interfaces we keep the carrier always on.
*
* Carrier for P2P interfaces is initially off and it is then
* switched on and off when the remote peer is added or deleted.
*/
if (ovpn->mode == OVPN_MODE_MP)
netif_carrier_on(dev);
else
netif_carrier_off(dev);
ret = register_netdevice(dev);
if (ret < 0)
return ret;
return 0;
}
static size_t ovpn_get_size(const struct net_device *dev)
{
/* IFLA_OVPN_MODE */
return nla_total_size(sizeof(u8));
}
static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct ovpn_priv *ovpn = netdev_priv(dev);
if (nla_put_u8(skb, IFLA_OVPN_MODE, ovpn->mode))
return -EMSGSIZE;
return 0;
}
static struct rtnl_link_ops ovpn_link_ops = {
.kind = "ovpn",
.netns_refund = false,
.priv_size = sizeof(struct ovpn_priv),
.setup = ovpn_setup,
.policy = ovpn_policy,
.maxtype = IFLA_OVPN_MAX,
.newlink = ovpn_newlink,
.get_size = ovpn_get_size,
.fill_info = ovpn_fill_info,
};
static int __init ovpn_init(void)
{
int err;
ovpn_tcp_init();
ovpn_wq = alloc_workqueue("ovpn", WQ_PERCPU, 0);
if (!ovpn_wq) {
pr_err("ovpn: cannot allocate workqueue\n");
return -ENOMEM;
}
err = rtnl_link_register(&ovpn_link_ops);
if (err) {
pr_err("ovpn: can't register rtnl link ops: %d\n", err);
goto destroy_wq;
}
err = ovpn_nl_register();
if (err) {
pr_err("ovpn: can't register netlink family: %d\n", err);
goto unreg_rtnl;
}
return 0;
unreg_rtnl:
rtnl_link_unregister(&ovpn_link_ops);
destroy_wq:
destroy_workqueue(ovpn_wq);
ovpn_wq = NULL;
return err;
}
static __exit void ovpn_cleanup(void)
{
ovpn_nl_unregister();
rtnl_link_unregister(&ovpn_link_ops);
flush_workqueue(ovpn_wq);
rcu_barrier();
destroy_workqueue(ovpn_wq);
ovpn_wq = NULL;
}
module_init(ovpn_init);
module_exit(ovpn_cleanup);
MODULE_DESCRIPTION("OpenVPN data channel offload (ovpn)");
MODULE_AUTHOR("Antonio Quartulli <[email protected]>");
MODULE_LICENSE("GPL");