Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'psp-add-a-kselftest-suite-and-netdevsim-implementation'

Jakub Kicinski says:

====================
psp: add a kselftest suite and netdevsim implementation

Add a basic test suite for drivers that support PSP. Also, add a PSP
implementation in the netdevsim driver.

The netdevsim implementation does encapsulation and decapsulation of
PSP packets, but no crypto.

The tests cover the basic usage of the uapi, and demonstrate key
exchange and connection setup. The tests and netdevsim support IPv4
and IPv6. Here is an example run on a system with a CX7 NIC.

TAP version 13
1..28
ok 1 psp.data_basic_send_v0_ip4
ok 2 psp.data_basic_send_v0_ip6
ok 3 psp.data_basic_send_v1_ip4
ok 4 psp.data_basic_send_v1_ip6
ok 5 psp.data_basic_send_v2_ip4 # SKIP ('PSP version not supported', 'hdr0-aes-gmac-128')
ok 6 psp.data_basic_send_v2_ip6 # SKIP ('PSP version not supported', 'hdr0-aes-gmac-128')
ok 7 psp.data_basic_send_v3_ip4 # SKIP ('PSP version not supported', 'hdr0-aes-gmac-256')
ok 8 psp.data_basic_send_v3_ip6 # SKIP ('PSP version not supported', 'hdr0-aes-gmac-256')
ok 9 psp.data_mss_adjust_ip4
ok 10 psp.data_mss_adjust_ip6
ok 11 psp.dev_list_devices
ok 12 psp.dev_get_device
ok 13 psp.dev_get_device_bad
ok 14 psp.dev_rotate
ok 15 psp.dev_rotate_spi
ok 16 psp.assoc_basic
ok 17 psp.assoc_bad_dev
ok 18 psp.assoc_sk_only_conn
ok 19 psp.assoc_sk_only_mismatch
ok 20 psp.assoc_sk_only_mismatch_tx
ok 21 psp.assoc_sk_only_unconn
ok 22 psp.assoc_version_mismatch
ok 23 psp.assoc_twice
ok 24 psp.data_send_bad_key
ok 25 psp.data_send_disconnect
ok 26 psp.data_stale_key
ok 27 psp.removal_device_rx # XFAIL Test only works on netdevsim
ok 28 psp.removal_device_bi # XFAIL Test only works on netdevsim
# Totals: pass:22 fail:0 xfail:2 xpass:0 skip:4 error:0
#
# Responder logs (0):
# STDERR:
# Set PSP enable on device 1 to 0x3
# Set PSP enable on device 1 to 0x0

v2: https://lore.kernel.org/20250925211647.3450332-1-daniel.zahka@gmail.com
v1: https://lore.kernel.org/20250924194959.2845473-1-daniel.zahka@gmail.com
====================

