diff options
| author | 2018-08-15 15:04:25 -0700 | |
|---|---|---|
| committer | 2018-08-15 15:04:25 -0700 | |
| commit | 9a76aba02a37718242d7cdc294f0a3901928aa57 (patch) | |
| tree | 2040d038f85d2120f21af83b0793efd5af1864e3 /drivers/net/netdevsim | |
| parent | x86: i8259: Add missing include file (diff) | |
| parent | bpf: test: fix spelling mistake "REUSEEPORT" -> "REUSEPORT" (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
"Highlights:
- Gustavo A. R. Silva keeps working on the implicit switch fallthru
changes.
- Support 802.11ax High-Efficiency wireless in cfg80211 et al, From
Luca Coelho.
- Re-enable ASPM in r8169, from Kai-Heng Feng.
- Add virtual XFRM interfaces, which avoids all of the limitations of
existing IPSEC tunnels. From Steffen Klassert.
- Convert GRO over to use a hash table, so that when we have many
flows active we don't traverse a long list during accumluation.
- Many new self tests for routing, TC, tunnels, etc. Too many
contributors to mention them all, but I'm really happy to keep
seeing this stuff.
- Hardware timestamping support for dpaa_eth/fsl-fman from Yangbo Lu.
- Lots of cleanups and fixes in L2TP code from Guillaume Nault.
- Add IPSEC offload support to netdevsim, from Shannon Nelson.
- Add support for slotting with non-uniform distribution to netem
packet scheduler, from Yousuk Seung.
- Add UDP GSO support to mlx5e, from Boris Pismenny.
- Support offloading of Team LAG in NFP, from John Hurley.
- Allow to configure TX queue selection based upon RX queue, from
Amritha Nambiar.
- Support ethtool ring size configuration in aquantia, from Anton
Mikaev.
- Support DSCP and flowlabel per-transport in SCTP, from Xin Long.
- Support list based batching and stack traversal of SKBs, this is
very exciting work. From Edward Cree.
- Busyloop optimizations in vhost_net, from Toshiaki Makita.
- Introduce the ETF qdisc, which allows time based transmissions. IGB
can offload this in hardware. From Vinicius Costa Gomes.
- Add parameter support to devlink, from Moshe Shemesh.
- Several multiplication and division optimizations for BPF JIT in
nfp driver, from Jiong Wang.
- Lots of prepatory work to make more of the packet scheduler layer
lockless, when possible, from Vlad Buslov.
- Add ACK filter and NAT awareness to sch_cake packet scheduler, from
Toke Høiland-Jørgensen.
- Support regions and region snapshots in devlink, from Alex Vesker.
- Allow to attach XDP programs to both HW and SW at the same time on
a given device, with initial support in nfp. From Jakub Kicinski.
- Add TLS RX offload and support in mlx5, from Ilya Lesokhin.
- Use PHYLIB in r8169 driver, from Heiner Kallweit.
- All sorts of changes to support Spectrum 2 in mlxsw driver, from
Ido Schimmel.
- PTP support in mv88e6xxx DSA driver, from Andrew Lunn.
- Make TCP_USER_TIMEOUT socket option more accurate, from Jon
Maxwell.
- Support for templates in packet scheduler classifier, from Jiri
Pirko.
- IPV6 support in RDS, from Ka-Cheong Poon.
- Native tproxy support in nf_tables, from Máté Eckl.
- Maintain IP fragment queue in an rbtree, but optimize properly for
in-order frags. From Peter Oskolkov.
- Improvde handling of ACKs on hole repairs, from Yuchung Cheng"
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1996 commits)
bpf: test: fix spelling mistake "REUSEEPORT" -> "REUSEPORT"
hv/netvsc: Fix NULL dereference at single queue mode fallback
net: filter: mark expected switch fall-through
xen-netfront: fix warn message as irq device name has '/'
cxgb4: Add new T5 PCI device ids 0x50af and 0x50b0
net: dsa: mv88e6xxx: missing unlock on error path
rds: fix building with IPV6=m
inet/connection_sock: prefer _THIS_IP_ to current_text_addr
net: dsa: mv88e6xxx: bitwise vs logical bug
net: sock_diag: Fix spectre v1 gadget in __sock_diag_cmd()
ieee802154: hwsim: using right kind of iteration
net: hns3: Add vlan filter setting by ethtool command -K
net: hns3: Set tx ring' tc info when netdev is up
net: hns3: Remove tx ring BD len register in hns3_enet
net: hns3: Fix desc num set to default when setting channel
net: hns3: Fix for phy link issue when using marvell phy driver
net: hns3: Fix for information of phydev lost problem when down/up
net: hns3: Fix for command format parsing error in hclge_is_all_function_id_zero
net: hns3: Add support for serdes loopback selftest
bnxt_en: take coredump_record structure off stack
...
Diffstat (limited to 'drivers/net/netdevsim')
| -rw-r--r-- | drivers/net/netdevsim/Makefile | 4 | ||||
| -rw-r--r-- | drivers/net/netdevsim/bpf.c | 91 | ||||
| -rw-r--r-- | drivers/net/netdevsim/ipsec.c | 297 | ||||
| -rw-r--r-- | drivers/net/netdevsim/netdev.c | 119 | ||||
| -rw-r--r-- | drivers/net/netdevsim/netdevsim.h | 71 |
5 files changed, 524 insertions, 58 deletions
diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile index 449b2a1a1800..0fee1d06c084 100644 --- a/drivers/net/netdevsim/Makefile +++ b/drivers/net/netdevsim/Makefile @@ -13,3 +13,7 @@ endif ifneq ($(CONFIG_NET_DEVLINK),) netdevsim-objs += devlink.o fib.o endif + +ifneq ($(CONFIG_XFRM_OFFLOAD),) +netdevsim-objs += ipsec.o +endif diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c index 75c25306d234..81444208b216 100644 --- a/drivers/net/netdevsim/bpf.c +++ b/drivers/net/netdevsim/bpf.c @@ -92,7 +92,7 @@ static const struct bpf_prog_offload_ops nsim_bpf_analyzer_ops = { static bool nsim_xdp_offload_active(struct netdevsim *ns) { - return ns->xdp_prog_mode == XDP_ATTACHED_HW; + return ns->xdp_hw.prog; } static void nsim_prog_set_loaded(struct bpf_prog *prog, bool loaded) @@ -195,14 +195,14 @@ static int nsim_xdp_offload_prog(struct netdevsim *ns, struct netdev_bpf *bpf) return nsim_bpf_offload(ns, bpf->prog, nsim_xdp_offload_active(ns)); } -static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf) +static int +nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf, + struct xdp_attachment_info *xdp) { int err; - if (ns->xdp_prog && (bpf->flags ^ ns->xdp_flags) & XDP_FLAGS_MODES) { - NSIM_EA(bpf->extack, "program loaded with different flags"); + if (!xdp_attachment_flags_ok(xdp, bpf)) return -EBUSY; - } if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) { NSIM_EA(bpf->extack, "driver XDP disabled in DebugFS"); @@ -219,18 +219,7 @@ static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf) return err; } - if (ns->xdp_prog) - bpf_prog_put(ns->xdp_prog); - - ns->xdp_prog = bpf->prog; - ns->xdp_flags = bpf->flags; - - if (!bpf->prog) - ns->xdp_prog_mode = XDP_ATTACHED_NONE; - else if (bpf->command == XDP_SETUP_PROG) - ns->xdp_prog_mode = XDP_ATTACHED_DRV; - else - ns->xdp_prog_mode = XDP_ATTACHED_HW; + xdp_attachment_setup(xdp, bpf); return 0; } @@ -249,8 +238,8 @@ static int nsim_bpf_create_prog(struct netdevsim *ns, struct bpf_prog *prog) state->state = "verify"; /* Program id is not populated yet when we create the state. */ - sprintf(name, "%u", ns->prog_id_gen++); - state->ddir = debugfs_create_dir(name, ns->ddir_bpf_bound_progs); + sprintf(name, "%u", ns->sdev->prog_id_gen++); + state->ddir = debugfs_create_dir(name, ns->sdev->ddir_bpf_bound_progs); if (IS_ERR_OR_NULL(state->ddir)) { kfree(state); return -ENOMEM; @@ -261,7 +250,7 @@ static int nsim_bpf_create_prog(struct netdevsim *ns, struct bpf_prog *prog) &state->state, &nsim_bpf_string_fops); debugfs_create_bool("loaded", 0400, state->ddir, &state->is_loaded); - list_add_tail(&state->l, &ns->bpf_bound_progs); + list_add_tail(&state->l, &ns->sdev->bpf_bound_progs); prog->aux->offload->dev_priv = state; @@ -290,10 +279,6 @@ static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf) NSIM_EA(bpf->extack, "MTU too large w/ XDP enabled"); return -EINVAL; } - if (nsim_xdp_offload_active(ns)) { - NSIM_EA(bpf->extack, "xdp offload active, can't load drv prog"); - return -EBUSY; - } return 0; } @@ -309,7 +294,7 @@ nsim_setup_prog_hw_checks(struct netdevsim *ns, struct netdev_bpf *bpf) NSIM_EA(bpf->extack, "xdpoffload of non-bound program"); return -EINVAL; } - if (bpf->prog->aux->offload->netdev != ns->netdev) { + if (!bpf_offload_dev_match(bpf->prog, ns->netdev)) { NSIM_EA(bpf->extack, "program bound to different dev"); return -EINVAL; } @@ -512,7 +497,7 @@ nsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap) } offmap->dev_ops = &nsim_bpf_map_ops; - list_add_tail(&nmap->l, &ns->bpf_bound_maps); + list_add_tail(&nmap->l, &ns->sdev->bpf_bound_maps); return 0; @@ -567,22 +552,21 @@ int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf) nsim_bpf_destroy_prog(bpf->offload.prog); return 0; case XDP_QUERY_PROG: - bpf->prog_attached = ns->xdp_prog_mode; - bpf->prog_id = ns->xdp_prog ? ns->xdp_prog->aux->id : 0; - bpf->prog_flags = ns->xdp_prog ? ns->xdp_flags : 0; - return 0; + return xdp_attachment_query(&ns->xdp, bpf); + case XDP_QUERY_PROG_HW: + return xdp_attachment_query(&ns->xdp_hw, bpf); case XDP_SETUP_PROG: err = nsim_setup_prog_checks(ns, bpf); if (err) return err; - return nsim_xdp_set_prog(ns, bpf); + return nsim_xdp_set_prog(ns, bpf, &ns->xdp); case XDP_SETUP_PROG_HW: err = nsim_setup_prog_hw_checks(ns, bpf); if (err) return err; - return nsim_xdp_set_prog(ns, bpf); + return nsim_xdp_set_prog(ns, bpf, &ns->xdp_hw); case BPF_OFFLOAD_MAP_ALLOC: if (!ns->bpf_map_accept) return -EOPNOTSUPP; @@ -598,8 +582,26 @@ int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf) int nsim_bpf_init(struct netdevsim *ns) { - INIT_LIST_HEAD(&ns->bpf_bound_progs); - INIT_LIST_HEAD(&ns->bpf_bound_maps); + int err; + + if (ns->sdev->refcnt == 1) { + INIT_LIST_HEAD(&ns->sdev->bpf_bound_progs); + INIT_LIST_HEAD(&ns->sdev->bpf_bound_maps); + + ns->sdev->ddir_bpf_bound_progs = + debugfs_create_dir("bpf_bound_progs", ns->sdev->ddir); + if (IS_ERR_OR_NULL(ns->sdev->ddir_bpf_bound_progs)) + return -ENOMEM; + + ns->sdev->bpf_dev = bpf_offload_dev_create(); + err = PTR_ERR_OR_ZERO(ns->sdev->bpf_dev); + if (err) + return err; + } + + err = bpf_offload_dev_netdev_register(ns->sdev->bpf_dev, ns->netdev); + if (err) + goto err_destroy_bdev; debugfs_create_u32("bpf_offloaded_id", 0400, ns->ddir, &ns->bpf_offloaded_id); @@ -609,10 +611,6 @@ int nsim_bpf_init(struct netdevsim *ns) &ns->bpf_bind_accept); debugfs_create_u32("bpf_bind_verifier_delay", 0600, ns->ddir, &ns->bpf_bind_verifier_delay); - ns->ddir_bpf_bound_progs = - debugfs_create_dir("bpf_bound_progs", ns->ddir); - if (IS_ERR_OR_NULL(ns->ddir_bpf_bound_progs)) - return -ENOMEM; ns->bpf_tc_accept = true; debugfs_create_bool("bpf_tc_accept", 0600, ns->ddir, @@ -631,12 +629,23 @@ int nsim_bpf_init(struct netdevsim *ns) &ns->bpf_map_accept); return 0; + +err_destroy_bdev: + if (ns->sdev->refcnt == 1) + bpf_offload_dev_destroy(ns->sdev->bpf_dev); + return err; } void nsim_bpf_uninit(struct netdevsim *ns) { - WARN_ON(!list_empty(&ns->bpf_bound_progs)); - WARN_ON(!list_empty(&ns->bpf_bound_maps)); - WARN_ON(ns->xdp_prog); + WARN_ON(ns->xdp.prog); + WARN_ON(ns->xdp_hw.prog); WARN_ON(ns->bpf_offloaded); + bpf_offload_dev_netdev_unregister(ns->sdev->bpf_dev, ns->netdev); + + if (ns->sdev->refcnt == 1) { + WARN_ON(!list_empty(&ns->sdev->bpf_bound_progs)); + WARN_ON(!list_empty(&ns->sdev->bpf_bound_maps)); + bpf_offload_dev_destroy(ns->sdev->bpf_dev); + } } diff --git a/drivers/net/netdevsim/ipsec.c b/drivers/net/netdevsim/ipsec.c new file mode 100644 index 000000000000..2dcf6cc269d0 --- /dev/null +++ b/drivers/net/netdevsim/ipsec.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2018 Oracle and/or its affiliates. All rights reserved. */ + +#include <crypto/aead.h> +#include <linux/debugfs.h> +#include <net/xfrm.h> + +#include "netdevsim.h" + +#define NSIM_IPSEC_AUTH_BITS 128 + +static ssize_t nsim_dbg_netdev_ops_read(struct file *filp, + char __user *buffer, + size_t count, loff_t *ppos) +{ + struct netdevsim *ns = filp->private_data; + struct nsim_ipsec *ipsec = &ns->ipsec; + size_t bufsize; + char *buf, *p; + int len; + int i; + + /* the buffer needed is + * (num SAs * 3 lines each * ~60 bytes per line) + one more line + */ + bufsize = (ipsec->count * 4 * 60) + 60; + buf = kzalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + p = buf; + p += snprintf(p, bufsize - (p - buf), + "SA count=%u tx=%u\n", + ipsec->count, ipsec->tx); + + for (i = 0; i < NSIM_IPSEC_MAX_SA_COUNT; i++) { + struct nsim_sa *sap = &ipsec->sa[i]; + + if (!sap->used) + continue; + + p += snprintf(p, bufsize - (p - buf), + "sa[%i] %cx ipaddr=0x%08x %08x %08x %08x\n", + i, (sap->rx ? 'r' : 't'), sap->ipaddr[0], + sap->ipaddr[1], sap->ipaddr[2], sap->ipaddr[3]); + p += snprintf(p, bufsize - (p - buf), + "sa[%i] spi=0x%08x proto=0x%x salt=0x%08x crypt=%d\n", + i, be32_to_cpu(sap->xs->id.spi), + sap->xs->id.proto, sap->salt, sap->crypt); + p += snprintf(p, bufsize - (p - buf), + "sa[%i] key=0x%08x %08x %08x %08x\n", + i, sap->key[0], sap->key[1], + sap->key[2], sap->key[3]); + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, p - buf); + + kfree(buf); + return len; +} + +static const struct file_operations ipsec_dbg_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = nsim_dbg_netdev_ops_read, +}; + +static int nsim_ipsec_find_empty_idx(struct nsim_ipsec *ipsec) +{ + u32 i; + + if (ipsec->count == NSIM_IPSEC_MAX_SA_COUNT) + return -ENOSPC; + + /* search sa table */ + for (i = 0; i < NSIM_IPSEC_MAX_SA_COUNT; i++) { + if (!ipsec->sa[i].used) + return i; + } + + return -ENOSPC; +} + +static int nsim_ipsec_parse_proto_keys(struct xfrm_state *xs, + u32 *mykey, u32 *mysalt) +{ + const char aes_gcm_name[] = "rfc4106(gcm(aes))"; + struct net_device *dev = xs->xso.dev; + unsigned char *key_data; + char *alg_name = NULL; + int key_len; + + if (!xs->aead) { + netdev_err(dev, "Unsupported IPsec algorithm\n"); + return -EINVAL; + } + + if (xs->aead->alg_icv_len != NSIM_IPSEC_AUTH_BITS) { + netdev_err(dev, "IPsec offload requires %d bit authentication\n", + NSIM_IPSEC_AUTH_BITS); + return -EINVAL; + } + + key_data = &xs->aead->alg_key[0]; + key_len = xs->aead->alg_key_len; + alg_name = xs->aead->alg_name; + + if (strcmp(alg_name, aes_gcm_name)) { + netdev_err(dev, "Unsupported IPsec algorithm - please use %s\n", + aes_gcm_name); + return -EINVAL; + } + + /* 160 accounts for 16 byte key and 4 byte salt */ + if (key_len > NSIM_IPSEC_AUTH_BITS) { + *mysalt = ((u32 *)key_data)[4]; + } else if (key_len == NSIM_IPSEC_AUTH_BITS) { + *mysalt = 0; + } else { + netdev_err(dev, "IPsec hw offload only supports 128 bit keys with optional 32 bit salt\n"); + return -EINVAL; + } + memcpy(mykey, key_data, 16); + + return 0; +} + +static int nsim_ipsec_add_sa(struct xfrm_state *xs) +{ + struct nsim_ipsec *ipsec; + struct net_device *dev; + struct netdevsim *ns; + struct nsim_sa sa; + u16 sa_idx; + int ret; + + dev = xs->xso.dev; + ns = netdev_priv(dev); + ipsec = &ns->ipsec; + + if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) { + netdev_err(dev, "Unsupported protocol 0x%04x for ipsec offload\n", + xs->id.proto); + return -EINVAL; + } + + if (xs->calg) { + netdev_err(dev, "Compression offload not supported\n"); + return -EINVAL; + } + + /* find the first unused index */ + ret = nsim_ipsec_find_empty_idx(ipsec); + if (ret < 0) { + netdev_err(dev, "No space for SA in Rx table!\n"); + return ret; + } + sa_idx = (u16)ret; + + memset(&sa, 0, sizeof(sa)); + sa.used = true; + sa.xs = xs; + + if (sa.xs->id.proto & IPPROTO_ESP) + sa.crypt = xs->ealg || xs->aead; + + /* get the key and salt */ + ret = nsim_ipsec_parse_proto_keys(xs, sa.key, &sa.salt); + if (ret) { + netdev_err(dev, "Failed to get key data for SA table\n"); + return ret; + } + + if (xs->xso.flags & XFRM_OFFLOAD_INBOUND) { + sa.rx = true; + + if (xs->props.family == AF_INET6) + memcpy(sa.ipaddr, &xs->id.daddr.a6, 16); + else + memcpy(&sa.ipaddr[3], &xs->id.daddr.a4, 4); + } + + /* the preparations worked, so save the info */ + memcpy(&ipsec->sa[sa_idx], &sa, sizeof(sa)); + + /* the XFRM stack doesn't like offload_handle == 0, + * so add a bitflag in case our array index is 0 + */ + xs->xso.offload_handle = sa_idx | NSIM_IPSEC_VALID; + ipsec->count++; + + return 0; +} + +static void nsim_ipsec_del_sa(struct xfrm_state *xs) +{ + struct netdevsim *ns = netdev_priv(xs->xso.dev); + struct nsim_ipsec *ipsec = &ns->ipsec; + u16 sa_idx; + + sa_idx = xs->xso.offload_handle & ~NSIM_IPSEC_VALID; + if (!ipsec->sa[sa_idx].used) { + netdev_err(ns->netdev, "Invalid SA for delete sa_idx=%d\n", + sa_idx); + return; + } + + memset(&ipsec->sa[sa_idx], 0, sizeof(struct nsim_sa)); + ipsec->count--; +} + +static bool nsim_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs) +{ + struct netdevsim *ns = netdev_priv(xs->xso.dev); + struct nsim_ipsec *ipsec = &ns->ipsec; + + ipsec->ok++; + + return true; +} + +static const struct xfrmdev_ops nsim_xfrmdev_ops = { + .xdo_dev_state_add = nsim_ipsec_add_sa, + .xdo_dev_state_delete = nsim_ipsec_del_sa, + .xdo_dev_offload_ok = nsim_ipsec_offload_ok, +}; + +bool nsim_ipsec_tx(struct netdevsim *ns, struct sk_buff *skb) +{ + struct nsim_ipsec *ipsec = &ns->ipsec; + struct xfrm_state *xs; + struct nsim_sa *tsa; + u32 sa_idx; + + /* do we even need to check this packet? */ + if (!skb->sp) + return true; + + if (unlikely(!skb->sp->len)) { + netdev_err(ns->netdev, "no xfrm state len = %d\n", + skb->sp->len); + return false; + } + + xs = xfrm_input_state(skb); + if (unlikely(!xs)) { + netdev_err(ns->netdev, "no xfrm_input_state() xs = %p\n", xs); + return false; + } + + sa_idx = xs->xso.offload_handle & ~NSIM_IPSEC_VALID; + if (unlikely(sa_idx >= NSIM_IPSEC_MAX_SA_COUNT)) { + netdev_err(ns->netdev, "bad sa_idx=%d max=%d\n", + sa_idx, NSIM_IPSEC_MAX_SA_COUNT); + return false; + } + + tsa = &ipsec->sa[sa_idx]; + if (unlikely(!tsa->used)) { + netdev_err(ns->netdev, "unused sa_idx=%d\n", sa_idx); + return false; + } + + if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) { + netdev_err(ns->netdev, "unexpected proto=%d\n", xs->id.proto); + return false; + } + + ipsec->tx++; + + return true; +} + +void nsim_ipsec_init(struct netdevsim *ns) +{ + ns->netdev->xfrmdev_ops = &nsim_xfrmdev_ops; + +#define NSIM_ESP_FEATURES (NETIF_F_HW_ESP | \ + NETIF_F_HW_ESP_TX_CSUM | \ + NETIF_F_GSO_ESP) + + ns->netdev->features |= NSIM_ESP_FEATURES; + ns->netdev->hw_enc_features |= NSIM_ESP_FEATURES; + + ns->ipsec.pfile = debugfs_create_file("ipsec", 0400, ns->ddir, ns, + &ipsec_dbg_fops); +} + +void nsim_ipsec_teardown(struct netdevsim *ns) +{ + struct nsim_ipsec *ipsec = &ns->ipsec; + + if (ipsec->count) + netdev_err(ns->netdev, "tearing down IPsec offload with %d SAs left\n", + ipsec->count); + debugfs_remove_recursive(ipsec->pfile); +} diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index ec68f38213d9..8d8e2b3f263e 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -22,6 +22,7 @@ #include <net/netlink.h> #include <net/pkt_cls.h> #include <net/rtnetlink.h> +#include <net/switchdev.h> #include "netdevsim.h" @@ -40,6 +41,9 @@ struct nsim_vf_config { static u32 nsim_dev_id; +static struct dentry *nsim_ddir; +static struct dentry *nsim_sdev_ddir; + static int nsim_num_vf(struct device *dev) { struct netdevsim *ns = to_nsim(dev); @@ -144,8 +148,29 @@ static struct device_type nsim_dev_type = { .release = nsim_dev_release, }; +static int +nsim_port_attr_get(struct net_device *dev, struct switchdev_attr *attr) +{ + struct netdevsim *ns = netdev_priv(dev); + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: + attr->u.ppid.id_len = sizeof(ns->sdev->switch_id); + memcpy(&attr->u.ppid.id, &ns->sdev->switch_id, + attr->u.ppid.id_len); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static const struct switchdev_ops nsim_switchdev_ops = { + .switchdev_port_attr_get = nsim_port_attr_get, +}; + static int nsim_init(struct net_device *dev) { + char sdev_ddir_name[10], sdev_link_name[32]; struct netdevsim *ns = netdev_priv(dev); int err; @@ -154,9 +179,32 @@ static int nsim_init(struct net_device *dev) if (IS_ERR_OR_NULL(ns->ddir)) return -ENOMEM; + if (!ns->sdev) { + ns->sdev = kzalloc(sizeof(*ns->sdev), GFP_KERNEL); + if (!ns->sdev) { + err = -ENOMEM; + goto err_debugfs_destroy; + } + ns->sdev->refcnt = 1; + ns->sdev->switch_id = nsim_dev_id; + sprintf(sdev_ddir_name, "%u", ns->sdev->switch_id); + ns->sdev->ddir = debugfs_create_dir(sdev_ddir_name, + nsim_sdev_ddir); + if (IS_ERR_OR_NULL(ns->sdev->ddir)) { + err = PTR_ERR_OR_ZERO(ns->sdev->ddir) ?: -EINVAL; + goto err_sdev_free; + } + } else { + sprintf(sdev_ddir_name, "%u", ns->sdev->switch_id); + ns->sdev->refcnt++; + } + + sprintf(sdev_link_name, "../../" DRV_NAME "_sdev/%s", sdev_ddir_name); + debugfs_create_symlink("sdev", ns->ddir, sdev_link_name); + err = nsim_bpf_init(ns); if (err) - goto err_debugfs_destroy; + goto err_sdev_destroy; ns->dev.id = nsim_dev_id++; ns->dev.bus = &nsim_bus; @@ -166,17 +214,26 @@ static int nsim_init(struct net_device *dev) goto err_bpf_uninit; SET_NETDEV_DEV(dev, &ns->dev); + SWITCHDEV_SET_OPS(dev, &nsim_switchdev_ops); err = nsim_devlink_setup(ns); if (err) goto err_unreg_dev; + nsim_ipsec_init(ns); + return 0; err_unreg_dev: device_unregister(&ns->dev); err_bpf_uninit: nsim_bpf_uninit(ns); +err_sdev_destroy: + if (!--ns->sdev->refcnt) { + debugfs_remove_recursive(ns->sdev->ddir); +err_sdev_free: + kfree(ns->sdev); + } err_debugfs_destroy: debugfs_remove_recursive(ns->ddir); return err; @@ -186,9 +243,14 @@ static void nsim_uninit(struct net_device *dev) { struct netdevsim *ns = netdev_priv(dev); + nsim_ipsec_teardown(ns); nsim_devlink_teardown(ns); debugfs_remove_recursive(ns->ddir); nsim_bpf_uninit(ns); + if (!--ns->sdev->refcnt) { + debugfs_remove_recursive(ns->sdev->ddir); + kfree(ns->sdev); + } } static void nsim_free(struct net_device *dev) @@ -203,11 +265,15 @@ static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct netdevsim *ns = netdev_priv(dev); + if (!nsim_ipsec_tx(ns, skb)) + goto out; + u64_stats_update_begin(&ns->syncp); ns->tx_packets++; ns->tx_bytes += skb->len; u64_stats_update_end(&ns->syncp); +out: dev_kfree_skb(skb); return NETDEV_TX_OK; @@ -221,8 +287,7 @@ static int nsim_change_mtu(struct net_device *dev, int new_mtu) { struct netdevsim *ns = netdev_priv(dev); - if (ns->xdp_prog_mode == XDP_ATTACHED_DRV && - new_mtu > NSIM_XDP_MAX_MTU) + if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU) return -EBUSY; dev->mtu = new_mtu; @@ -260,7 +325,7 @@ nsim_setup_tc_block(struct net_device *dev, struct tc_block_offload *f) switch (f->command) { case TC_BLOCK_BIND: return tcf_block_cb_register(f->block, nsim_setup_tc_block_cb, - ns, ns); + ns, ns, f->extack); case TC_BLOCK_UNBIND: tcf_block_cb_unregister(f->block, nsim_setup_tc_block_cb, ns); return 0; @@ -464,15 +529,46 @@ static int nsim_validate(struct nlattr *tb[], struct nlattr *data[], return 0; } +static int nsim_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct netdevsim *ns = netdev_priv(dev); + + if (tb[IFLA_LINK]) { + struct net_device *joindev; + struct netdevsim *joinns; + + joindev = __dev_get_by_index(src_net, + nla_get_u32(tb[IFLA_LINK])); + if (!joindev) + return -ENODEV; + if (joindev->netdev_ops != &nsim_netdev_ops) + return -EINVAL; + + joinns = netdev_priv(joindev); + if (!joinns->sdev || !joinns->sdev->refcnt) + return -EINVAL; + ns->sdev = joinns->sdev; + } + + return register_netdevice(dev); +} + +static void nsim_dellink(struct net_device *dev, struct list_head *head) +{ + unregister_netdevice_queue(dev, head); +} + static struct rtnl_link_ops nsim_link_ops __read_mostly = { .kind = DRV_NAME, .priv_size = sizeof(struct netdevsim), .setup = nsim_setup, .validate = nsim_validate, + .newlink = nsim_newlink, + .dellink = nsim_dellink, }; -struct dentry *nsim_ddir; - static int __init nsim_module_init(void) { int err; @@ -481,9 +577,15 @@ static int __init nsim_module_init(void) if (IS_ERR_OR_NULL(nsim_ddir)) return -ENOMEM; + nsim_sdev_ddir = debugfs_create_dir(DRV_NAME "_sdev", NULL); + if (IS_ERR_OR_NULL(nsim_sdev_ddir)) { + err = -ENOMEM; + goto err_debugfs_destroy; + } + err = bus_register(&nsim_bus); if (err) - goto err_debugfs_destroy; + goto err_sdir_destroy; err = nsim_devlink_init(); if (err) @@ -499,6 +601,8 @@ err_dl_fini: nsim_devlink_exit(); err_unreg_bus: bus_unregister(&nsim_bus); +err_sdir_destroy: + debugfs_remove_recursive(nsim_sdev_ddir); err_debugfs_destroy: debugfs_remove_recursive(nsim_ddir); return err; @@ -509,6 +613,7 @@ static void __exit nsim_module_exit(void) rtnl_link_unregister(&nsim_link_ops); nsim_devlink_exit(); bus_unregister(&nsim_bus); + debugfs_remove_recursive(nsim_sdev_ddir); debugfs_remove_recursive(nsim_ddir); } diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 8ca50b72c328..384c254fafc5 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -18,6 +18,7 @@ #include <linux/list.h> #include <linux/netdevice.h> #include <linux/u64_stats_sync.h> +#include <net/xdp.h> #define DRV_NAME "netdevsim" @@ -26,9 +27,46 @@ #define NSIM_EA(extack, msg) NL_SET_ERR_MSG_MOD((extack), msg) struct bpf_prog; +struct bpf_offload_dev; struct dentry; struct nsim_vf_config; +struct netdevsim_shared_dev { + unsigned int refcnt; + u32 switch_id; + + struct dentry *ddir; + + struct bpf_offload_dev *bpf_dev; + + struct dentry *ddir_bpf_bound_progs; + u32 prog_id_gen; + + struct list_head bpf_bound_progs; + struct list_head bpf_bound_maps; +}; + +#define NSIM_IPSEC_MAX_SA_COUNT 33 +#define NSIM_IPSEC_VALID BIT(31) + +struct nsim_sa { + struct xfrm_state *xs; + __be32 ipaddr[4]; + u32 key[4]; + u32 salt; + bool used; + bool crypt; + bool rx; +}; + +struct nsim_ipsec { + struct nsim_sa sa[NSIM_IPSEC_MAX_SA_COUNT]; + struct dentry *pfile; + u32 count; + u32 tx; + u32 ok; +}; + struct netdevsim { struct net_device *netdev; @@ -37,6 +75,7 @@ struct netdevsim { struct u64_stats_sync syncp; struct device dev; + struct netdevsim_shared_dev *sdev; struct dentry *ddir; @@ -46,16 +85,11 @@ struct netdevsim { struct bpf_prog *bpf_offloaded; u32 bpf_offloaded_id; - u32 xdp_flags; - int xdp_prog_mode; - struct bpf_prog *xdp_prog; - - u32 prog_id_gen; + struct xdp_attachment_info xdp; + struct xdp_attachment_info xdp_hw; bool bpf_bind_accept; u32 bpf_bind_verifier_delay; - struct dentry *ddir_bpf_bound_progs; - struct list_head bpf_bound_progs; bool bpf_tc_accept; bool bpf_tc_non_bound_accept; @@ -63,14 +97,12 @@ struct netdevsim { bool bpf_xdpoffload_accept; bool bpf_map_accept; - struct list_head bpf_bound_maps; #if IS_ENABLED(CONFIG_NET_DEVLINK) struct devlink *devlink; #endif + struct nsim_ipsec ipsec; }; -extern struct dentry *nsim_ddir; - #ifdef CONFIG_BPF_SYSCALL int nsim_bpf_init(struct netdevsim *ns); void nsim_bpf_uninit(struct netdevsim *ns); @@ -148,6 +180,25 @@ static inline void nsim_devlink_exit(void) } #endif +#if IS_ENABLED(CONFIG_XFRM_OFFLOAD) +void nsim_ipsec_init(struct netdevsim *ns); +void nsim_ipsec_teardown(struct netdevsim *ns); +bool nsim_ipsec_tx(struct netdevsim *ns, struct sk_buff *skb); +#else +static inline void nsim_ipsec_init(struct netdevsim *ns) +{ +} + +static inline void nsim_ipsec_teardown(struct netdevsim *ns) +{ +} + +static inline bool nsim_ipsec_tx(struct netdevsim *ns, struct sk_buff *skb) +{ + return true; +} +#endif + static inline struct netdevsim *to_nsim(struct device *ptr) { return container_of(ptr, struct netdevsim, dev); |
