[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <willemdebruijn.kernel.b88097f659ab@gmail.com>
Date: Sun, 28 Sep 2025 12:05:27 -0400
From: Willem de Bruijn <willemdebruijn.kernel@...il.com>
To: Jakub Kicinski <kuba@...nel.org>,
davem@...emloft.net
Cc: netdev@...r.kernel.org,
edumazet@...gle.com,
pabeni@...hat.com,
andrew+netdev@...n.ch,
horms@...nel.org,
petrm@...dia.com,
willemb@...gle.com,
shuah@...nel.org,
daniel.zahka@...il.com,
linux-kselftest@...r.kernel.org,
Jakub Kicinski <kuba@...nel.org>
Subject: Re: [PATCH net-next v3 3/8] selftests: drv-net: add PSP responder
Jakub Kicinski wrote:
> PSP tests need the remote system to support PSP, and some PSP capable
> application to exchange data with. Create a simple PSP responder app
> which we can build and deploy to the remote host. The tests themselves
> can be written in Python but for ease of deploying the responder is in C
> (using C YNL).
>
> Signed-off-by: Jakub Kicinski <kuba@...nel.org>
> Signed-off-by: Daniel Zahka <daniel.zahka@...il.com>
> ---
> tools/testing/selftests/drivers/net/Makefile | 9 +
> .../selftests/drivers/net/psp_responder.c | 483 ++++++++++++++++++
> .../testing/selftests/drivers/net/.gitignore | 1 +
> 3 files changed, 493 insertions(+)
> create mode 100644 tools/testing/selftests/drivers/net/psp_responder.c
>
> diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
> index 102cfb36846c..bd3af9a34e2f 100644
> --- a/tools/testing/selftests/drivers/net/Makefile
> +++ b/tools/testing/selftests/drivers/net/Makefile
> @@ -27,4 +27,13 @@ TEST_PROGS := \
> xdp.py \
> # end of TEST_PROGS
>
> +# YNL files, must be before "include ..lib.mk"
> +YNL_GEN_FILES := psp_responder
> +TEST_GEN_FILES += $(YNL_GEN_FILES)
> +
> include ../../lib.mk
> +
> +# YNL build
> +YNL_GENS := psp
> +
> +include ../../net/ynl.mk
> diff --git a/tools/testing/selftests/drivers/net/psp_responder.c b/tools/testing/selftests/drivers/net/psp_responder.c
> new file mode 100644
> index 000000000000..f309e0d73cbf
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/net/psp_responder.c
> @@ -0,0 +1,483 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/poll.h>
> +#include <sys/socket.h>
> +#include <sys/time.h>
> +#include <netinet/in.h>
> +#include <unistd.h>
> +
> +#include <ynl.h>
> +
> +#include "psp-user.h"
> +
> +#define dbg(msg...) \
> +do { \
> + if (opts->verbose) \
> + fprintf(stderr, "DEBUG: " msg); \
> +} while (0)
> +
> +static bool should_quit;
> +
> +struct opts {
> + int port;
> + int devid;
> + bool verbose;
> +};
> +
> +enum accept_cfg {
> + ACCEPT_CFG_NONE = 0,
> + ACCEPT_CFG_CLEAR,
> + ACCEPT_CFG_PSP,
> +};
> +
> +static struct {
> + unsigned char tx;
> + unsigned char rx;
> +} psp_vers;
> +
> +static int conn_setup_psp(struct ynl_sock *ys, struct opts *opts, int data_sock)
> +{
> + struct psp_rx_assoc_rsp *rsp;
> + struct psp_rx_assoc_req *req;
> + struct psp_tx_assoc_rsp *tsp;
> + struct psp_tx_assoc_req *teq;
> + char info[300];
Optionally a clearer upper bound, e.g., based on PSP_MAX_KEY.
And a struct, to avoid having to cast to beyond the SPI field.
> + int key_len;
> + ssize_t sz;
> + __u32 spi;
> +
> + dbg("create PSP connection\n");
> +
> + // Rx assoc alloc
> + req = psp_rx_assoc_req_alloc();
> +
> + psp_rx_assoc_req_set_sock_fd(req, data_sock);
> + psp_rx_assoc_req_set_version(req, psp_vers.rx);
> +
> + rsp = psp_rx_assoc(ys, req);
> + psp_rx_assoc_req_free(req);
> +
> + if (!rsp) {
> + perror("ERROR: failed to Rx assoc");
> + return -1;
> + }
> +
> + // SPI exchange
> + key_len = rsp->rx_key._len.key;
> + memcpy(info, &rsp->rx_key.spi, sizeof(spi));
> + memcpy(&info[sizeof(spi)], rsp->rx_key.key, key_len);
> + sz = sizeof(spi) + key_len;
> +
> + send(data_sock, info, sz, MSG_WAITALL);
Return value not checked
> + psp_rx_assoc_rsp_free(rsp);
> +
> + sz = recv(data_sock, info, sz, MSG_WAITALL);
> + if (sz < 0) {
> + perror("ERROR: failed to read PSP key from sock");
> + return -1;
> + }
> + memcpy(&spi, info, sizeof(spi));
> +
> + // Setup Tx assoc
> + teq = psp_tx_assoc_req_alloc();
> +
> + psp_tx_assoc_req_set_sock_fd(teq, data_sock);
> + psp_tx_assoc_req_set_version(teq, psp_vers.tx);
> + psp_tx_assoc_req_set_tx_key_spi(teq, spi);
> + psp_tx_assoc_req_set_tx_key_key(teq, &info[sizeof(spi)], key_len);
> +
> + tsp = psp_tx_assoc(ys, teq);
> + psp_tx_assoc_req_free(teq);
> + if (!tsp) {
> + perror("ERROR: failed to Tx assoc");
> + return -1;
> + }
> + psp_tx_assoc_rsp_free(tsp);
> +
> + return 0;
> +}
> +
> +static void send_ack(int sock)
> +{
> + send(sock, "ack", 4, MSG_WAITALL);
> +}
> +
> +static void send_err(int sock)
> +{
> + send(sock, "err", 4, MSG_WAITALL);
> +}
> +
> +static void send_str(int sock, int value)
> +{
> + char buf[128];
> + int ret;
> +
> + ret = snprintf(buf, sizeof(buf), "%d", value);
> + send(sock, buf, ret + 1, MSG_WAITALL);
> +}
> +
> +static void
> +run_session(struct ynl_sock *ys, struct opts *opts,
> + int server_sock, int comm_sock)
> +{
> + enum accept_cfg accept_cfg = ACCEPT_CFG_NONE;
> + struct pollfd pfds[3];
> + size_t data_read = 0;
> + int data_sock = -1;
> +
> + while (true) {
> + bool race_close = false;
> + int nfds;
> +
> + memset(pfds, 0, sizeof(pfds));
> +
> + pfds[0].fd = server_sock;
> + pfds[0].events = POLLIN;
> +
> + pfds[1].fd = comm_sock;
> + pfds[1].events = POLLIN;
> +
> + nfds = 2;
> + if (data_sock >= 0) {
> + pfds[2].fd = data_sock;
> + pfds[2].events = POLLIN;
> + nfds++;
> + }
> +
> + dbg(" ...\n");
> + if (poll(pfds, nfds, -1) < 0) {
> + perror("poll");
> + break;
> + }
> +
> + /* data sock */
> + if (pfds[2].revents & POLLIN) {
> + char buf[8192];
> + ssize_t n;
> +
> + n = recv(data_sock, buf, sizeof(buf), 0);
> + if (n <= 0) {
> + if (n < 0)
> + perror("data read");
> + close(data_sock);
> + data_sock = -1;
> + dbg("data sock closed\n");
> + } else {
> + data_read += n;
> + dbg("data read %zd\n", data_read);
> + }
> + }
> +
> + /* comm sock */
> + if (pfds[1].revents & POLLIN) {
> + static char buf[4096];
> + static ssize_t off;
> + bool consumed;
> + ssize_t n;
> +
> + n = recv(comm_sock, &buf[off], sizeof(buf) - off, 0);
> + if (n <= 0) {
> + if (n < 0)
> + perror("comm read");
> + return;
> + }
> +
> + off += n;
> + n = off;
> +
> +#define __consume(sz) \
> + ({ \
> + if (n == (sz)) { \
> + off = 0; \
> + } else { \
> + off -= (sz); \
> + memmove(buf, &buf[(sz)], off); \
> + } \
> + })
> +
> +#define cmd(_name) \
> + ({ \
> + ssize_t sz = sizeof(_name); \
> + bool match = n >= sz && !memcmp(buf, _name, sz); \
> + \
> + if (match) { \
> + dbg("command: " _name "\n"); \
> + __consume(sz); \
> + } \
> + consumed |= match; \
> + match; \
> + })
> +
> + do {
> + consumed = false;
> +
> + if (cmd("read len"))
> + send_str(comm_sock, data_read);
> +
> + if (cmd("data echo")) {
> + if (data_sock >= 0)
> + send(data_sock, "echo", 5,
> + MSG_WAITALL);
> + else
> + fprintf(stderr, "WARN: echo but no data sock\n");
> + send_ack(comm_sock);
> + }
> + if (cmd("data close")) {
> + if (data_sock >= 0) {
> + close(data_sock);
> + data_sock = -1;
> + send_ack(comm_sock);
> + } else {
> + race_close = true;
> + }
> + }
> + if (cmd("conn psp")) {
> + if (accept_cfg != ACCEPT_CFG_NONE)
> + fprintf(stderr, "WARN: old conn config still set!\n");
> + accept_cfg = ACCEPT_CFG_PSP;
> + send_ack(comm_sock);
> + /* next two bytes are versions */
> + if (off >= 2) {
> + memcpy(&psp_vers, buf, 2);
> + __consume(2);
> + } else {
> + fprintf(stderr, "WARN: short conn psp command!\n");
> + }
> + }
> + if (cmd("conn clr")) {
> + if (accept_cfg != ACCEPT_CFG_NONE)
> + fprintf(stderr, "WARN: old conn config still set!\n");
> + accept_cfg = ACCEPT_CFG_CLEAR;
> + send_ack(comm_sock);
> + }
> + if (cmd("exit"))
> + should_quit = true;
> +#undef cmd
> +
> + if (!consumed) {
> + fprintf(stderr, "WARN: unknown cmd: [%zd] %s\n",
> + off, buf);
> + }
> + } while (consumed && off);
> + }
> +
> + /* server sock */
> + if (pfds[0].revents & POLLIN) {
> + if (data_sock >= 0) {
> + fprintf(stderr, "WARN: new data sock but old one still here\n");
> + close(data_sock);
> + data_sock = -1;
> + }
> + data_sock = accept(server_sock, NULL, NULL);
> + if (data_sock < 0) {
> + perror("accept");
> + continue;
> + }
> + data_read = 0;
> +
> + if (accept_cfg == ACCEPT_CFG_CLEAR) {
> + dbg("new data sock: clear\n");
> + /* nothing to do */
> + } else if (accept_cfg == ACCEPT_CFG_PSP) {
> + dbg("new data sock: psp\n");
> + conn_setup_psp(ys, opts, data_sock);
> + } else {
> + fprintf(stderr, "WARN: new data sock but no config\n");
> + }
> + accept_cfg = ACCEPT_CFG_NONE;
> + }
> +
> + if (race_close) {
> + if (data_sock >= 0) {
> + /* indeed, ordering problem, handle the close */
> + close(data_sock);
> + data_sock = -1;
> + send_ack(comm_sock);
> + } else {
> + fprintf(stderr, "WARN: close but no data sock\n");
> + send_err(comm_sock);
> + }
> + }
> + }
> + dbg("session ending\n");
> +}
> +
> +static int spawn_server(struct opts *opts)
> +{
> + struct sockaddr_in6 addr;
> + int fd;
> +
> + fd = socket(AF_INET6, SOCK_STREAM, 0);
> + if (fd < 0) {
> + perror("can't open socket");
> + return -1;
> + }
> +
> + memset(&addr, 0, sizeof(addr));
> +
> + addr.sin6_family = AF_INET6;
> + addr.sin6_addr = in6addr_any;
> + addr.sin6_port = htons(opts->port);
> +
> + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) {
> + perror("can't bind socket");
> + return -1;
> + }
> +
> + if (listen(fd, 5)) {
> + perror("can't listen");
> + return -1;
> + }
> +
> + return fd;
> +}
> +
> +static int run_responder(struct ynl_sock *ys, struct opts *opts)
> +{
> + int server_sock, comm;
> +
> + server_sock = spawn_server(opts);
> + if (server_sock < 0)
> + return 4;
> +
> + while (!should_quit) {
> + comm = accept(server_sock, NULL, NULL);
> + if (comm < 0) {
> + perror("accept failed");
> + } else {
> + run_session(ys, opts, server_sock, comm);
> + close(comm);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void usage(const char *name, const char *miss)
> +{
> + if (miss)
> + fprintf(stderr, "Missing argument: %s\n", miss);
> +
> + fprintf(stderr, "Usage: %s -p port [-v] [-d psp-dev-id]\n", name);
> + exit(EXIT_FAILURE);
> +}
> +
> +static void parse_cmd_opts(int argc, char **argv, struct opts *opts)
> +{
> + int opt;
> +
> + while ((opt = getopt(argc, argv, "vp:d:")) != -1) {
> + switch (opt) {
> + case 'v':
> + opts->verbose = 1;
> + break;
> + case 'p':
> + opts->port = atoi(optarg);
> + break;
> + case 'd':
> + opts->devid = atoi(optarg);
> + break;
> + default:
> + usage(argv[0], NULL);
> + }
> + }
> +}
> +
> +static int psp_dev_set_ena(struct ynl_sock *ys, __u32 dev_id, __u32 versions)
> +{
> + struct psp_dev_set_req *sreq;
> + struct psp_dev_set_rsp *srsp;
> +
> + fprintf(stderr, "Set PSP enable on device %d to 0x%x\n",
> + dev_id, versions);
> +
> + sreq = psp_dev_set_req_alloc();
> +
> + psp_dev_set_req_set_id(sreq, dev_id);
> + psp_dev_set_req_set_psp_versions_ena(sreq, versions);
> +
> + srsp = psp_dev_set(ys, sreq);
> + psp_dev_set_req_free(sreq);
> + if (!srsp)
> + return 10;
typo, return 1 intended? (does not matter functionally, of course)
> +
> + psp_dev_set_rsp_free(srsp);
> + return 0;
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct psp_dev_get_list *dev_list;
> + bool devid_found = false;
> + __u32 ver_ena, ver_cap;
> + struct opts opts = {};
> + struct ynl_error yerr;
> + struct ynl_sock *ys;
> + int first_id = 0;
> + int ret;
> +
> + parse_cmd_opts(argc, argv, &opts);
> + if (!opts.port)
> + usage(argv[0], "port"); // exits
> +
> + ys = ynl_sock_create(&ynl_psp_family, &yerr);
> + if (!ys) {
> + fprintf(stderr, "YNL: %s\n", yerr.msg);
> + return 1;
> + }
> +
> + dev_list = psp_dev_get_dump(ys);
> + if (ynl_dump_empty(dev_list)) {
> + if (ys->err.code)
> + goto err_close;
> + fprintf(stderr, "No PSP devices\n");
> + goto err_close_silent;
> + }
> +
> + ynl_dump_foreach(dev_list, d) {
> + if (opts.devid) {
> + devid_found = true;
> + ver_ena = d->psp_versions_ena;
> + ver_cap = d->psp_versions_cap;
> + } else if (!first_id) {
> + first_id = d->id;
> + ver_ena = d->psp_versions_ena;
> + ver_cap = d->psp_versions_cap;
> + } else {
> + fprintf(stderr, "Multiple PSP devices found\n");
> + goto err_close_silent;
> + }
> + }
> + psp_dev_get_list_free(dev_list);
> +
> + if (opts.devid && !devid_found) {
> + fprintf(stderr, "PSP device %d requested on cmdline, not found\n",
> + opts.devid);
> + goto err_close_silent;
> + } else if (!opts.devid) {
> + opts.devid = first_id;
> + }
> +
> + if (ver_ena != ver_cap) {
> + ret = psp_dev_set_ena(ys, opts.devid, ver_cap);
> + if (ret)
> + goto err_close;
> + }
> +
> + ret = run_responder(ys, &opts);
> +
> + if (ver_ena != ver_cap && psp_dev_set_ena(ys, opts.devid, ver_ena))
> + fprintf(stderr, "WARN: failed to set the PSP versions back\n");
> +
> + ynl_sock_destroy(ys);
> +
> + return ret;
> +
> +err_close:
> + fprintf(stderr, "YNL: %s\n", ys->err.msg);
> +err_close_silent:
> + ynl_sock_destroy(ys);
> + return 2;
> +}
> diff --git a/tools/testing/selftests/drivers/net/.gitignore b/tools/testing/selftests/drivers/net/.gitignore
> index d634d8395d90..585ecb4d5dc4 100644
> --- a/tools/testing/selftests/drivers/net/.gitignore
> +++ b/tools/testing/selftests/drivers/net/.gitignore
> @@ -1,2 +1,3 @@
> # SPDX-License-Identifier: GPL-2.0-only
> napi_id_helper
> +psp_responder
> --
> 2.51.0
>
Powered by blists - more mailing lists