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

selftests: drv-net: add PSP responder

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@kernel.org>
Signed-off-by: Daniel Zahka <daniel.zahka@gmail.com>
Link: https://patch.msgid.link/20250927225420.1443468-4-kuba@kernel.org
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Jakub Kicinski and committed by
Paolo Abeni
2aeb71b2 8a5f956a

+493
+1
tools/testing/selftests/drivers/net/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 napi_id_helper 3 + psp_responder
+9
tools/testing/selftests/drivers/net/Makefile
··· 27 27 xdp.py \ 28 28 # end of TEST_PROGS 29 29 30 + # YNL files, must be before "include ..lib.mk" 31 + YNL_GEN_FILES := psp_responder 32 + TEST_GEN_FILES += $(YNL_GEN_FILES) 33 + 30 34 include ../../lib.mk 35 + 36 + # YNL build 37 + YNL_GENS := psp 38 + 39 + include ../../net/ynl.mk
+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 + }