Link: https://patch.msgid.link/20250927225420.1443468-1-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+1440 -11
+4
drivers/net/netdevsim/Makefile
··· 18 18 netdevsim-objs += psample.o 19 19 endif 20 20 21 + ifneq ($(CONFIG_INET_PSP),) 22 + netdevsim-objs += psp.o 23 + endif 24 + 21 25 ifneq ($(CONFIG_MACSEC),) 22 26 netdevsim-objs += macsec.o 23 27 endif
+37 -6
drivers/net/netdevsim/netdev.c
··· 103 103 static int nsim_forward_skb(struct net_device *tx_dev, 104 104 struct net_device *rx_dev, 105 105 struct sk_buff *skb, 106 - struct nsim_rq *rq) 106 + struct nsim_rq *rq, 107 + struct skb_ext *psp_ext) 107 108 { 108 - return __dev_forward_skb(rx_dev, skb) ?: 109 - nsim_napi_rx(tx_dev, rx_dev, rq, skb); 109 + int ret; 110 + 111 + ret = __dev_forward_skb(rx_dev, skb); 112 + if (ret) 113 + return ret; 114 + 115 + nsim_psp_handle_ext(skb, psp_ext); 116 + 117 + return nsim_napi_rx(tx_dev, rx_dev, rq, skb); 110 118 } 111 119 112 120 static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) 113 121 { 114 122 struct netdevsim *ns = netdev_priv(dev); 123 + struct skb_ext *psp_ext = NULL; 115 124 struct net_device *peer_dev; 116 125 unsigned int len = skb->len; 117 126 struct netdevsim *peer_ns; 118 127 struct netdev_config *cfg; 119 128 struct nsim_rq *rq; 120 129 int rxq; 130 + int dr; 121 131 122 132 rcu_read_lock(); 123 133 if (!nsim_ipsec_tx(ns, skb)) 124 - goto out_drop_free; 134 + goto out_drop_any; 125 135 126 136 peer_ns = rcu_dereference(ns->peer); 127 137 if (!peer_ns) 138 + goto out_drop_any; 139 + 140 + dr = nsim_do_psp(skb, ns, peer_ns, &psp_ext); 141 + if (dr) 128 142 goto out_drop_free; 129 143 130 144 peer_dev = peer_ns->netdev; ··· 155 141 skb_linearize(skb); 156 142 157 143 skb_tx_timestamp(skb); 158 - if (unlikely(nsim_forward_skb(dev, peer_dev, skb, rq) == NET_RX_DROP)) 144 + if (unlikely(nsim_forward_skb(dev, peer_dev, 145 + skb, rq, psp_ext) == NET_RX_DROP)) 159 146 goto out_drop_cnt; 160 147 161 148 if (!hrtimer_active(&rq->napi_timer)) ··· 166 151 dev_dstats_tx_add(dev, len); 167 152 return NETDEV_TX_OK; 168 153 154 + out_drop_any: 155 + dr = SKB_DROP_REASON_NOT_SPECIFIED; 169 156 out_drop_free: 170 - dev_kfree_skb(skb); 157 + kfree_skb_reason(skb, dr); 171 158 out_drop_cnt: 172 159 rcu_read_unlock(); 173 160 dev_dstats_tx_dropped(dev); ··· 1019 1002 1020 1003 static int nsim_init_netdevsim(struct netdevsim *ns) 1021 1004 { 1005 + struct netdevsim *peer; 1022 1006 struct mock_phc *phc; 1023 1007 int err; 1024 1008 ··· 1054 1036 goto err_ipsec_teardown; 1055 1037 rtnl_unlock(); 1056 1038 1039 + err = nsim_psp_init(ns); 1040 + if (err) 1041 + goto err_unregister_netdev; 1042 + 1057 1043 if (IS_ENABLED(CONFIG_DEBUG_NET)) { 1058 1044 ns->nb.notifier_call = netdev_debug_event; 1059 1045 if (register_netdevice_notifier_dev_net(ns->netdev, &ns->nb, ··· 1067 1045 1068 1046 return 0; 1069 1047 1048 + err_unregister_netdev: 1049 + rtnl_lock(); 1050 + peer = rtnl_dereference(ns->peer); 1051 + if (peer) 1052 + RCU_INIT_POINTER(peer->peer, NULL); 1053 + RCU_INIT_POINTER(ns->peer, NULL); 1054 + unregister_netdevice(ns->netdev); 1070 1055 err_ipsec_teardown: 1071 1056 nsim_ipsec_teardown(ns); 1072 1057 nsim_macsec_teardown(ns); ··· 1160 1131 if (ns->nb.notifier_call) 1161 1132 unregister_netdevice_notifier_dev_net(ns->netdev, &ns->nb, 1162 1133 &ns->nn); 1134 + 1135 + nsim_psp_uninit(ns); 1163 1136 1164 1137 rtnl_lock(); 1165 1138 peer = rtnl_dereference(ns->peer);
+27
drivers/net/netdevsim/netdevsim.h
··· 108 108 109 109 int rq_reset_mode; 110 110 111 + struct { 112 + struct psp_dev *dev; 113 + u32 spi; 114 + u32 assoc_cnt; 115 + } psp; 116 + 111 117 struct nsim_bus_dev *nsim_bus_dev; 112 118 113 119 struct bpf_prog *bpf_offloaded; ··· 425 419 static inline void nsim_macsec_teardown(struct netdevsim *ns) 426 420 { 427 421 } 422 + #endif 423 + 424 + #if IS_ENABLED(CONFIG_INET_PSP) 425 + int nsim_psp_init(struct netdevsim *ns); 426 + void nsim_psp_uninit(struct netdevsim *ns); 427 + void nsim_psp_handle_ext(struct sk_buff *skb, struct skb_ext *psp_ext); 428 + enum skb_drop_reason 429 + nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, 430 + struct netdevsim *peer_ns, struct skb_ext **psp_ext); 431 + #else 432 + static inline int nsim_psp_init(struct netdevsim *ns) { return 0; } 433 + static inline void nsim_psp_uninit(struct netdevsim *ns) {} 434 + static inline enum skb_drop_reason 435 + nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, 436 + struct netdevsim *peer_ns, struct skb_ext **psp_ext) 437 + { 438 + return 0; 439 + } 440 + 441 + static inline void 442 + nsim_psp_handle_ext(struct sk_buff *skb, struct skb_ext *psp_ext) {} 428 443 #endif 429 444 430 445 struct nsim_bus_dev {
+225
drivers/net/netdevsim/psp.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/ip.h> 4 + #include <linux/skbuff.h> 5 + #include <net/ip6_checksum.h> 6 + #include <net/psp.h> 7 + #include <net/sock.h> 8 + 9 + #include "netdevsim.h" 10 + 11 + void nsim_psp_handle_ext(struct sk_buff *skb, struct skb_ext *psp_ext) 12 + { 13 + if (psp_ext) 14 + __skb_ext_set(skb, SKB_EXT_PSP, psp_ext); 15 + } 16 + 17 + enum skb_drop_reason 18 + nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, 19 + struct netdevsim *peer_ns, struct skb_ext **psp_ext) 20 + { 21 + enum skb_drop_reason rc = 0; 22 + struct psp_assoc *pas; 23 + struct net *net; 24 + void **ptr; 25 + 26 + rcu_read_lock(); 27 + pas = psp_skb_get_assoc_rcu(skb); 28 + if (!pas) { 29 + rc = SKB_NOT_DROPPED_YET; 30 + goto out_unlock; 31 + } 32 + 33 + if (!skb_transport_header_was_set(skb)) { 34 + rc = SKB_DROP_REASON_PSP_OUTPUT; 35 + goto out_unlock; 36 + } 37 + 38 + ptr = psp_assoc_drv_data(pas); 39 + if (*ptr != ns) { 40 + rc = SKB_DROP_REASON_PSP_OUTPUT; 41 + goto out_unlock; 42 + } 43 + 44 + net = sock_net(skb->sk); 45 + if (!psp_dev_encapsulate(net, skb, pas->tx.spi, pas->version, 0)) { 46 + rc = SKB_DROP_REASON_PSP_OUTPUT; 47 + goto out_unlock; 48 + } 49 + 50 + /* Now pretend we just received this frame */ 51 + if (peer_ns->psp.dev->config.versions & (1 << pas->version)) { 52 + bool strip_icv = false; 53 + u8 generation; 54 + 55 + /* We cheat a bit and put the generation in the key. 56 + * In real life if generation was too old, then decryption would 57 + * fail. Here, we just make it so a bad key causes a bad 58 + * generation too, and psp_sk_rx_policy_check() will fail. 59 + */ 60 + generation = pas->tx.key[0]; 61 + 62 + skb_ext_reset(skb); 63 + skb->mac_len = ETH_HLEN; 64 + if (psp_dev_rcv(skb, peer_ns->psp.dev->id, generation, 65 + strip_icv)) { 66 + rc = SKB_DROP_REASON_PSP_OUTPUT; 67 + goto out_unlock; 68 + } 69 + 70 + *psp_ext = skb->extensions; 71 + refcount_inc(&(*psp_ext)->refcnt); 72 + skb->decrypted = 1; 73 + } else { 74 + struct ipv6hdr *ip6h __maybe_unused; 75 + struct iphdr *iph; 76 + struct udphdr *uh; 77 + __wsum csum; 78 + 79 + /* Do not decapsulate. Receive the skb with the udp and psp 80 + * headers still there as if this is a normal udp packet. 81 + * psp_dev_encapsulate() sets udp checksum to 0, so we need to 82 + * provide a valid checksum here, so the skb isn't dropped. 83 + */ 84 + uh = udp_hdr(skb); 85 + csum = skb_checksum(skb, skb_transport_offset(skb), 86 + ntohs(uh->len), 0); 87 + 88 + switch (skb->protocol) { 89 + case htons(ETH_P_IP): 90 + iph = ip_hdr(skb); 91 + uh->check = udp_v4_check(ntohs(uh->len), iph->saddr, 92 + iph->daddr, csum); 93 + break; 94 + #if IS_ENABLED(CONFIG_IPV6) 95 + case htons(ETH_P_IPV6): 96 + ip6h = ipv6_hdr(skb); 97 + uh->check = udp_v6_check(ntohs(uh->len), &ip6h->saddr, 98 + &ip6h->daddr, csum); 99 + break; 100 + #endif 101 + } 102 + 103 + uh->check = uh->check ?: CSUM_MANGLED_0; 104 + skb->ip_summed = CHECKSUM_NONE; 105 + } 106 + 107 + out_unlock: 108 + rcu_read_unlock(); 109 + return rc; 110 + } 111 + 112 + static int 113 + nsim_psp_set_config(struct psp_dev *psd, struct psp_dev_config *conf, 114 + struct netlink_ext_ack *extack) 115 + { 116 + return 0; 117 + } 118 + 119 + static int 120 + nsim_rx_spi_alloc(struct psp_dev *psd, u32 version, 121 + struct psp_key_parsed *assoc, 122 + struct netlink_ext_ack *extack) 123 + { 124 + struct netdevsim *ns = psd->drv_priv; 125 + unsigned int new; 126 + int i; 127 + 128 + new = ++ns->psp.spi & PSP_SPI_KEY_ID; 129 + if (psd->generation & 1) 130 + new |= PSP_SPI_KEY_PHASE; 131 + 132 + assoc->spi = cpu_to_be32(new); 133 + assoc->key[0] = psd->generation; 134 + for (i = 1; i < PSP_MAX_KEY; i++) 135 + assoc->key[i] = ns->psp.spi + i; 136 + 137 + return 0; 138 + } 139 + 140 + static int nsim_assoc_add(struct psp_dev *psd, struct psp_assoc *pas, 141 + struct netlink_ext_ack *extack) 142 + { 143 + struct netdevsim *ns = psd->drv_priv; 144 + void **ptr = psp_assoc_drv_data(pas); 145 + 146 + /* Copy drv_priv from psd to assoc */ 147 + *ptr = psd->drv_priv; 148 + ns->psp.assoc_cnt++; 149 + 150 + return 0; 151 + } 152 + 153 + static int nsim_key_rotate(struct psp_dev *psd, struct netlink_ext_ack *extack) 154 + { 155 + return 0; 156 + } 157 + 158 + static void nsim_assoc_del(struct psp_dev *psd, struct psp_assoc *pas) 159 + { 160 + struct netdevsim *ns = psd->drv_priv; 161 + void **ptr = psp_assoc_drv_data(pas); 162 + 163 + *ptr = NULL; 164 + ns->psp.assoc_cnt--; 165 + } 166 + 167 + static struct psp_dev_ops nsim_psp_ops = { 168 + .set_config = nsim_psp_set_config, 169 + .rx_spi_alloc = nsim_rx_spi_alloc, 170 + .tx_key_add = nsim_assoc_add, 171 + .tx_key_del = nsim_assoc_del, 172 + .key_rotate = nsim_key_rotate, 173 + }; 174 + 175 + static struct psp_dev_caps nsim_psp_caps = { 176 + .versions = 1 << PSP_VERSION_HDR0_AES_GCM_128 | 177 + 1 << PSP_VERSION_HDR0_AES_GMAC_128 | 178 + 1 << PSP_VERSION_HDR0_AES_GCM_256 | 179 + 1 << PSP_VERSION_HDR0_AES_GMAC_256, 180 + .assoc_drv_spc = sizeof(void *), 181 + }; 182 + 183 + void nsim_psp_uninit(struct netdevsim *ns) 184 + { 185 + if (!IS_ERR(ns->psp.dev)) 186 + psp_dev_unregister(ns->psp.dev); 187 + WARN_ON(ns->psp.assoc_cnt); 188 + } 189 + 190 + static ssize_t 191 + nsim_psp_rereg_write(struct file *file, const char __user *data, size_t count, 192 + loff_t *ppos) 193 + { 194 + struct netdevsim *ns = file->private_data; 195 + int err; 196 + 197 + nsim_psp_uninit(ns); 198 + 199 + ns->psp.dev = psp_dev_create(ns->netdev, &nsim_psp_ops, 200 + &nsim_psp_caps, ns); 201 + err = PTR_ERR_OR_ZERO(ns->psp.dev); 202 + return err ?: count; 203 + } 204 + 205 + static const struct file_operations nsim_psp_rereg_fops = { 206 + .open = simple_open, 207 + .write = nsim_psp_rereg_write, 208 + .llseek = generic_file_llseek, 209 + .owner = THIS_MODULE, 210 + }; 211 + 212 + int nsim_psp_init(struct netdevsim *ns) 213 + { 214 + struct dentry *ddir = ns->nsim_dev_port->ddir; 215 + int err; 216 + 217 + ns->psp.dev = psp_dev_create(ns->netdev, &nsim_psp_ops, 218 + &nsim_psp_caps, ns); 219 + err = PTR_ERR_OR_ZERO(ns->psp.dev); 220 + if (err) 221 + return err; 222 + 223 + debugfs_create_file("psp_rereg", 0200, ddir, ns, &nsim_psp_rereg_fops); 224 + return 0; 225 + }
+1
net/core/skbuff.c
··· 7048 7048 skb->active_extensions = 1 << id; 7049 7049 return skb_ext_get_ptr(ext, id); 7050 7050 } 7051 + EXPORT_SYMBOL_NS_GPL(__skb_ext_set, "NETDEV_INTERNAL"); 7051 7052 7052 7053 /** 7053 7054 * skb_ext_add - allocate space for given extension, COW if needed
+1
tools/testing/selftests/drivers/net/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 napi_id_helper 3 + psp_responder
+10
tools/testing/selftests/drivers/net/Makefile
··· 19 19 netcons_sysdata.sh \ 20 20 netpoll_basic.py \ 21 21 ping.py \ 22 + psp.py \ 22 23 queues.py \ 23 24 stats.py \ 24 25 shaper.py \ ··· 27 26 xdp.py \ 28 27 # end of TEST_PROGS 29 28 29 + # YNL files, must be before "include ..lib.mk" 30 + YNL_GEN_FILES := psp_responder 31 + TEST_GEN_FILES += $(YNL_GEN_FILES) 32 + 30 33 include ../../lib.mk 34 + 35 + # YNL build 36 + YNL_GENS := psp 37 + 38 + include ../../net/ynl.mk
+1
tools/testing/selftests/drivers/net/config
··· 1 1 CONFIG_CONFIGFS_FS=y 2 2 CONFIG_DEBUG_INFO_BTF=y 3 3 CONFIG_DEBUG_INFO_BTF_MODULES=n 4 + CONFIG_INET_PSP=y 4 5 CONFIG_IPV6=y 5 6 CONFIG_NETDEVSIM=m 6 7 CONFIG_NETCONSOLE=m
+2 -2
tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
··· 13 13 14 14 # Import one by one to avoid pylint false positives 15 15 from net.lib.py import EthtoolFamily, NetdevFamily, NetshaperFamily, \ 16 - NlError, RtnlFamily, DevlinkFamily 16 + NlError, RtnlFamily, DevlinkFamily, PSPFamily 17 17 from net.lib.py import CmdExitFailure 18 18 from net.lib.py import bkg, cmd, defer, ethtool, fd_read_timeout, ip, \ 19 19 rand_port, tool, wait_port_listen ··· 22 22 from net.lib.py import ksft_disruptive, ksft_exit, ksft_pr, ksft_run, \ 23 23 ksft_setup 24 24 from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \ 25 - ksft_ne, ksft_not_in, ksft_raises, ksft_true 25 + ksft_ne, ksft_not_in, ksft_raises, ksft_true, ksft_gt, ksft_not_none 26 26 from net.lib.py import NetNSEnter 27 27 from drivers.net.lib.py import GenerateTraffic 28 28 from drivers.net.lib.py import NetDrvEnv, NetDrvEpEnv
+2 -2
tools/testing/selftests/drivers/net/lib/py/__init__.py
··· 12 12 13 13 # Import one by one to avoid pylint false positives 14 14 from net.lib.py import EthtoolFamily, NetdevFamily, NetshaperFamily, \ 15 - NlError, RtnlFamily, DevlinkFamily 15 + NlError, RtnlFamily, DevlinkFamily, PSPFamily 16 16 from net.lib.py import CmdExitFailure 17 17 from net.lib.py import bkg, cmd, bpftool, bpftrace, defer, ethtool, \ 18 18 fd_read_timeout, ip, rand_port, tool, wait_port_listen, wait_file ··· 21 21 from net.lib.py import ksft_disruptive, ksft_exit, ksft_pr, ksft_run, \ 22 22 ksft_setup 23 23 from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \ 24 - ksft_ne, ksft_not_in, ksft_raises, ksft_true 24 + ksft_ne, ksft_not_in, ksft_raises, ksft_true, ksft_gt, ksft_not_none 25 25 except ModuleNotFoundError as e: 26 26 ksft_pr("Failed importing `net` library from kernel sources") 27 27 ksft_pr(str(e))
+4
tools/testing/selftests/drivers/net/lib/py/env.py
··· 245 245 if not self.addr_v[ipver] or not self.remote_addr_v[ipver]: 246 246 raise KsftSkipEx(f"Test requires IPv{ipver} connectivity") 247 247 248 + def require_nsim(self): 249 + if self._ns is None: 250 + raise KsftXfailEx("Test only works on netdevsim") 251 + 248 252 def _require_cmd(self, comm, key, host=None): 249 253 cached = self._required_cmd.get(comm, {}) 250 254 if cached.get(key) is None:
+627
tools/testing/selftests/drivers/net/psp.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + """Test suite for PSP capable drivers.""" 5 + 6 + import errno 7 + import fcntl 8 + import socket 9 + import struct 10 + import termios 11 + import time 12 + 13 + from lib.py import defer 14 + from lib.py import ksft_run, ksft_exit, ksft_pr 15 + from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises 16 + from lib.py import ksft_not_none 17 + from lib.py import KsftSkipEx 18 + from lib.py import NetDrvEpEnv, PSPFamily, NlError 19 + from lib.py import bkg, rand_port, wait_port_listen 20 + 21 + 22 + def _get_outq(s): 23 + one = b'\0' * 4 24 + outq = fcntl.ioctl(s.fileno(), termios.TIOCOUTQ, one) 25 + return struct.unpack("I", outq)[0] 26 + 27 + 28 + def _send_with_ack(cfg, msg): 29 + cfg.comm_sock.send(msg) 30 + response = cfg.comm_sock.recv(4) 31 + if response != b'ack\0': 32 + raise RuntimeError("Unexpected server response", response) 33 + 34 + 35 + def _remote_read_len(cfg): 36 + cfg.comm_sock.send(b'read len\0') 37 + return int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8')) 38 + 39 + 40 + def _make_clr_conn(cfg, ipver=None): 41 + _send_with_ack(cfg, b'conn clr\0') 42 + remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr 43 + s = socket.create_connection((remote_addr, cfg.comm_port), ) 44 + return s 45 + 46 + 47 + def _make_psp_conn(cfg, version=0, ipver=None): 48 + _send_with_ack(cfg, b'conn psp\0' + struct.pack('BB', version, version)) 49 + remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr 50 + s = socket.create_connection((remote_addr, cfg.comm_port), ) 51 + return s 52 + 53 + 54 + def _close_conn(cfg, s): 55 + _send_with_ack(cfg, b'data close\0') 56 + s.close() 57 + 58 + 59 + def _close_psp_conn(cfg, s): 60 + _close_conn(cfg, s) 61 + 62 + 63 + def _spi_xchg(s, rx): 64 + s.send(struct.pack('I', rx['spi']) + rx['key']) 65 + tx = s.recv(4 + len(rx['key'])) 66 + return { 67 + 'spi': struct.unpack('I', tx[:4])[0], 68 + 'key': tx[4:] 69 + } 70 + 71 + 72 + def _send_careful(cfg, s, rounds): 73 + data = b'0123456789' * 200 74 + for i in range(rounds): 75 + n = 0 76 + for _ in range(10): # allow 10 retries 77 + try: 78 + n += s.send(data[n:], socket.MSG_DONTWAIT) 79 + if n == len(data): 80 + break 81 + except BlockingIOError: 82 + time.sleep(0.05) 83 + else: 84 + rlen = _remote_read_len(cfg) 85 + outq = _get_outq(s) 86 + report = f'sent: {i * len(data) + n} remote len: {rlen} outq: {outq}' 87 + raise RuntimeError(report) 88 + 89 + return len(data) * rounds 90 + 91 + 92 + def _check_data_rx(cfg, exp_len): 93 + read_len = -1 94 + for _ in range(30): 95 + cfg.comm_sock.send(b'read len\0') 96 + read_len = int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8')) 97 + if read_len == exp_len: 98 + break 99 + time.sleep(0.01) 100 + ksft_eq(read_len, exp_len) 101 + 102 + 103 + def _check_data_outq(s, exp_len, force_wait=False): 104 + outq = 0 105 + for _ in range(10): 106 + outq = _get_outq(s) 107 + if not force_wait and outq == exp_len: 108 + break 109 + time.sleep(0.01) 110 + ksft_eq(outq, exp_len) 111 + 112 + # 113 + # Test case boiler plate 114 + # 115 + 116 + def _init_psp_dev(cfg): 117 + if not hasattr(cfg, 'psp_dev_id'): 118 + # Figure out which local device we are testing against 119 + for dev in cfg.pspnl.dev_get({}, dump=True): 120 + if dev['ifindex'] == cfg.ifindex: 121 + cfg.psp_info = dev 122 + cfg.psp_dev_id = cfg.psp_info['id'] 123 + break 124 + else: 125 + raise KsftSkipEx("No PSP devices found") 126 + 127 + # Enable PSP if necessary 128 + cap = cfg.psp_info['psp-versions-cap'] 129 + ena = cfg.psp_info['psp-versions-ena'] 130 + if cap != ena: 131 + cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap}) 132 + defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id, 133 + 'psp-versions-ena': ena }) 134 + 135 + # 136 + # Test cases 137 + # 138 + 139 + def dev_list_devices(cfg): 140 + """ Dump all devices """ 141 + _init_psp_dev(cfg) 142 + 143 + devices = cfg.pspnl.dev_get({}, dump=True) 144 + 145 + found = False 146 + for dev in devices: 147 + found |= dev['id'] == cfg.psp_dev_id 148 + ksft_true(found) 149 + 150 + 151 + def dev_get_device(cfg): 152 + """ Get the device we intend to use """ 153 + _init_psp_dev(cfg) 154 + 155 + dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id}) 156 + ksft_eq(dev['id'], cfg.psp_dev_id) 157 + 158 + 159 + def dev_get_device_bad(cfg): 160 + """ Test getting device which doesn't exist """ 161 + raised = False 162 + try: 163 + cfg.pspnl.dev_get({'id': 1234567}) 164 + except NlError as e: 165 + ksft_eq(e.nl_msg.error, -errno.ENODEV) 166 + raised = True 167 + ksft_true(raised) 168 + 169 + 170 + def dev_rotate(cfg): 171 + """ Test key rotation """ 172 + _init_psp_dev(cfg) 173 + 174 + rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 175 + ksft_eq(rot['id'], cfg.psp_dev_id) 176 + rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 177 + ksft_eq(rot['id'], cfg.psp_dev_id) 178 + 179 + 180 + def dev_rotate_spi(cfg): 181 + """ Test key rotation and SPI check """ 182 + _init_psp_dev(cfg) 183 + 184 + top_a = top_b = 0 185 + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 186 + assoc_a = cfg.pspnl.rx_assoc({"version": 0, 187 + "dev-id": cfg.psp_dev_id, 188 + "sock-fd": s.fileno()}) 189 + top_a = assoc_a['rx-key']['spi'] >> 31 190 + s.close() 191 + rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 192 + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 193 + ksft_eq(rot['id'], cfg.psp_dev_id) 194 + assoc_b = cfg.pspnl.rx_assoc({"version": 0, 195 + "dev-id": cfg.psp_dev_id, 196 + "sock-fd": s.fileno()}) 197 + top_b = assoc_b['rx-key']['spi'] >> 31 198 + s.close() 199 + ksft_ne(top_a, top_b) 200 + 201 + 202 + def assoc_basic(cfg): 203 + """ Test creating associations """ 204 + _init_psp_dev(cfg) 205 + 206 + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 207 + assoc = cfg.pspnl.rx_assoc({"version": 0, 208 + "dev-id": cfg.psp_dev_id, 209 + "sock-fd": s.fileno()}) 210 + ksft_eq(assoc['dev-id'], cfg.psp_dev_id) 211 + ksft_gt(assoc['rx-key']['spi'], 0) 212 + ksft_eq(len(assoc['rx-key']['key']), 16) 213 + 214 + assoc = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 215 + "version": 0, 216 + "tx-key": assoc['rx-key'], 217 + "sock-fd": s.fileno()}) 218 + ksft_eq(len(assoc), 0) 219 + s.close() 220 + 221 + 222 + def assoc_bad_dev(cfg): 223 + """ Test creating associations with bad device ID """ 224 + _init_psp_dev(cfg) 225 + 226 + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 227 + with ksft_raises(NlError) as cm: 228 + cfg.pspnl.rx_assoc({"version": 0, 229 + "dev-id": cfg.psp_dev_id + 1234567, 230 + "sock-fd": s.fileno()}) 231 + ksft_eq(cm.exception.nl_msg.error, -errno.ENODEV) 232 + 233 + 234 + def assoc_sk_only_conn(cfg): 235 + """ Test creating associations based on socket """ 236 + _init_psp_dev(cfg) 237 + 238 + with _make_clr_conn(cfg) as s: 239 + assoc = cfg.pspnl.rx_assoc({"version": 0, 240 + "sock-fd": s.fileno()}) 241 + ksft_eq(assoc['dev-id'], cfg.psp_dev_id) 242 + cfg.pspnl.tx_assoc({"version": 0, 243 + "tx-key": assoc['rx-key'], 244 + "sock-fd": s.fileno()}) 245 + _close_conn(cfg, s) 246 + 247 + 248 + def assoc_sk_only_mismatch(cfg): 249 + """ Test creating associations based on socket (dev mismatch) """ 250 + _init_psp_dev(cfg) 251 + 252 + with _make_clr_conn(cfg) as s: 253 + with ksft_raises(NlError) as cm: 254 + cfg.pspnl.rx_assoc({"version": 0, 255 + "dev-id": cfg.psp_dev_id + 1234567, 256 + "sock-fd": s.fileno()}) 257 + the_exception = cm.exception 258 + ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id") 259 + ksft_eq(the_exception.nl_msg.error, -errno.EINVAL) 260 + 261 + 262 + def assoc_sk_only_mismatch_tx(cfg): 263 + """ Test creating associations based on socket (dev mismatch) """ 264 + _init_psp_dev(cfg) 265 + 266 + with _make_clr_conn(cfg) as s: 267 + with ksft_raises(NlError) as cm: 268 + assoc = cfg.pspnl.rx_assoc({"version": 0, 269 + "sock-fd": s.fileno()}) 270 + cfg.pspnl.tx_assoc({"version": 0, 271 + "tx-key": assoc['rx-key'], 272 + "dev-id": cfg.psp_dev_id + 1234567, 273 + "sock-fd": s.fileno()}) 274 + the_exception = cm.exception 275 + ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id") 276 + ksft_eq(the_exception.nl_msg.error, -errno.EINVAL) 277 + 278 + 279 + def assoc_sk_only_unconn(cfg): 280 + """ Test creating associations based on socket (unconnected, should fail) """ 281 + _init_psp_dev(cfg) 282 + 283 + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 284 + with ksft_raises(NlError) as cm: 285 + cfg.pspnl.rx_assoc({"version": 0, 286 + "sock-fd": s.fileno()}) 287 + the_exception = cm.exception 288 + ksft_eq(the_exception.nl_msg.extack['miss-type'], "dev-id") 289 + ksft_eq(the_exception.nl_msg.error, -errno.EINVAL) 290 + 291 + 292 + def assoc_version_mismatch(cfg): 293 + """ Test creating associations where Rx and Tx PSP versions do not match """ 294 + _init_psp_dev(cfg) 295 + 296 + versions = list(cfg.psp_info['psp-versions-cap']) 297 + if len(versions) < 2: 298 + raise KsftSkipEx("Not enough PSP versions supported by the device for the test") 299 + 300 + # Translate versions to integers 301 + versions = [cfg.pspnl.consts["version"].entries[v].value for v in versions] 302 + 303 + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 304 + rx = cfg.pspnl.rx_assoc({"version": versions[0], 305 + "dev-id": cfg.psp_dev_id, 306 + "sock-fd": s.fileno()}) 307 + 308 + for version in versions[1:]: 309 + with ksft_raises(NlError) as cm: 310 + cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 311 + "version": version, 312 + "tx-key": rx['rx-key'], 313 + "sock-fd": s.fileno()}) 314 + the_exception = cm.exception 315 + ksft_eq(the_exception.nl_msg.error, -errno.EINVAL) 316 + 317 + 318 + def assoc_twice(cfg): 319 + """ Test reusing Tx assoc for two sockets """ 320 + _init_psp_dev(cfg) 321 + 322 + def rx_assoc_check(s): 323 + assoc = cfg.pspnl.rx_assoc({"version": 0, 324 + "dev-id": cfg.psp_dev_id, 325 + "sock-fd": s.fileno()}) 326 + ksft_eq(assoc['dev-id'], cfg.psp_dev_id) 327 + ksft_gt(assoc['rx-key']['spi'], 0) 328 + ksft_eq(len(assoc['rx-key']['key']), 16) 329 + 330 + return assoc 331 + 332 + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 333 + assoc = rx_assoc_check(s) 334 + tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 335 + "version": 0, 336 + "tx-key": assoc['rx-key'], 337 + "sock-fd": s.fileno()}) 338 + ksft_eq(len(tx), 0) 339 + 340 + # Use the same Tx assoc second time 341 + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s2: 342 + rx_assoc_check(s2) 343 + tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 344 + "version": 0, 345 + "tx-key": assoc['rx-key'], 346 + "sock-fd": s2.fileno()}) 347 + ksft_eq(len(tx), 0) 348 + 349 + s.close() 350 + 351 + 352 + def _data_basic_send(cfg, version, ipver): 353 + """ Test basic data send """ 354 + _init_psp_dev(cfg) 355 + 356 + # Version 0 is required by spec, don't let it skip 357 + if version: 358 + name = cfg.pspnl.consts["version"].entries_by_val[version].name 359 + if name not in cfg.psp_info['psp-versions-cap']: 360 + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 361 + with ksft_raises(NlError) as cm: 362 + cfg.pspnl.rx_assoc({"version": version, 363 + "dev-id": cfg.psp_dev_id, 364 + "sock-fd": s.fileno()}) 365 + ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP) 366 + raise KsftSkipEx("PSP version not supported", name) 367 + 368 + s = _make_psp_conn(cfg, version, ipver) 369 + 370 + rx_assoc = cfg.pspnl.rx_assoc({"version": version, 371 + "dev-id": cfg.psp_dev_id, 372 + "sock-fd": s.fileno()}) 373 + rx = rx_assoc['rx-key'] 374 + tx = _spi_xchg(s, rx) 375 + 376 + cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 377 + "version": version, 378 + "tx-key": tx, 379 + "sock-fd": s.fileno()}) 380 + 381 + data_len = _send_careful(cfg, s, 100) 382 + _check_data_rx(cfg, data_len) 383 + _close_psp_conn(cfg, s) 384 + 385 + 386 + def __bad_xfer_do(cfg, s, tx, version='hdr0-aes-gcm-128'): 387 + # Make sure we accept the ACK for the SPI before we seal with the bad assoc 388 + _check_data_outq(s, 0) 389 + 390 + cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 391 + "version": version, 392 + "tx-key": tx, 393 + "sock-fd": s.fileno()}) 394 + 395 + data_len = _send_careful(cfg, s, 20) 396 + _check_data_outq(s, data_len, force_wait=True) 397 + _check_data_rx(cfg, 0) 398 + _close_psp_conn(cfg, s) 399 + 400 + 401 + def data_send_bad_key(cfg): 402 + """ Test send data with bad key """ 403 + _init_psp_dev(cfg) 404 + 405 + s = _make_psp_conn(cfg) 406 + 407 + rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 408 + "dev-id": cfg.psp_dev_id, 409 + "sock-fd": s.fileno()}) 410 + rx = rx_assoc['rx-key'] 411 + tx = _spi_xchg(s, rx) 412 + tx['key'] = (tx['key'][0] ^ 0xff).to_bytes(1, 'little') + tx['key'][1:] 413 + __bad_xfer_do(cfg, s, tx) 414 + 415 + 416 + def data_send_disconnect(cfg): 417 + """ Test socket close after sending data """ 418 + _init_psp_dev(cfg) 419 + 420 + with _make_psp_conn(cfg) as s: 421 + assoc = cfg.pspnl.rx_assoc({"version": 0, 422 + "sock-fd": s.fileno()}) 423 + tx = _spi_xchg(s, assoc['rx-key']) 424 + cfg.pspnl.tx_assoc({"version": 0, 425 + "tx-key": tx, 426 + "sock-fd": s.fileno()}) 427 + 428 + data_len = _send_careful(cfg, s, 100) 429 + _check_data_rx(cfg, data_len) 430 + 431 + s.shutdown(socket.SHUT_RDWR) 432 + s.close() 433 + 434 + 435 + def _data_mss_adjust(cfg, ipver): 436 + _init_psp_dev(cfg) 437 + 438 + # First figure out what the MSS would be without any adjustments 439 + s = _make_clr_conn(cfg, ipver) 440 + s.send(b"0123456789abcdef" * 1024) 441 + _check_data_rx(cfg, 16 * 1024) 442 + mss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) 443 + _close_conn(cfg, s) 444 + 445 + s = _make_psp_conn(cfg, 0, ipver) 446 + try: 447 + rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 448 + "dev-id": cfg.psp_dev_id, 449 + "sock-fd": s.fileno()}) 450 + rx = rx_assoc['rx-key'] 451 + tx = _spi_xchg(s, rx) 452 + 453 + rxmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) 454 + ksft_eq(mss, rxmss) 455 + 456 + cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 457 + "version": 0, 458 + "tx-key": tx, 459 + "sock-fd": s.fileno()}) 460 + 461 + txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) 462 + ksft_eq(mss, txmss + 40) 463 + 464 + data_len = _send_careful(cfg, s, 100) 465 + _check_data_rx(cfg, data_len) 466 + _check_data_outq(s, 0) 467 + 468 + txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) 469 + ksft_eq(mss, txmss + 40) 470 + finally: 471 + _close_psp_conn(cfg, s) 472 + 473 + 474 + def data_stale_key(cfg): 475 + """ Test send on a double-rotated key """ 476 + _init_psp_dev(cfg) 477 + 478 + s = _make_psp_conn(cfg) 479 + try: 480 + rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 481 + "dev-id": cfg.psp_dev_id, 482 + "sock-fd": s.fileno()}) 483 + rx = rx_assoc['rx-key'] 484 + tx = _spi_xchg(s, rx) 485 + 486 + cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 487 + "version": 0, 488 + "tx-key": tx, 489 + "sock-fd": s.fileno()}) 490 + 491 + data_len = _send_careful(cfg, s, 100) 492 + _check_data_rx(cfg, data_len) 493 + _check_data_outq(s, 0) 494 + 495 + cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 496 + cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 497 + 498 + s.send(b'0123456789' * 200) 499 + _check_data_outq(s, 2000, force_wait=True) 500 + finally: 501 + _close_psp_conn(cfg, s) 502 + 503 + 504 + def __nsim_psp_rereg(cfg): 505 + # The PSP dev ID will change, remember what was there before 506 + before = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)]) 507 + 508 + cfg._ns.nsims[0].dfs_write('psp_rereg', '1') 509 + 510 + after = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)]) 511 + 512 + new_devs = list(after - before) 513 + ksft_eq(len(new_devs), 1) 514 + cfg.psp_dev_id = list(after - before)[0] 515 + 516 + 517 + def removal_device_rx(cfg): 518 + """ Test removing a netdev / PSD with active Rx assoc """ 519 + 520 + # We could technically devlink reload real devices, too 521 + # but that kills the control socket. So test this on 522 + # netdevsim only for now 523 + cfg.require_nsim() 524 + 525 + s = _make_clr_conn(cfg) 526 + try: 527 + rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 528 + "dev-id": cfg.psp_dev_id, 529 + "sock-fd": s.fileno()}) 530 + ksft_not_none(rx_assoc) 531 + 532 + __nsim_psp_rereg(cfg) 533 + finally: 534 + _close_conn(cfg, s) 535 + 536 + 537 + def removal_device_bi(cfg): 538 + """ Test removing a netdev / PSD with active Rx/Tx assoc """ 539 + 540 + # We could technically devlink reload real devices, too 541 + # but that kills the control socket. So test this on 542 + # netdevsim only for now 543 + cfg.require_nsim() 544 + 545 + s = _make_clr_conn(cfg) 546 + try: 547 + rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 548 + "dev-id": cfg.psp_dev_id, 549 + "sock-fd": s.fileno()}) 550 + cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 551 + "version": 0, 552 + "tx-key": rx_assoc['rx-key'], 553 + "sock-fd": s.fileno()}) 554 + __nsim_psp_rereg(cfg) 555 + finally: 556 + _close_conn(cfg, s) 557 + 558 + 559 + def psp_ip_ver_test_builder(name, test_func, psp_ver, ipver): 560 + """Build test cases for each combo of PSP version and IP version""" 561 + def test_case(cfg): 562 + cfg.require_ipver(ipver) 563 + test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}" 564 + test_func(cfg, psp_ver, ipver) 565 + return test_case 566 + 567 + 568 + def ipver_test_builder(name, test_func, ipver): 569 + """Build test cases for each IP version""" 570 + def test_case(cfg): 571 + cfg.require_ipver(ipver) 572 + test_case.__name__ = f"{name}_ip{ipver}" 573 + test_func(cfg, ipver) 574 + return test_case 575 + 576 + 577 + def main() -> None: 578 + """ Ksft boiler plate main """ 579 + 580 + with NetDrvEpEnv(__file__) as cfg: 581 + cfg.pspnl = PSPFamily() 582 + 583 + # Set up responder and communication sock 584 + responder = cfg.remote.deploy("psp_responder") 585 + 586 + cfg.comm_port = rand_port() 587 + srv = None 588 + try: 589 + with bkg(responder + f" -p {cfg.comm_port}", host=cfg.remote, 590 + exit_wait=True) as srv: 591 + wait_port_listen(cfg.comm_port, host=cfg.remote) 592 + 593 + cfg.comm_sock = socket.create_connection((cfg.remote_addr, 594 + cfg.comm_port), 595 + timeout=1) 596 + 597 + cases = [ 598 + psp_ip_ver_test_builder( 599 + "data_basic_send", _data_basic_send, version, ipver 600 + ) 601 + for version in range(0, 4) 602 + for ipver in ("4", "6") 603 + ] 604 + cases += [ 605 + ipver_test_builder("data_mss_adjust", _data_mss_adjust, ipver) 606 + for ipver in ("4", "6") 607 + ] 608 + 609 + ksft_run(cases=cases, globs=globals(), 610 + case_pfx={"dev_", "data_", "assoc_", "removal_"}, 611 + args=(cfg, )) 612 + 613 + cfg.comm_sock.send(b"exit\0") 614 + cfg.comm_sock.close() 615 + finally: 616 + if srv and (srv.stdout or srv.stderr): 617 + ksft_pr("") 618 + ksft_pr(f"Responder logs ({srv.ret}):") 619 + if srv and srv.stdout: 620 + ksft_pr("STDOUT:\n# " + srv.stdout.strip().replace("\n", "\n# ")) 621 + if srv and srv.stderr: 622 + ksft_pr("STDERR:\n# " + srv.stderr.strip().replace("\n", "\n# ")) 623 + ksft_exit() 624 + 625 + 626 + if __name__ == "__main__": 627 + main()
+483
tools/testing/selftests/drivers/net/psp_responder.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <stdio.h> 4 + #include <string.h> 5 + #include <sys/poll.h> 6 + #include <sys/socket.h> 7 + #include <sys/time.h> 8 + #include <netinet/in.h> 9 + #include <unistd.h> 10 + 11 + #include <ynl.h> 12 + 13 + #include "psp-user.h" 14 + 15 + #define dbg(msg...) \ 16 + do { \ 17 + if (opts->verbose) \ 18 + fprintf(stderr, "DEBUG: " msg); \ 19 + } while (0) 20 + 21 + static bool should_quit; 22 + 23 + struct opts { 24 + int port; 25 + int devid; 26 + bool verbose; 27 + }; 28 + 29 + enum accept_cfg { 30 + ACCEPT_CFG_NONE = 0, 31 + ACCEPT_CFG_CLEAR, 32 + ACCEPT_CFG_PSP, 33 + }; 34 + 35 + static struct { 36 + unsigned char tx; 37 + unsigned char rx; 38 + } psp_vers; 39 + 40 + static int conn_setup_psp(struct ynl_sock *ys, struct opts *opts, int data_sock) 41 + { 42 + struct psp_rx_assoc_rsp *rsp; 43 + struct psp_rx_assoc_req *req; 44 + struct psp_tx_assoc_rsp *tsp; 45 + struct psp_tx_assoc_req *teq; 46 + char info[300]; 47 + int key_len; 48 + ssize_t sz; 49 + __u32 spi; 50 + 51 + dbg("create PSP connection\n"); 52 + 53 + // Rx assoc alloc 54 + req = psp_rx_assoc_req_alloc(); 55 + 56 + psp_rx_assoc_req_set_sock_fd(req, data_sock); 57 + psp_rx_assoc_req_set_version(req, psp_vers.rx); 58 + 59 + rsp = psp_rx_assoc(ys, req); 60 + psp_rx_assoc_req_free(req); 61 + 62 + if (!rsp) { 63 + perror("ERROR: failed to Rx assoc"); 64 + return -1; 65 + } 66 + 67 + // SPI exchange 68 + key_len = rsp->rx_key._len.key; 69 + memcpy(info, &rsp->rx_key.spi, sizeof(spi)); 70 + memcpy(&info[sizeof(spi)], rsp->rx_key.key, key_len); 71 + sz = sizeof(spi) + key_len; 72 + 73 + send(data_sock, info, sz, MSG_WAITALL); 74 + psp_rx_assoc_rsp_free(rsp); 75 + 76 + sz = recv(data_sock, info, sz, MSG_WAITALL); 77 + if (sz < 0) { 78 + perror("ERROR: failed to read PSP key from sock"); 79 + return -1; 80 + } 81 + memcpy(&spi, info, sizeof(spi)); 82 + 83 + // Setup Tx assoc 84 + teq = psp_tx_assoc_req_alloc(); 85 + 86 + psp_tx_assoc_req_set_sock_fd(teq, data_sock); 87 + psp_tx_assoc_req_set_version(teq, psp_vers.tx); 88 + psp_tx_assoc_req_set_tx_key_spi(teq, spi); 89 + psp_tx_assoc_req_set_tx_key_key(teq, &info[sizeof(spi)], key_len); 90 + 91 + tsp = psp_tx_assoc(ys, teq); 92 + psp_tx_assoc_req_free(teq); 93 + if (!tsp) { 94 + perror("ERROR: failed to Tx assoc"); 95 + return -1; 96 + } 97 + psp_tx_assoc_rsp_free(tsp); 98 + 99 + return 0; 100 + } 101 + 102 + static void send_ack(int sock) 103 + { 104 + send(sock, "ack", 4, MSG_WAITALL); 105 + } 106 + 107 + static void send_err(int sock) 108 + { 109 + send(sock, "err", 4, MSG_WAITALL); 110 + } 111 + 112 + static void send_str(int sock, int value) 113 + { 114 + char buf[128]; 115 + int ret; 116 + 117 + ret = snprintf(buf, sizeof(buf), "%d", value); 118 + send(sock, buf, ret + 1, MSG_WAITALL); 119 + } 120 + 121 + static void 122 + run_session(struct ynl_sock *ys, struct opts *opts, 123 + int server_sock, int comm_sock) 124 + { 125 + enum accept_cfg accept_cfg = ACCEPT_CFG_NONE; 126 + struct pollfd pfds[3]; 127 + size_t data_read = 0; 128 + int data_sock = -1; 129 + 130 + while (true) { 131 + bool race_close = false; 132 + int nfds; 133 + 134 + memset(pfds, 0, sizeof(pfds)); 135 + 136 + pfds[0].fd = server_sock; 137 + pfds[0].events = POLLIN; 138 + 139 + pfds[1].fd = comm_sock; 140 + pfds[1].events = POLLIN; 141 + 142 + nfds = 2; 143 + if (data_sock >= 0) { 144 + pfds[2].fd = data_sock; 145 + pfds[2].events = POLLIN; 146 + nfds++; 147 + } 148 + 149 + dbg(" ...\n"); 150 + if (poll(pfds, nfds, -1) < 0) { 151 + perror("poll"); 152 + break; 153 + } 154 + 155 + /* data sock */ 156 + if (pfds[2].revents & POLLIN) { 157 + char buf[8192]; 158 + ssize_t n; 159 + 160 + n = recv(data_sock, buf, sizeof(buf), 0); 161 + if (n <= 0) { 162 + if (n < 0) 163 + perror("data read"); 164 + close(data_sock); 165 + data_sock = -1; 166 + dbg("data sock closed\n"); 167 + } else { 168 + data_read += n; 169 + dbg("data read %zd\n", data_read); 170 + } 171 + } 172 + 173 + /* comm sock */ 174 + if (pfds[1].revents & POLLIN) { 175 + static char buf[4096]; 176 + static ssize_t off; 177 + bool consumed; 178 + ssize_t n; 179 + 180 + n = recv(comm_sock, &buf[off], sizeof(buf) - off, 0); 181 + if (n <= 0) { 182 + if (n < 0) 183 + perror("comm read"); 184 + return; 185 + } 186 + 187 + off += n; 188 + n = off; 189 + 190 + #define __consume(sz) \ 191 + ({ \ 192 + if (n == (sz)) { \ 193 + off = 0; \ 194 + } else { \ 195 + off -= (sz); \ 196 + memmove(buf, &buf[(sz)], off); \ 197 + } \ 198 + }) 199 + 200 + #define cmd(_name) \ 201 + ({ \ 202 + ssize_t sz = sizeof(_name); \ 203 + bool match = n >= sz && !memcmp(buf, _name, sz); \ 204 + \ 205 + if (match) { \ 206 + dbg("command: " _name "\n"); \ 207 + __consume(sz); \ 208 + } \ 209 + consumed |= match; \ 210 + match; \ 211 + }) 212 + 213 + do { 214 + consumed = false; 215 + 216 + if (cmd("read len")) 217 + send_str(comm_sock, data_read); 218 + 219 + if (cmd("data echo")) { 220 + if (data_sock >= 0) 221 + send(data_sock, "echo", 5, 222 + MSG_WAITALL); 223 + else 224 + fprintf(stderr, "WARN: echo but no data sock\n"); 225 + send_ack(comm_sock); 226 + } 227 + if (cmd("data close")) { 228 + if (data_sock >= 0) { 229 + close(data_sock); 230 + data_sock = -1; 231 + send_ack(comm_sock); 232 + } else { 233 + race_close = true; 234 + } 235 + } 236 + if (cmd("conn psp")) { 237 + if (accept_cfg != ACCEPT_CFG_NONE) 238 + fprintf(stderr, "WARN: old conn config still set!\n"); 239 + accept_cfg = ACCEPT_CFG_PSP; 240 + send_ack(comm_sock); 241 + /* next two bytes are versions */ 242 + if (off >= 2) { 243 + memcpy(&psp_vers, buf, 2); 244 + __consume(2); 245 + } else { 246 + fprintf(stderr, "WARN: short conn psp command!\n"); 247 + } 248 + } 249 + if (cmd("conn clr")) { 250 + if (accept_cfg != ACCEPT_CFG_NONE) 251 + fprintf(stderr, "WARN: old conn config still set!\n"); 252 + accept_cfg = ACCEPT_CFG_CLEAR; 253 + send_ack(comm_sock); 254 + } 255 + if (cmd("exit")) 256 + should_quit = true; 257 + #undef cmd 258 + 259 + if (!consumed) { 260 + fprintf(stderr, "WARN: unknown cmd: [%zd] %s\n", 261 + off, buf); 262 + } 263 + } while (consumed && off); 264 + } 265 + 266 + /* server sock */ 267 + if (pfds[0].revents & POLLIN) { 268 + if (data_sock >= 0) { 269 + fprintf(stderr, "WARN: new data sock but old one still here\n"); 270 + close(data_sock); 271 + data_sock = -1; 272 + } 273 + data_sock = accept(server_sock, NULL, NULL); 274 + if (data_sock < 0) { 275 + perror("accept"); 276 + continue; 277 + } 278 + data_read = 0; 279 + 280 + if (accept_cfg == ACCEPT_CFG_CLEAR) { 281 + dbg("new data sock: clear\n"); 282 + /* nothing to do */ 283 + } else if (accept_cfg == ACCEPT_CFG_PSP) { 284 + dbg("new data sock: psp\n"); 285 + conn_setup_psp(ys, opts, data_sock); 286 + } else { 287 + fprintf(stderr, "WARN: new data sock but no config\n"); 288 + } 289 + accept_cfg = ACCEPT_CFG_NONE; 290 + } 291 + 292 + if (race_close) { 293 + if (data_sock >= 0) { 294 + /* indeed, ordering problem, handle the close */ 295 + close(data_sock); 296 + data_sock = -1; 297 + send_ack(comm_sock); 298 + } else { 299 + fprintf(stderr, "WARN: close but no data sock\n"); 300 + send_err(comm_sock); 301 + } 302 + } 303 + } 304 + dbg("session ending\n"); 305 + } 306 + 307 + static int spawn_server(struct opts *opts) 308 + { 309 + struct sockaddr_in6 addr; 310 + int fd; 311 + 312 + fd = socket(AF_INET6, SOCK_STREAM, 0); 313 + if (fd < 0) { 314 + perror("can't open socket"); 315 + return -1; 316 + } 317 + 318 + memset(&addr, 0, sizeof(addr)); 319 + 320 + addr.sin6_family = AF_INET6; 321 + addr.sin6_addr = in6addr_any; 322 + addr.sin6_port = htons(opts->port); 323 + 324 + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) { 325 + perror("can't bind socket"); 326 + return -1; 327 + } 328 + 329 + if (listen(fd, 5)) { 330 + perror("can't listen"); 331 + return -1; 332 + } 333 + 334 + return fd; 335 + } 336 + 337 + static int run_responder(struct ynl_sock *ys, struct opts *opts) 338 + { 339 + int server_sock, comm; 340 + 341 + server_sock = spawn_server(opts); 342 + if (server_sock < 0) 343 + return 4; 344 + 345 + while (!should_quit) { 346 + comm = accept(server_sock, NULL, NULL); 347 + if (comm < 0) { 348 + perror("accept failed"); 349 + } else { 350 + run_session(ys, opts, server_sock, comm); 351 + close(comm); 352 + } 353 + } 354 + 355 + return 0; 356 + } 357 + 358 + static void usage(const char *name, const char *miss) 359 + { 360 + if (miss) 361 + fprintf(stderr, "Missing argument: %s\n", miss); 362 + 363 + fprintf(stderr, "Usage: %s -p port [-v] [-d psp-dev-id]\n", name); 364 + exit(EXIT_FAILURE); 365 + } 366 + 367 + static void parse_cmd_opts(int argc, char **argv, struct opts *opts) 368 + { 369 + int opt; 370 + 371 + while ((opt = getopt(argc, argv, "vp:d:")) != -1) { 372 + switch (opt) { 373 + case 'v': 374 + opts->verbose = 1; 375 + break; 376 + case 'p': 377 + opts->port = atoi(optarg); 378 + break; 379 + case 'd': 380 + opts->devid = atoi(optarg); 381 + break; 382 + default: 383 + usage(argv[0], NULL); 384 + } 385 + } 386 + } 387 + 388 + static int psp_dev_set_ena(struct ynl_sock *ys, __u32 dev_id, __u32 versions) 389 + { 390 + struct psp_dev_set_req *sreq; 391 + struct psp_dev_set_rsp *srsp; 392 + 393 + fprintf(stderr, "Set PSP enable on device %d to 0x%x\n", 394 + dev_id, versions); 395 + 396 + sreq = psp_dev_set_req_alloc(); 397 + 398 + psp_dev_set_req_set_id(sreq, dev_id); 399 + psp_dev_set_req_set_psp_versions_ena(sreq, versions); 400 + 401 + srsp = psp_dev_set(ys, sreq); 402 + psp_dev_set_req_free(sreq); 403 + if (!srsp) 404 + return 10; 405 + 406 + psp_dev_set_rsp_free(srsp); 407 + return 0; 408 + } 409 + 410 + int main(int argc, char **argv) 411 + { 412 + struct psp_dev_get_list *dev_list; 413 + bool devid_found = false; 414 + __u32 ver_ena, ver_cap; 415 + struct opts opts = {}; 416 + struct ynl_error yerr; 417 + struct ynl_sock *ys; 418 + int first_id = 0; 419 + int ret; 420 + 421 + parse_cmd_opts(argc, argv, &opts); 422 + if (!opts.port) 423 + usage(argv[0], "port"); // exits 424 + 425 + ys = ynl_sock_create(&ynl_psp_family, &yerr); 426 + if (!ys) { 427 + fprintf(stderr, "YNL: %s\n", yerr.msg); 428 + return 1; 429 + } 430 + 431 + dev_list = psp_dev_get_dump(ys); 432 + if (ynl_dump_empty(dev_list)) { 433 + if (ys->err.code) 434 + goto err_close; 435 + fprintf(stderr, "No PSP devices\n"); 436 + goto err_close_silent; 437 + } 438 + 439 + ynl_dump_foreach(dev_list, d) { 440 + if (opts.devid) { 441 + devid_found = true; 442 + ver_ena = d->psp_versions_ena; 443 + ver_cap = d->psp_versions_cap; 444 + } else if (!first_id) { 445 + first_id = d->id; 446 + ver_ena = d->psp_versions_ena; 447 + ver_cap = d->psp_versions_cap; 448 + } else { 449 + fprintf(stderr, "Multiple PSP devices found\n"); 450 + goto err_close_silent; 451 + } 452 + } 453 + psp_dev_get_list_free(dev_list); 454 + 455 + if (opts.devid && !devid_found) { 456 + fprintf(stderr, "PSP device %d requested on cmdline, not found\n", 457 + opts.devid); 458 + goto err_close_silent; 459 + } else if (!opts.devid) { 460 + opts.devid = first_id; 461 + } 462 + 463 + if (ver_ena != ver_cap) { 464 + ret = psp_dev_set_ena(ys, opts.devid, ver_cap); 465 + if (ret) 466 + goto err_close; 467 + } 468 + 469 + ret = run_responder(ys, &opts); 470 + 471 + if (ver_ena != ver_cap && psp_dev_set_ena(ys, opts.devid, ver_ena)) 472 + fprintf(stderr, "WARN: failed to set the PSP versions back\n"); 473 + 474 + ynl_sock_destroy(ys); 475 + 476 + return ret; 477 + 478 + err_close: 479 + fprintf(stderr, "YNL: %s\n", ys->err.msg); 480 + err_close_silent: 481 + ynl_sock_destroy(ys); 482 + return 2; 483 + }
+1 -1
tools/testing/selftests/net/lib/py/__init__.py
··· 6 6 from .nsim import * 7 7 from .utils import * 8 8 from .ynl import NlError, YnlFamily, EthtoolFamily, NetdevFamily, RtnlFamily, RtnlAddrFamily 9 - from .ynl import NetshaperFamily, DevlinkFamily 9 + from .ynl import NetshaperFamily, DevlinkFamily, PSPFamily
+10
tools/testing/selftests/net/lib/py/ksft.py
··· 72 72 _fail("Check failed", a, "does not eval to True", comment) 73 73 74 74 75 + def ksft_not_none(a, comment=""): 76 + if a is None: 77 + _fail("Check failed", a, "is None", comment) 78 + 79 + 75 80 def ksft_in(a, b, comment=""): 76 81 if a not in b: 77 82 _fail("Check failed", a, "not in", b, comment) ··· 95 90 def ksft_ge(a, b, comment=""): 96 91 if a < b: 97 92 _fail("Check failed", a, "<", b, comment) 93 + 94 + 95 + def ksft_gt(a, b, comment=""): 96 + if a <= b: 97 + _fail("Check failed", a, "<=", b, comment) 98 98 99 99 100 100 def ksft_lt(a, b, comment=""):
+5
tools/testing/selftests/net/lib/py/ynl.py
··· 61 61 def __init__(self, recv_size=0): 62 62 super().__init__((SPEC_PATH / Path('devlink.yaml')).as_posix(), 63 63 schema='', recv_size=recv_size) 64 + 65 + class PSPFamily(YnlFamily): 66 + def __init__(self, recv_size=0): 67 + super().__init__((SPEC_PATH / Path('psp.yaml')).as_posix(), 68 + schema='', recv_size=recv_size)