Files
Centos-kernel-stream-10/drivers/net/macvtap.c
T
Guillaume Nault dbcb13b51d rtnetlink: Pack newlink() params into struct
JIRA: https://redhat.atlassian.net/browse/RHEL-144808
Upstream Status: linux.git
Conflicts:
  * Missing upstream commit 83134ef46093 ("netkit: Add option for
    scrubbing skb meta data"):
    Centos Stream 10 doesn't have the IFLA_NETKIT_SCRUB and
    IFLA_NETKIT_PEER_SCRUB attributes and therefore netkit_new_link()
    doesn't have the scrub_{prim,peer} and policy_{prim,peer} variables
    and still uses default_{prim,peer} instead.

  * (context) Missing upstream commit 0ebe224ffce8 ("netkit: Simplify
    netkit mode over to use NLA_POLICY_MAX"):
    Centos Stream 10 still checks the IFLA_NETKIT_MODE attribute
    manually in netkit_new_link() and stores it in the attr variable,
    instead of relying netlink policy and using nla_get_u32() directly.

  * (context) Missing upstream commit b9ed315d3c4c ("netkit: Allow for
    configuring needed_{head,tail}room"):
    Centos Stream 10 doesn't have the IFLA_NETKIT_HEADROOM and
    IFLA_NETKIT_TAILROOM attributes and therefore netkit_new_link()
    doesn't have the headroom and tailroom variables.

  * (context) Already backported upstream commit c0178eec8884 ("net:
    hsr: prevent creation of HSR device with slaves from another
    netns") (RHEL commit 71ba9c253e):
    Centos Stream 10 already has the link and interlink variable in
    hsr_newlink().

commit 69c7be1b903fca2835e80ec506bd1d75ce84fb4d
Author: Xiao Liang <shaw.leon@gmail.com>
Date:   Wed Feb 19 20:50:28 2025 +0800

    rtnetlink: Pack newlink() params into struct

    There are 4 net namespaces involved when creating links:

     - source netns - where the netlink socket resides,
     - target netns - where to put the device being created,
     - link netns - netns associated with the device (backend),
     - peer netns - netns of peer device.

    Currently, two nets are passed to newlink() callback - "src_net"
    parameter and "dev_net" (implicitly in net_device). They are set as
    follows, depending on netlink attributes in the request.

     +------------+-------------------+---------+---------+
     | peer netns | IFLA_LINK_NETNSID | src_net | dev_net |
     +------------+-------------------+---------+---------+
     |            | absent            | source  | target  |
     | absent     +-------------------+---------+---------+
     |            | present           | link    | link    |
     +------------+-------------------+---------+---------+
     |            | absent            | peer    | target  |
     | present    +-------------------+---------+---------+
     |            | present           | peer    | link    |
     +------------+-------------------+---------+---------+

    When IFLA_LINK_NETNSID is present, the device is created in link netns
    first and then moved to target netns. This has some side effects,
    including extra ifindex allocation, ifname validation and link events.
    These could be avoided if we create it in target netns from
    the beginning.

    On the other hand, the meaning of src_net parameter is ambiguous. It
    varies depending on how parameters are passed. It is the effective
    link (or peer netns) by design, but some drivers ignore it and use
    dev_net instead.

    To provide more netns context for drivers, this patch packs existing
    newlink() parameters, along with the source netns, link netns and peer
    netns, into a struct. The old "src_net" is renamed to "net" to avoid
    confusion with real source netns, and will be deprecated later. The use
    of src_net are converted to params->net trivially.

    Signed-off-by: Xiao Liang <shaw.leon@gmail.com>
    Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
    Link: https://patch.msgid.link/20250219125039.18024-3-shaw.leon@gmail.com
    Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Signed-off-by: Guillaume Nault <gnault@redhat.com>
2026-05-13 14:43:10 +02:00

256 lines
6.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/etherdevice.h>
#include <linux/if_macvlan.h>
#include <linux/if_tap.h>
#include <linux/if_vlan.h>
#include <linux/interrupt.h>
#include <linux/nsproxy.h>
#include <linux/compat.h>
#include <linux/if_tun.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/cache.h>
#include <linux/sched/signal.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/idr.h>
#include <linux/fs.h>
#include <linux/uio.h>
#include <net/net_namespace.h>
#include <net/rtnetlink.h>
#include <net/sock.h>
#include <linux/virtio_net.h>
#include <linux/skb_array.h>
struct macvtap_dev {
struct macvlan_dev vlan;
struct tap_dev tap;
};
/*
* Variables for dealing with macvtaps device numbers.
*/
static dev_t macvtap_major;
static const void *macvtap_net_namespace(const struct device *d)
{
const struct net_device *dev = to_net_dev(d->parent);
return dev_net(dev);
}
static struct class macvtap_class = {
.name = "macvtap",
.ns_type = &net_ns_type_operations,
.namespace = macvtap_net_namespace,
};
static struct cdev macvtap_cdev;
#define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \
NETIF_F_TSO6)
static void macvtap_count_tx_dropped(struct tap_dev *tap)
{
struct macvtap_dev *vlantap = container_of(tap, struct macvtap_dev, tap);
struct macvlan_dev *vlan = &vlantap->vlan;
this_cpu_inc(vlan->pcpu_stats->tx_dropped);
}
static void macvtap_count_rx_dropped(struct tap_dev *tap)
{
struct macvtap_dev *vlantap = container_of(tap, struct macvtap_dev, tap);
struct macvlan_dev *vlan = &vlantap->vlan;
macvlan_count_rx(vlan, 0, 0, 0);
}
static void macvtap_update_features(struct tap_dev *tap,
netdev_features_t features)
{
struct macvtap_dev *vlantap = container_of(tap, struct macvtap_dev, tap);
struct macvlan_dev *vlan = &vlantap->vlan;
vlan->set_features = features;
netdev_update_features(vlan->dev);
}
static int macvtap_newlink(struct net_device *dev,
struct rtnl_newlink_params *params,
struct netlink_ext_ack *extack)
{
struct macvtap_dev *vlantap = netdev_priv(dev);
int err;
INIT_LIST_HEAD(&vlantap->tap.queue_list);
/* Since macvlan supports all offloads by default, make
* tap support all offloads also.
*/
vlantap->tap.tap_features = TUN_OFFLOADS;
/* Register callbacks for rx/tx drops accounting and updating
* net_device features
*/
vlantap->tap.count_tx_dropped = macvtap_count_tx_dropped;
vlantap->tap.count_rx_dropped = macvtap_count_rx_dropped;
vlantap->tap.update_features = macvtap_update_features;
err = netdev_rx_handler_register(dev, tap_handle_frame, &vlantap->tap);
if (err)
return err;
/* Don't put anything that may fail after macvlan_common_newlink
* because we can't undo what it does.
*/
err = macvlan_common_newlink(dev, params, extack);
if (err) {
netdev_rx_handler_unregister(dev);
return err;
}
vlantap->tap.dev = vlantap->vlan.dev;
return 0;
}
static void macvtap_dellink(struct net_device *dev,
struct list_head *head)
{
struct macvtap_dev *vlantap = netdev_priv(dev);
netdev_rx_handler_unregister(dev);
tap_del_queues(&vlantap->tap);
macvlan_dellink(dev, head);
}
static void macvtap_setup(struct net_device *dev)
{
macvlan_common_setup(dev);
dev->tx_queue_len = TUN_READQ_SIZE;
}
static struct net *macvtap_link_net(const struct net_device *dev)
{
return dev_net(macvlan_dev_real_dev(dev));
}
static struct rtnl_link_ops macvtap_link_ops __read_mostly = {
.kind = "macvtap",
.setup = macvtap_setup,
.newlink = macvtap_newlink,
.dellink = macvtap_dellink,
.get_link_net = macvtap_link_net,
.priv_size = sizeof(struct macvtap_dev),
};
static int macvtap_device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct macvtap_dev *vlantap;
struct device *classdev;
dev_t devt;
int err;
char tap_name[IFNAMSIZ];
if (dev->rtnl_link_ops != &macvtap_link_ops)
return NOTIFY_DONE;
snprintf(tap_name, IFNAMSIZ, "tap%d", dev->ifindex);
vlantap = netdev_priv(dev);
switch (event) {
case NETDEV_REGISTER:
/* Create the device node here after the network device has
* been registered but before register_netdevice has
* finished running.
*/
err = tap_get_minor(macvtap_major, &vlantap->tap);
if (err)
return notifier_from_errno(err);
devt = MKDEV(MAJOR(macvtap_major), vlantap->tap.minor);
classdev = device_create(&macvtap_class, &dev->dev, devt,
dev, "%s", tap_name);
if (IS_ERR(classdev)) {
tap_free_minor(macvtap_major, &vlantap->tap);
return notifier_from_errno(PTR_ERR(classdev));
}
err = sysfs_create_link(&dev->dev.kobj, &classdev->kobj,
tap_name);
if (err)
return notifier_from_errno(err);
break;
case NETDEV_UNREGISTER:
/* vlan->minor == 0 if NETDEV_REGISTER above failed */
if (vlantap->tap.minor == 0)
break;
sysfs_remove_link(&dev->dev.kobj, tap_name);
devt = MKDEV(MAJOR(macvtap_major), vlantap->tap.minor);
device_destroy(&macvtap_class, devt);
tap_free_minor(macvtap_major, &vlantap->tap);
break;
case NETDEV_CHANGE_TX_QUEUE_LEN:
if (tap_queue_resize(&vlantap->tap))
return NOTIFY_BAD;
break;
}
return NOTIFY_DONE;
}
static struct notifier_block macvtap_notifier_block __read_mostly = {
.notifier_call = macvtap_device_event,
};
static int __init macvtap_init(void)
{
int err;
err = tap_create_cdev(&macvtap_cdev, &macvtap_major, "macvtap",
THIS_MODULE);
if (err)
goto out1;
err = class_register(&macvtap_class);
if (err)
goto out2;
err = register_netdevice_notifier(&macvtap_notifier_block);
if (err)
goto out3;
err = macvlan_link_register(&macvtap_link_ops);
if (err)
goto out4;
return 0;
out4:
unregister_netdevice_notifier(&macvtap_notifier_block);
out3:
class_unregister(&macvtap_class);
out2:
tap_destroy_cdev(macvtap_major, &macvtap_cdev);
out1:
return err;
}
module_init(macvtap_init);
static void __exit macvtap_exit(void)
{
rtnl_link_unregister(&macvtap_link_ops);
unregister_netdevice_notifier(&macvtap_notifier_block);
class_unregister(&macvtap_class);
tap_destroy_cdev(macvtap_major, &macvtap_cdev);
}
module_exit(macvtap_exit);
MODULE_ALIAS_RTNL_LINK("macvtap");
MODULE_DESCRIPTION("MAC-VLAN based tap driver");
MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>");
MODULE_LICENSE("GPL");