Path: blob/master/tools/testing/selftests/drivers/net/psp_responder.c
29270 views
// SPDX-License-Identifier: GPL-2.012#include <stdio.h>3#include <string.h>4#include <sys/poll.h>5#include <sys/socket.h>6#include <sys/time.h>7#include <netinet/in.h>8#include <unistd.h>910#include <ynl.h>1112#include "psp-user.h"1314#define dbg(msg...) \15do { \16if (opts->verbose) \17fprintf(stderr, "DEBUG: " msg); \18} while (0)1920static bool should_quit;2122struct opts {23int port;24int devid;25bool verbose;26};2728enum accept_cfg {29ACCEPT_CFG_NONE = 0,30ACCEPT_CFG_CLEAR,31ACCEPT_CFG_PSP,32};3334static struct {35unsigned char tx;36unsigned char rx;37} psp_vers;3839static int conn_setup_psp(struct ynl_sock *ys, struct opts *opts, int data_sock)40{41struct psp_rx_assoc_rsp *rsp;42struct psp_rx_assoc_req *req;43struct psp_tx_assoc_rsp *tsp;44struct psp_tx_assoc_req *teq;45char info[300];46int key_len;47ssize_t sz;48__u32 spi;4950dbg("create PSP connection\n");5152// Rx assoc alloc53req = psp_rx_assoc_req_alloc();5455psp_rx_assoc_req_set_sock_fd(req, data_sock);56psp_rx_assoc_req_set_version(req, psp_vers.rx);5758rsp = psp_rx_assoc(ys, req);59psp_rx_assoc_req_free(req);6061if (!rsp) {62perror("ERROR: failed to Rx assoc");63return -1;64}6566// SPI exchange67key_len = rsp->rx_key._len.key;68memcpy(info, &rsp->rx_key.spi, sizeof(spi));69memcpy(&info[sizeof(spi)], rsp->rx_key.key, key_len);70sz = sizeof(spi) + key_len;7172send(data_sock, info, sz, MSG_WAITALL);73psp_rx_assoc_rsp_free(rsp);7475sz = recv(data_sock, info, sz, MSG_WAITALL);76if (sz < 0) {77perror("ERROR: failed to read PSP key from sock");78return -1;79}80memcpy(&spi, info, sizeof(spi));8182// Setup Tx assoc83teq = psp_tx_assoc_req_alloc();8485psp_tx_assoc_req_set_sock_fd(teq, data_sock);86psp_tx_assoc_req_set_version(teq, psp_vers.tx);87psp_tx_assoc_req_set_tx_key_spi(teq, spi);88psp_tx_assoc_req_set_tx_key_key(teq, &info[sizeof(spi)], key_len);8990tsp = psp_tx_assoc(ys, teq);91psp_tx_assoc_req_free(teq);92if (!tsp) {93perror("ERROR: failed to Tx assoc");94return -1;95}96psp_tx_assoc_rsp_free(tsp);9798return 0;99}100101static void send_ack(int sock)102{103send(sock, "ack", 4, MSG_WAITALL);104}105106static void send_err(int sock)107{108send(sock, "err", 4, MSG_WAITALL);109}110111static void send_str(int sock, int value)112{113char buf[128];114int ret;115116ret = snprintf(buf, sizeof(buf), "%d", value);117send(sock, buf, ret + 1, MSG_WAITALL);118}119120static void121run_session(struct ynl_sock *ys, struct opts *opts,122int server_sock, int comm_sock)123{124enum accept_cfg accept_cfg = ACCEPT_CFG_NONE;125struct pollfd pfds[3];126size_t data_read = 0;127int data_sock = -1;128129while (true) {130bool race_close = false;131int nfds;132133memset(pfds, 0, sizeof(pfds));134135pfds[0].fd = server_sock;136pfds[0].events = POLLIN;137138pfds[1].fd = comm_sock;139pfds[1].events = POLLIN;140141nfds = 2;142if (data_sock >= 0) {143pfds[2].fd = data_sock;144pfds[2].events = POLLIN;145nfds++;146}147148dbg(" ...\n");149if (poll(pfds, nfds, -1) < 0) {150perror("poll");151break;152}153154/* data sock */155if (pfds[2].revents & POLLIN) {156char buf[8192];157ssize_t n;158159n = recv(data_sock, buf, sizeof(buf), 0);160if (n <= 0) {161if (n < 0)162perror("data read");163close(data_sock);164data_sock = -1;165dbg("data sock closed\n");166} else {167data_read += n;168dbg("data read %zd\n", data_read);169}170}171172/* comm sock */173if (pfds[1].revents & POLLIN) {174static char buf[4096];175static ssize_t off;176bool consumed;177ssize_t n;178179n = recv(comm_sock, &buf[off], sizeof(buf) - off, 0);180if (n <= 0) {181if (n < 0)182perror("comm read");183return;184}185186off += n;187n = off;188189#define __consume(sz) \190({ \191if (n == (sz)) { \192off = 0; \193} else { \194off -= (sz); \195memmove(buf, &buf[(sz)], off); \196} \197})198199#define cmd(_name) \200({ \201ssize_t sz = sizeof(_name); \202bool match = n >= sz && !memcmp(buf, _name, sz); \203\204if (match) { \205dbg("command: " _name "\n"); \206__consume(sz); \207} \208consumed |= match; \209match; \210})211212do {213consumed = false;214215if (cmd("read len"))216send_str(comm_sock, data_read);217218if (cmd("data echo")) {219if (data_sock >= 0)220send(data_sock, "echo", 5,221MSG_WAITALL);222else223fprintf(stderr, "WARN: echo but no data sock\n");224send_ack(comm_sock);225}226if (cmd("data close")) {227if (data_sock >= 0) {228close(data_sock);229data_sock = -1;230send_ack(comm_sock);231} else {232race_close = true;233}234}235if (cmd("conn psp")) {236if (accept_cfg != ACCEPT_CFG_NONE)237fprintf(stderr, "WARN: old conn config still set!\n");238accept_cfg = ACCEPT_CFG_PSP;239send_ack(comm_sock);240/* next two bytes are versions */241if (off >= 2) {242memcpy(&psp_vers, buf, 2);243__consume(2);244} else {245fprintf(stderr, "WARN: short conn psp command!\n");246}247}248if (cmd("conn clr")) {249if (accept_cfg != ACCEPT_CFG_NONE)250fprintf(stderr, "WARN: old conn config still set!\n");251accept_cfg = ACCEPT_CFG_CLEAR;252send_ack(comm_sock);253}254if (cmd("exit"))255should_quit = true;256#undef cmd257258if (!consumed) {259fprintf(stderr, "WARN: unknown cmd: [%zd] %s\n",260off, buf);261}262} while (consumed && off);263}264265/* server sock */266if (pfds[0].revents & POLLIN) {267if (data_sock >= 0) {268fprintf(stderr, "WARN: new data sock but old one still here\n");269close(data_sock);270data_sock = -1;271}272data_sock = accept(server_sock, NULL, NULL);273if (data_sock < 0) {274perror("accept");275continue;276}277data_read = 0;278279if (accept_cfg == ACCEPT_CFG_CLEAR) {280dbg("new data sock: clear\n");281/* nothing to do */282} else if (accept_cfg == ACCEPT_CFG_PSP) {283dbg("new data sock: psp\n");284conn_setup_psp(ys, opts, data_sock);285} else {286fprintf(stderr, "WARN: new data sock but no config\n");287}288accept_cfg = ACCEPT_CFG_NONE;289}290291if (race_close) {292if (data_sock >= 0) {293/* indeed, ordering problem, handle the close */294close(data_sock);295data_sock = -1;296send_ack(comm_sock);297} else {298fprintf(stderr, "WARN: close but no data sock\n");299send_err(comm_sock);300}301}302}303dbg("session ending\n");304}305306static int spawn_server(struct opts *opts)307{308struct sockaddr_in6 addr;309int fd;310311fd = socket(AF_INET6, SOCK_STREAM, 0);312if (fd < 0) {313perror("can't open socket");314return -1;315}316317memset(&addr, 0, sizeof(addr));318319addr.sin6_family = AF_INET6;320addr.sin6_addr = in6addr_any;321addr.sin6_port = htons(opts->port);322323if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) {324perror("can't bind socket");325return -1;326}327328if (listen(fd, 5)) {329perror("can't listen");330return -1;331}332333return fd;334}335336static int run_responder(struct ynl_sock *ys, struct opts *opts)337{338int server_sock, comm;339340server_sock = spawn_server(opts);341if (server_sock < 0)342return 4;343344while (!should_quit) {345comm = accept(server_sock, NULL, NULL);346if (comm < 0) {347perror("accept failed");348} else {349run_session(ys, opts, server_sock, comm);350close(comm);351}352}353354return 0;355}356357static void usage(const char *name, const char *miss)358{359if (miss)360fprintf(stderr, "Missing argument: %s\n", miss);361362fprintf(stderr, "Usage: %s -p port [-v] [-d psp-dev-id]\n", name);363exit(EXIT_FAILURE);364}365366static void parse_cmd_opts(int argc, char **argv, struct opts *opts)367{368int opt;369370while ((opt = getopt(argc, argv, "vp:d:")) != -1) {371switch (opt) {372case 'v':373opts->verbose = 1;374break;375case 'p':376opts->port = atoi(optarg);377break;378case 'd':379opts->devid = atoi(optarg);380break;381default:382usage(argv[0], NULL);383}384}385}386387static int psp_dev_set_ena(struct ynl_sock *ys, __u32 dev_id, __u32 versions)388{389struct psp_dev_set_req *sreq;390struct psp_dev_set_rsp *srsp;391392fprintf(stderr, "Set PSP enable on device %d to 0x%x\n",393dev_id, versions);394395sreq = psp_dev_set_req_alloc();396397psp_dev_set_req_set_id(sreq, dev_id);398psp_dev_set_req_set_psp_versions_ena(sreq, versions);399400srsp = psp_dev_set(ys, sreq);401psp_dev_set_req_free(sreq);402if (!srsp)403return 10;404405psp_dev_set_rsp_free(srsp);406return 0;407}408409int main(int argc, char **argv)410{411struct psp_dev_get_list *dev_list;412bool devid_found = false;413__u32 ver_ena, ver_cap;414struct opts opts = {};415struct ynl_error yerr;416struct ynl_sock *ys;417int first_id = 0;418int ret;419420parse_cmd_opts(argc, argv, &opts);421if (!opts.port)422usage(argv[0], "port"); // exits423424ys = ynl_sock_create(&ynl_psp_family, &yerr);425if (!ys) {426fprintf(stderr, "YNL: %s\n", yerr.msg);427return 1;428}429430dev_list = psp_dev_get_dump(ys);431if (ynl_dump_empty(dev_list)) {432if (ys->err.code)433goto err_close;434fprintf(stderr, "No PSP devices\n");435goto err_close_silent;436}437438ynl_dump_foreach(dev_list, d) {439if (opts.devid) {440devid_found = true;441ver_ena = d->psp_versions_ena;442ver_cap = d->psp_versions_cap;443} else if (!first_id) {444first_id = d->id;445ver_ena = d->psp_versions_ena;446ver_cap = d->psp_versions_cap;447} else {448fprintf(stderr, "Multiple PSP devices found\n");449goto err_close_silent;450}451}452psp_dev_get_list_free(dev_list);453454if (opts.devid && !devid_found) {455fprintf(stderr, "PSP device %d requested on cmdline, not found\n",456opts.devid);457goto err_close_silent;458} else if (!opts.devid) {459opts.devid = first_id;460}461462if (ver_ena != ver_cap) {463ret = psp_dev_set_ena(ys, opts.devid, ver_cap);464if (ret)465goto err_close;466}467468ret = run_responder(ys, &opts);469470if (ver_ena != ver_cap && psp_dev_set_ena(ys, opts.devid, ver_ena))471fprintf(stderr, "WARN: failed to set the PSP versions back\n");472473ynl_sock_destroy(ys);474475return ret;476477err_close:478fprintf(stderr, "YNL: %s\n", ys->err.msg);479err_close_silent:480ynl_sock_destroy(ys);481return 2;482}483484485