Path: blob/master/tools/testing/vsock/vsock_diag_test.c
26282 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* vsock_diag_test - vsock_diag.ko test suite3*4* Copyright (C) 2017 Red Hat, Inc.5*6* Author: Stefan Hajnoczi <[email protected]>7*/89#include <getopt.h>10#include <stdio.h>11#include <stdlib.h>12#include <string.h>13#include <errno.h>14#include <unistd.h>15#include <sys/stat.h>16#include <sys/types.h>17#include <linux/list.h>18#include <linux/net.h>19#include <linux/netlink.h>20#include <linux/sock_diag.h>21#include <linux/vm_sockets_diag.h>22#include <netinet/tcp.h>2324#include "timeout.h"25#include "control.h"26#include "util.h"2728/* Per-socket status */29struct vsock_stat {30struct list_head list;31struct vsock_diag_msg msg;32};3334static const char *sock_type_str(int type)35{36switch (type) {37case SOCK_DGRAM:38return "DGRAM";39case SOCK_STREAM:40return "STREAM";41case SOCK_SEQPACKET:42return "SEQPACKET";43default:44return "INVALID TYPE";45}46}4748static const char *sock_state_str(int state)49{50switch (state) {51case TCP_CLOSE:52return "UNCONNECTED";53case TCP_SYN_SENT:54return "CONNECTING";55case TCP_ESTABLISHED:56return "CONNECTED";57case TCP_CLOSING:58return "DISCONNECTING";59case TCP_LISTEN:60return "LISTEN";61default:62return "INVALID STATE";63}64}6566static const char *sock_shutdown_str(int shutdown)67{68switch (shutdown) {69case 1:70return "RCV_SHUTDOWN";71case 2:72return "SEND_SHUTDOWN";73case 3:74return "RCV_SHUTDOWN | SEND_SHUTDOWN";75default:76return "0";77}78}7980static void print_vsock_addr(FILE *fp, unsigned int cid, unsigned int port)81{82if (cid == VMADDR_CID_ANY)83fprintf(fp, "*:");84else85fprintf(fp, "%u:", cid);8687if (port == VMADDR_PORT_ANY)88fprintf(fp, "*");89else90fprintf(fp, "%u", port);91}9293static void print_vsock_stat(FILE *fp, struct vsock_stat *st)94{95print_vsock_addr(fp, st->msg.vdiag_src_cid, st->msg.vdiag_src_port);96fprintf(fp, " ");97print_vsock_addr(fp, st->msg.vdiag_dst_cid, st->msg.vdiag_dst_port);98fprintf(fp, " %s %s %s %u\n",99sock_type_str(st->msg.vdiag_type),100sock_state_str(st->msg.vdiag_state),101sock_shutdown_str(st->msg.vdiag_shutdown),102st->msg.vdiag_ino);103}104105static void print_vsock_stats(FILE *fp, struct list_head *head)106{107struct vsock_stat *st;108109list_for_each_entry(st, head, list)110print_vsock_stat(fp, st);111}112113static struct vsock_stat *find_vsock_stat(struct list_head *head, int fd)114{115struct vsock_stat *st;116struct stat stat;117118if (fstat(fd, &stat) < 0) {119perror("fstat");120exit(EXIT_FAILURE);121}122123list_for_each_entry(st, head, list)124if (st->msg.vdiag_ino == stat.st_ino)125return st;126127fprintf(stderr, "cannot find fd %d\n", fd);128exit(EXIT_FAILURE);129}130131static void check_no_sockets(struct list_head *head)132{133if (!list_empty(head)) {134fprintf(stderr, "expected no sockets\n");135print_vsock_stats(stderr, head);136exit(1);137}138}139140static void check_num_sockets(struct list_head *head, int expected)141{142struct list_head *node;143int n = 0;144145list_for_each(node, head)146n++;147148if (n != expected) {149fprintf(stderr, "expected %d sockets, found %d\n",150expected, n);151print_vsock_stats(stderr, head);152exit(EXIT_FAILURE);153}154}155156static void check_socket_state(struct vsock_stat *st, __u8 state)157{158if (st->msg.vdiag_state != state) {159fprintf(stderr, "expected socket state %#x, got %#x\n",160state, st->msg.vdiag_state);161exit(EXIT_FAILURE);162}163}164165static void send_req(int fd)166{167struct sockaddr_nl nladdr = {168.nl_family = AF_NETLINK,169};170struct {171struct nlmsghdr nlh;172struct vsock_diag_req vreq;173} req = {174.nlh = {175.nlmsg_len = sizeof(req),176.nlmsg_type = SOCK_DIAG_BY_FAMILY,177.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,178},179.vreq = {180.sdiag_family = AF_VSOCK,181.vdiag_states = ~(__u32)0,182},183};184struct iovec iov = {185.iov_base = &req,186.iov_len = sizeof(req),187};188struct msghdr msg = {189.msg_name = &nladdr,190.msg_namelen = sizeof(nladdr),191.msg_iov = &iov,192.msg_iovlen = 1,193};194195for (;;) {196if (sendmsg(fd, &msg, 0) < 0) {197if (errno == EINTR)198continue;199200perror("sendmsg");201exit(EXIT_FAILURE);202}203204return;205}206}207208static ssize_t recv_resp(int fd, void *buf, size_t len)209{210struct sockaddr_nl nladdr = {211.nl_family = AF_NETLINK,212};213struct iovec iov = {214.iov_base = buf,215.iov_len = len,216};217struct msghdr msg = {218.msg_name = &nladdr,219.msg_namelen = sizeof(nladdr),220.msg_iov = &iov,221.msg_iovlen = 1,222};223ssize_t ret;224225do {226ret = recvmsg(fd, &msg, 0);227} while (ret < 0 && errno == EINTR);228229if (ret < 0) {230perror("recvmsg");231exit(EXIT_FAILURE);232}233234return ret;235}236237static void add_vsock_stat(struct list_head *sockets,238const struct vsock_diag_msg *resp)239{240struct vsock_stat *st;241242st = malloc(sizeof(*st));243if (!st) {244perror("malloc");245exit(EXIT_FAILURE);246}247248st->msg = *resp;249list_add_tail(&st->list, sockets);250}251252/*253* Read vsock stats into a list.254*/255static void read_vsock_stat(struct list_head *sockets)256{257long buf[8192 / sizeof(long)];258int fd;259260fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);261if (fd < 0) {262perror("socket");263exit(EXIT_FAILURE);264}265266send_req(fd);267268for (;;) {269const struct nlmsghdr *h;270ssize_t ret;271272ret = recv_resp(fd, buf, sizeof(buf));273if (ret == 0)274goto done;275if (ret < sizeof(*h)) {276fprintf(stderr, "short read of %zd bytes\n", ret);277exit(EXIT_FAILURE);278}279280h = (struct nlmsghdr *)buf;281282while (NLMSG_OK(h, ret)) {283if (h->nlmsg_type == NLMSG_DONE)284goto done;285286if (h->nlmsg_type == NLMSG_ERROR) {287const struct nlmsgerr *err = NLMSG_DATA(h);288289if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))290fprintf(stderr, "NLMSG_ERROR\n");291else {292errno = -err->error;293perror("NLMSG_ERROR");294}295296exit(EXIT_FAILURE);297}298299if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY) {300fprintf(stderr, "unexpected nlmsg_type %#x\n",301h->nlmsg_type);302exit(EXIT_FAILURE);303}304if (h->nlmsg_len <305NLMSG_LENGTH(sizeof(struct vsock_diag_msg))) {306fprintf(stderr, "short vsock_diag_msg\n");307exit(EXIT_FAILURE);308}309310add_vsock_stat(sockets, NLMSG_DATA(h));311312h = NLMSG_NEXT(h, ret);313}314}315316done:317close(fd);318}319320static void free_sock_stat(struct list_head *sockets)321{322struct vsock_stat *st;323struct vsock_stat *next;324325list_for_each_entry_safe(st, next, sockets, list)326free(st);327}328329static void test_no_sockets(const struct test_opts *opts)330{331LIST_HEAD(sockets);332333read_vsock_stat(&sockets);334335check_no_sockets(&sockets);336}337338static void test_listen_socket_server(const struct test_opts *opts)339{340union {341struct sockaddr sa;342struct sockaddr_vm svm;343} addr = {344.svm = {345.svm_family = AF_VSOCK,346.svm_port = opts->peer_port,347.svm_cid = VMADDR_CID_ANY,348},349};350LIST_HEAD(sockets);351struct vsock_stat *st;352int fd;353354fd = socket(AF_VSOCK, SOCK_STREAM, 0);355356if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {357perror("bind");358exit(EXIT_FAILURE);359}360361if (listen(fd, 1) < 0) {362perror("listen");363exit(EXIT_FAILURE);364}365366read_vsock_stat(&sockets);367368check_num_sockets(&sockets, 1);369st = find_vsock_stat(&sockets, fd);370check_socket_state(st, TCP_LISTEN);371372close(fd);373free_sock_stat(&sockets);374}375376static void test_connect_client(const struct test_opts *opts)377{378int fd;379LIST_HEAD(sockets);380struct vsock_stat *st;381382fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);383if (fd < 0) {384perror("connect");385exit(EXIT_FAILURE);386}387388read_vsock_stat(&sockets);389390check_num_sockets(&sockets, 1);391st = find_vsock_stat(&sockets, fd);392check_socket_state(st, TCP_ESTABLISHED);393394control_expectln("DONE");395control_writeln("DONE");396397close(fd);398free_sock_stat(&sockets);399}400401static void test_connect_server(const struct test_opts *opts)402{403struct vsock_stat *st;404LIST_HEAD(sockets);405int client_fd;406407client_fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);408if (client_fd < 0) {409perror("accept");410exit(EXIT_FAILURE);411}412413read_vsock_stat(&sockets);414415check_num_sockets(&sockets, 1);416st = find_vsock_stat(&sockets, client_fd);417check_socket_state(st, TCP_ESTABLISHED);418419control_writeln("DONE");420control_expectln("DONE");421422close(client_fd);423free_sock_stat(&sockets);424}425426static struct test_case test_cases[] = {427{428.name = "No sockets",429.run_server = test_no_sockets,430},431{432.name = "Listen socket",433.run_server = test_listen_socket_server,434},435{436.name = "Connect",437.run_client = test_connect_client,438.run_server = test_connect_server,439},440{},441};442443static const char optstring[] = "";444static const struct option longopts[] = {445{446.name = "control-host",447.has_arg = required_argument,448.val = 'H',449},450{451.name = "control-port",452.has_arg = required_argument,453.val = 'P',454},455{456.name = "mode",457.has_arg = required_argument,458.val = 'm',459},460{461.name = "peer-cid",462.has_arg = required_argument,463.val = 'p',464},465{466.name = "peer-port",467.has_arg = required_argument,468.val = 'q',469},470{471.name = "list",472.has_arg = no_argument,473.val = 'l',474},475{476.name = "skip",477.has_arg = required_argument,478.val = 's',479},480{481.name = "help",482.has_arg = no_argument,483.val = '?',484},485{},486};487488static void usage(void)489{490fprintf(stderr, "Usage: vsock_diag_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--peer-port=<port>] [--list] [--skip=<test_id>]\n"491"\n"492" Server: vsock_diag_test --control-port=1234 --mode=server --peer-cid=3\n"493" Client: vsock_diag_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"494"\n"495"Run vsock_diag.ko tests. Must be launched in both\n"496"guest and host. One side must use --mode=client and\n"497"the other side must use --mode=server.\n"498"\n"499"A TCP control socket connection is used to coordinate tests\n"500"between the client and the server. The server requires a\n"501"listen address and the client requires an address to\n"502"connect to.\n"503"\n"504"The CID of the other side must be given with --peer-cid=<cid>.\n"505"\n"506"Options:\n"507" --help This help message\n"508" --control-host <host> Server IP address to connect to\n"509" --control-port <port> Server port to listen on/connect to\n"510" --mode client|server Server or client mode\n"511" --peer-cid <cid> CID of the other side\n"512" --peer-port <port> AF_VSOCK port used for the test [default: %d]\n"513" --list List of tests that will be executed\n"514" --skip <test_id> Test ID to skip;\n"515" use multiple --skip options to skip more tests\n",516DEFAULT_PEER_PORT517);518exit(EXIT_FAILURE);519}520521int main(int argc, char **argv)522{523const char *control_host = NULL;524const char *control_port = NULL;525struct test_opts opts = {526.mode = TEST_MODE_UNSET,527.peer_cid = VMADDR_CID_ANY,528.peer_port = DEFAULT_PEER_PORT,529};530531init_signals();532533for (;;) {534int opt = getopt_long(argc, argv, optstring, longopts, NULL);535536if (opt == -1)537break;538539switch (opt) {540case 'H':541control_host = optarg;542break;543case 'm':544if (strcmp(optarg, "client") == 0)545opts.mode = TEST_MODE_CLIENT;546else if (strcmp(optarg, "server") == 0)547opts.mode = TEST_MODE_SERVER;548else {549fprintf(stderr, "--mode must be \"client\" or \"server\"\n");550return EXIT_FAILURE;551}552break;553case 'p':554opts.peer_cid = parse_cid(optarg);555break;556case 'q':557opts.peer_port = parse_port(optarg);558break;559case 'P':560control_port = optarg;561break;562case 'l':563list_tests(test_cases);564break;565case 's':566skip_test(test_cases, ARRAY_SIZE(test_cases) - 1,567optarg);568break;569case '?':570default:571usage();572}573}574575if (!control_port)576usage();577if (opts.mode == TEST_MODE_UNSET)578usage();579if (opts.peer_cid == VMADDR_CID_ANY)580usage();581582if (!control_host) {583if (opts.mode != TEST_MODE_SERVER)584usage();585control_host = "0.0.0.0";586}587588control_init(control_host, control_port,589opts.mode == TEST_MODE_SERVER);590591run_tests(test_cases, &opts);592593control_cleanup();594return EXIT_SUCCESS;595}596597598