cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

vsock_test.c (16665B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * vsock_test - vsock.ko test suite
      4 *
      5 * Copyright (C) 2017 Red Hat, Inc.
      6 *
      7 * Author: Stefan Hajnoczi <stefanha@redhat.com>
      8 */
      9
     10#include <getopt.h>
     11#include <stdio.h>
     12#include <stdlib.h>
     13#include <string.h>
     14#include <errno.h>
     15#include <unistd.h>
     16#include <linux/kernel.h>
     17#include <sys/types.h>
     18#include <sys/socket.h>
     19#include <time.h>
     20#include <sys/mman.h>
     21
     22#include "timeout.h"
     23#include "control.h"
     24#include "util.h"
     25
     26static void test_stream_connection_reset(const struct test_opts *opts)
     27{
     28	union {
     29		struct sockaddr sa;
     30		struct sockaddr_vm svm;
     31	} addr = {
     32		.svm = {
     33			.svm_family = AF_VSOCK,
     34			.svm_port = 1234,
     35			.svm_cid = opts->peer_cid,
     36		},
     37	};
     38	int ret;
     39	int fd;
     40
     41	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
     42
     43	timeout_begin(TIMEOUT);
     44	do {
     45		ret = connect(fd, &addr.sa, sizeof(addr.svm));
     46		timeout_check("connect");
     47	} while (ret < 0 && errno == EINTR);
     48	timeout_end();
     49
     50	if (ret != -1) {
     51		fprintf(stderr, "expected connect(2) failure, got %d\n", ret);
     52		exit(EXIT_FAILURE);
     53	}
     54	if (errno != ECONNRESET) {
     55		fprintf(stderr, "unexpected connect(2) errno %d\n", errno);
     56		exit(EXIT_FAILURE);
     57	}
     58
     59	close(fd);
     60}
     61
     62static void test_stream_bind_only_client(const struct test_opts *opts)
     63{
     64	union {
     65		struct sockaddr sa;
     66		struct sockaddr_vm svm;
     67	} addr = {
     68		.svm = {
     69			.svm_family = AF_VSOCK,
     70			.svm_port = 1234,
     71			.svm_cid = opts->peer_cid,
     72		},
     73	};
     74	int ret;
     75	int fd;
     76
     77	/* Wait for the server to be ready */
     78	control_expectln("BIND");
     79
     80	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
     81
     82	timeout_begin(TIMEOUT);
     83	do {
     84		ret = connect(fd, &addr.sa, sizeof(addr.svm));
     85		timeout_check("connect");
     86	} while (ret < 0 && errno == EINTR);
     87	timeout_end();
     88
     89	if (ret != -1) {
     90		fprintf(stderr, "expected connect(2) failure, got %d\n", ret);
     91		exit(EXIT_FAILURE);
     92	}
     93	if (errno != ECONNRESET) {
     94		fprintf(stderr, "unexpected connect(2) errno %d\n", errno);
     95		exit(EXIT_FAILURE);
     96	}
     97
     98	/* Notify the server that the client has finished */
     99	control_writeln("DONE");
    100
    101	close(fd);
    102}
    103
    104static void test_stream_bind_only_server(const struct test_opts *opts)
    105{
    106	union {
    107		struct sockaddr sa;
    108		struct sockaddr_vm svm;
    109	} addr = {
    110		.svm = {
    111			.svm_family = AF_VSOCK,
    112			.svm_port = 1234,
    113			.svm_cid = VMADDR_CID_ANY,
    114		},
    115	};
    116	int fd;
    117
    118	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
    119
    120	if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {
    121		perror("bind");
    122		exit(EXIT_FAILURE);
    123	}
    124
    125	/* Notify the client that the server is ready */
    126	control_writeln("BIND");
    127
    128	/* Wait for the client to finish */
    129	control_expectln("DONE");
    130
    131	close(fd);
    132}
    133
    134static void test_stream_client_close_client(const struct test_opts *opts)
    135{
    136	int fd;
    137
    138	fd = vsock_stream_connect(opts->peer_cid, 1234);
    139	if (fd < 0) {
    140		perror("connect");
    141		exit(EXIT_FAILURE);
    142	}
    143
    144	send_byte(fd, 1, 0);
    145	close(fd);
    146}
    147
    148static void test_stream_client_close_server(const struct test_opts *opts)
    149{
    150	int fd;
    151
    152	fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
    153	if (fd < 0) {
    154		perror("accept");
    155		exit(EXIT_FAILURE);
    156	}
    157
    158	/* Wait for the remote to close the connection, before check
    159	 * -EPIPE error on send.
    160	 */
    161	vsock_wait_remote_close(fd);
    162
    163	send_byte(fd, -EPIPE, 0);
    164	recv_byte(fd, 1, 0);
    165	recv_byte(fd, 0, 0);
    166	close(fd);
    167}
    168
    169static void test_stream_server_close_client(const struct test_opts *opts)
    170{
    171	int fd;
    172
    173	fd = vsock_stream_connect(opts->peer_cid, 1234);
    174	if (fd < 0) {
    175		perror("connect");
    176		exit(EXIT_FAILURE);
    177	}
    178
    179	/* Wait for the remote to close the connection, before check
    180	 * -EPIPE error on send.
    181	 */
    182	vsock_wait_remote_close(fd);
    183
    184	send_byte(fd, -EPIPE, 0);
    185	recv_byte(fd, 1, 0);
    186	recv_byte(fd, 0, 0);
    187	close(fd);
    188}
    189
    190static void test_stream_server_close_server(const struct test_opts *opts)
    191{
    192	int fd;
    193
    194	fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
    195	if (fd < 0) {
    196		perror("accept");
    197		exit(EXIT_FAILURE);
    198	}
    199
    200	send_byte(fd, 1, 0);
    201	close(fd);
    202}
    203
    204/* With the standard socket sizes, VMCI is able to support about 100
    205 * concurrent stream connections.
    206 */
    207#define MULTICONN_NFDS 100
    208
    209static void test_stream_multiconn_client(const struct test_opts *opts)
    210{
    211	int fds[MULTICONN_NFDS];
    212	int i;
    213
    214	for (i = 0; i < MULTICONN_NFDS; i++) {
    215		fds[i] = vsock_stream_connect(opts->peer_cid, 1234);
    216		if (fds[i] < 0) {
    217			perror("connect");
    218			exit(EXIT_FAILURE);
    219		}
    220	}
    221
    222	for (i = 0; i < MULTICONN_NFDS; i++) {
    223		if (i % 2)
    224			recv_byte(fds[i], 1, 0);
    225		else
    226			send_byte(fds[i], 1, 0);
    227	}
    228
    229	for (i = 0; i < MULTICONN_NFDS; i++)
    230		close(fds[i]);
    231}
    232
    233static void test_stream_multiconn_server(const struct test_opts *opts)
    234{
    235	int fds[MULTICONN_NFDS];
    236	int i;
    237
    238	for (i = 0; i < MULTICONN_NFDS; i++) {
    239		fds[i] = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
    240		if (fds[i] < 0) {
    241			perror("accept");
    242			exit(EXIT_FAILURE);
    243		}
    244	}
    245
    246	for (i = 0; i < MULTICONN_NFDS; i++) {
    247		if (i % 2)
    248			send_byte(fds[i], 1, 0);
    249		else
    250			recv_byte(fds[i], 1, 0);
    251	}
    252
    253	for (i = 0; i < MULTICONN_NFDS; i++)
    254		close(fds[i]);
    255}
    256
    257static void test_stream_msg_peek_client(const struct test_opts *opts)
    258{
    259	int fd;
    260
    261	fd = vsock_stream_connect(opts->peer_cid, 1234);
    262	if (fd < 0) {
    263		perror("connect");
    264		exit(EXIT_FAILURE);
    265	}
    266
    267	send_byte(fd, 1, 0);
    268	close(fd);
    269}
    270
    271static void test_stream_msg_peek_server(const struct test_opts *opts)
    272{
    273	int fd;
    274
    275	fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
    276	if (fd < 0) {
    277		perror("accept");
    278		exit(EXIT_FAILURE);
    279	}
    280
    281	recv_byte(fd, 1, MSG_PEEK);
    282	recv_byte(fd, 1, 0);
    283	close(fd);
    284}
    285
    286#define MESSAGES_CNT 7
    287#define MSG_EOR_IDX (MESSAGES_CNT / 2)
    288static void test_seqpacket_msg_bounds_client(const struct test_opts *opts)
    289{
    290	int fd;
    291
    292	fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
    293	if (fd < 0) {
    294		perror("connect");
    295		exit(EXIT_FAILURE);
    296	}
    297
    298	/* Send several messages, one with MSG_EOR flag */
    299	for (int i = 0; i < MESSAGES_CNT; i++)
    300		send_byte(fd, 1, (i == MSG_EOR_IDX) ? MSG_EOR : 0);
    301
    302	control_writeln("SENDDONE");
    303	close(fd);
    304}
    305
    306static void test_seqpacket_msg_bounds_server(const struct test_opts *opts)
    307{
    308	int fd;
    309	char buf[16];
    310	struct msghdr msg = {0};
    311	struct iovec iov = {0};
    312
    313	fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
    314	if (fd < 0) {
    315		perror("accept");
    316		exit(EXIT_FAILURE);
    317	}
    318
    319	control_expectln("SENDDONE");
    320	iov.iov_base = buf;
    321	iov.iov_len = sizeof(buf);
    322	msg.msg_iov = &iov;
    323	msg.msg_iovlen = 1;
    324
    325	for (int i = 0; i < MESSAGES_CNT; i++) {
    326		if (recvmsg(fd, &msg, 0) != 1) {
    327			perror("message bound violated");
    328			exit(EXIT_FAILURE);
    329		}
    330
    331		if ((i == MSG_EOR_IDX) ^ !!(msg.msg_flags & MSG_EOR)) {
    332			perror("MSG_EOR");
    333			exit(EXIT_FAILURE);
    334		}
    335	}
    336
    337	close(fd);
    338}
    339
    340#define MESSAGE_TRUNC_SZ 32
    341static void test_seqpacket_msg_trunc_client(const struct test_opts *opts)
    342{
    343	int fd;
    344	char buf[MESSAGE_TRUNC_SZ];
    345
    346	fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
    347	if (fd < 0) {
    348		perror("connect");
    349		exit(EXIT_FAILURE);
    350	}
    351
    352	if (send(fd, buf, sizeof(buf), 0) != sizeof(buf)) {
    353		perror("send failed");
    354		exit(EXIT_FAILURE);
    355	}
    356
    357	control_writeln("SENDDONE");
    358	close(fd);
    359}
    360
    361static void test_seqpacket_msg_trunc_server(const struct test_opts *opts)
    362{
    363	int fd;
    364	char buf[MESSAGE_TRUNC_SZ / 2];
    365	struct msghdr msg = {0};
    366	struct iovec iov = {0};
    367
    368	fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
    369	if (fd < 0) {
    370		perror("accept");
    371		exit(EXIT_FAILURE);
    372	}
    373
    374	control_expectln("SENDDONE");
    375	iov.iov_base = buf;
    376	iov.iov_len = sizeof(buf);
    377	msg.msg_iov = &iov;
    378	msg.msg_iovlen = 1;
    379
    380	ssize_t ret = recvmsg(fd, &msg, MSG_TRUNC);
    381
    382	if (ret != MESSAGE_TRUNC_SZ) {
    383		printf("%zi\n", ret);
    384		perror("MSG_TRUNC doesn't work");
    385		exit(EXIT_FAILURE);
    386	}
    387
    388	if (!(msg.msg_flags & MSG_TRUNC)) {
    389		fprintf(stderr, "MSG_TRUNC expected\n");
    390		exit(EXIT_FAILURE);
    391	}
    392
    393	close(fd);
    394}
    395
    396static time_t current_nsec(void)
    397{
    398	struct timespec ts;
    399
    400	if (clock_gettime(CLOCK_REALTIME, &ts)) {
    401		perror("clock_gettime(3) failed");
    402		exit(EXIT_FAILURE);
    403	}
    404
    405	return (ts.tv_sec * 1000000000ULL) + ts.tv_nsec;
    406}
    407
    408#define RCVTIMEO_TIMEOUT_SEC 1
    409#define READ_OVERHEAD_NSEC 250000000 /* 0.25 sec */
    410
    411static void test_seqpacket_timeout_client(const struct test_opts *opts)
    412{
    413	int fd;
    414	struct timeval tv;
    415	char dummy;
    416	time_t read_enter_ns;
    417	time_t read_overhead_ns;
    418
    419	fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
    420	if (fd < 0) {
    421		perror("connect");
    422		exit(EXIT_FAILURE);
    423	}
    424
    425	tv.tv_sec = RCVTIMEO_TIMEOUT_SEC;
    426	tv.tv_usec = 0;
    427
    428	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv)) == -1) {
    429		perror("setsockopt 'SO_RCVTIMEO'");
    430		exit(EXIT_FAILURE);
    431	}
    432
    433	read_enter_ns = current_nsec();
    434
    435	if (read(fd, &dummy, sizeof(dummy)) != -1) {
    436		fprintf(stderr,
    437			"expected 'dummy' read(2) failure\n");
    438		exit(EXIT_FAILURE);
    439	}
    440
    441	if (errno != EAGAIN) {
    442		perror("EAGAIN expected");
    443		exit(EXIT_FAILURE);
    444	}
    445
    446	read_overhead_ns = current_nsec() - read_enter_ns -
    447			1000000000ULL * RCVTIMEO_TIMEOUT_SEC;
    448
    449	if (read_overhead_ns > READ_OVERHEAD_NSEC) {
    450		fprintf(stderr,
    451			"too much time in read(2), %lu > %i ns\n",
    452			read_overhead_ns, READ_OVERHEAD_NSEC);
    453		exit(EXIT_FAILURE);
    454	}
    455
    456	control_writeln("WAITDONE");
    457	close(fd);
    458}
    459
    460static void test_seqpacket_timeout_server(const struct test_opts *opts)
    461{
    462	int fd;
    463
    464	fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
    465	if (fd < 0) {
    466		perror("accept");
    467		exit(EXIT_FAILURE);
    468	}
    469
    470	control_expectln("WAITDONE");
    471	close(fd);
    472}
    473
    474#define BUF_PATTERN_1 'a'
    475#define BUF_PATTERN_2 'b'
    476
    477static void test_seqpacket_invalid_rec_buffer_client(const struct test_opts *opts)
    478{
    479	int fd;
    480	unsigned char *buf1;
    481	unsigned char *buf2;
    482	int buf_size = getpagesize() * 3;
    483
    484	fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
    485	if (fd < 0) {
    486		perror("connect");
    487		exit(EXIT_FAILURE);
    488	}
    489
    490	buf1 = malloc(buf_size);
    491	if (!buf1) {
    492		perror("'malloc()' for 'buf1'");
    493		exit(EXIT_FAILURE);
    494	}
    495
    496	buf2 = malloc(buf_size);
    497	if (!buf2) {
    498		perror("'malloc()' for 'buf2'");
    499		exit(EXIT_FAILURE);
    500	}
    501
    502	memset(buf1, BUF_PATTERN_1, buf_size);
    503	memset(buf2, BUF_PATTERN_2, buf_size);
    504
    505	if (send(fd, buf1, buf_size, 0) != buf_size) {
    506		perror("send failed");
    507		exit(EXIT_FAILURE);
    508	}
    509
    510	if (send(fd, buf2, buf_size, 0) != buf_size) {
    511		perror("send failed");
    512		exit(EXIT_FAILURE);
    513	}
    514
    515	close(fd);
    516}
    517
    518static void test_seqpacket_invalid_rec_buffer_server(const struct test_opts *opts)
    519{
    520	int fd;
    521	unsigned char *broken_buf;
    522	unsigned char *valid_buf;
    523	int page_size = getpagesize();
    524	int buf_size = page_size * 3;
    525	ssize_t res;
    526	int prot = PROT_READ | PROT_WRITE;
    527	int flags = MAP_PRIVATE | MAP_ANONYMOUS;
    528	int i;
    529
    530	fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
    531	if (fd < 0) {
    532		perror("accept");
    533		exit(EXIT_FAILURE);
    534	}
    535
    536	/* Setup first buffer. */
    537	broken_buf = mmap(NULL, buf_size, prot, flags, -1, 0);
    538	if (broken_buf == MAP_FAILED) {
    539		perror("mmap for 'broken_buf'");
    540		exit(EXIT_FAILURE);
    541	}
    542
    543	/* Unmap "hole" in buffer. */
    544	if (munmap(broken_buf + page_size, page_size)) {
    545		perror("'broken_buf' setup");
    546		exit(EXIT_FAILURE);
    547	}
    548
    549	valid_buf = mmap(NULL, buf_size, prot, flags, -1, 0);
    550	if (valid_buf == MAP_FAILED) {
    551		perror("mmap for 'valid_buf'");
    552		exit(EXIT_FAILURE);
    553	}
    554
    555	/* Try to fill buffer with unmapped middle. */
    556	res = read(fd, broken_buf, buf_size);
    557	if (res != -1) {
    558		fprintf(stderr,
    559			"expected 'broken_buf' read(2) failure, got %zi\n",
    560			res);
    561		exit(EXIT_FAILURE);
    562	}
    563
    564	if (errno != ENOMEM) {
    565		perror("unexpected errno of 'broken_buf'");
    566		exit(EXIT_FAILURE);
    567	}
    568
    569	/* Try to fill valid buffer. */
    570	res = read(fd, valid_buf, buf_size);
    571	if (res < 0) {
    572		perror("unexpected 'valid_buf' read(2) failure");
    573		exit(EXIT_FAILURE);
    574	}
    575
    576	if (res != buf_size) {
    577		fprintf(stderr,
    578			"invalid 'valid_buf' read(2), expected %i, got %zi\n",
    579			buf_size, res);
    580		exit(EXIT_FAILURE);
    581	}
    582
    583	for (i = 0; i < buf_size; i++) {
    584		if (valid_buf[i] != BUF_PATTERN_2) {
    585			fprintf(stderr,
    586				"invalid pattern for 'valid_buf' at %i, expected %hhX, got %hhX\n",
    587				i, BUF_PATTERN_2, valid_buf[i]);
    588			exit(EXIT_FAILURE);
    589		}
    590	}
    591
    592	/* Unmap buffers. */
    593	munmap(broken_buf, page_size);
    594	munmap(broken_buf + page_size * 2, page_size);
    595	munmap(valid_buf, buf_size);
    596	close(fd);
    597}
    598
    599static struct test_case test_cases[] = {
    600	{
    601		.name = "SOCK_STREAM connection reset",
    602		.run_client = test_stream_connection_reset,
    603	},
    604	{
    605		.name = "SOCK_STREAM bind only",
    606		.run_client = test_stream_bind_only_client,
    607		.run_server = test_stream_bind_only_server,
    608	},
    609	{
    610		.name = "SOCK_STREAM client close",
    611		.run_client = test_stream_client_close_client,
    612		.run_server = test_stream_client_close_server,
    613	},
    614	{
    615		.name = "SOCK_STREAM server close",
    616		.run_client = test_stream_server_close_client,
    617		.run_server = test_stream_server_close_server,
    618	},
    619	{
    620		.name = "SOCK_STREAM multiple connections",
    621		.run_client = test_stream_multiconn_client,
    622		.run_server = test_stream_multiconn_server,
    623	},
    624	{
    625		.name = "SOCK_STREAM MSG_PEEK",
    626		.run_client = test_stream_msg_peek_client,
    627		.run_server = test_stream_msg_peek_server,
    628	},
    629	{
    630		.name = "SOCK_SEQPACKET msg bounds",
    631		.run_client = test_seqpacket_msg_bounds_client,
    632		.run_server = test_seqpacket_msg_bounds_server,
    633	},
    634	{
    635		.name = "SOCK_SEQPACKET MSG_TRUNC flag",
    636		.run_client = test_seqpacket_msg_trunc_client,
    637		.run_server = test_seqpacket_msg_trunc_server,
    638	},
    639	{
    640		.name = "SOCK_SEQPACKET timeout",
    641		.run_client = test_seqpacket_timeout_client,
    642		.run_server = test_seqpacket_timeout_server,
    643	},
    644	{
    645		.name = "SOCK_SEQPACKET invalid receive buffer",
    646		.run_client = test_seqpacket_invalid_rec_buffer_client,
    647		.run_server = test_seqpacket_invalid_rec_buffer_server,
    648	},
    649	{},
    650};
    651
    652static const char optstring[] = "";
    653static const struct option longopts[] = {
    654	{
    655		.name = "control-host",
    656		.has_arg = required_argument,
    657		.val = 'H',
    658	},
    659	{
    660		.name = "control-port",
    661		.has_arg = required_argument,
    662		.val = 'P',
    663	},
    664	{
    665		.name = "mode",
    666		.has_arg = required_argument,
    667		.val = 'm',
    668	},
    669	{
    670		.name = "peer-cid",
    671		.has_arg = required_argument,
    672		.val = 'p',
    673	},
    674	{
    675		.name = "list",
    676		.has_arg = no_argument,
    677		.val = 'l',
    678	},
    679	{
    680		.name = "skip",
    681		.has_arg = required_argument,
    682		.val = 's',
    683	},
    684	{
    685		.name = "help",
    686		.has_arg = no_argument,
    687		.val = '?',
    688	},
    689	{},
    690};
    691
    692static void usage(void)
    693{
    694	fprintf(stderr, "Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--list] [--skip=<test_id>]\n"
    695		"\n"
    696		"  Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n"
    697		"  Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"
    698		"\n"
    699		"Run vsock.ko tests.  Must be launched in both guest\n"
    700		"and host.  One side must use --mode=client and\n"
    701		"the other side must use --mode=server.\n"
    702		"\n"
    703		"A TCP control socket connection is used to coordinate tests\n"
    704		"between the client and the server.  The server requires a\n"
    705		"listen address and the client requires an address to\n"
    706		"connect to.\n"
    707		"\n"
    708		"The CID of the other side must be given with --peer-cid=<cid>.\n"
    709		"\n"
    710		"Options:\n"
    711		"  --help                 This help message\n"
    712		"  --control-host <host>  Server IP address to connect to\n"
    713		"  --control-port <port>  Server port to listen on/connect to\n"
    714		"  --mode client|server   Server or client mode\n"
    715		"  --peer-cid <cid>       CID of the other side\n"
    716		"  --list                 List of tests that will be executed\n"
    717		"  --skip <test_id>       Test ID to skip;\n"
    718		"                         use multiple --skip options to skip more tests\n"
    719		);
    720	exit(EXIT_FAILURE);
    721}
    722
    723int main(int argc, char **argv)
    724{
    725	const char *control_host = NULL;
    726	const char *control_port = NULL;
    727	struct test_opts opts = {
    728		.mode = TEST_MODE_UNSET,
    729		.peer_cid = VMADDR_CID_ANY,
    730	};
    731
    732	init_signals();
    733
    734	for (;;) {
    735		int opt = getopt_long(argc, argv, optstring, longopts, NULL);
    736
    737		if (opt == -1)
    738			break;
    739
    740		switch (opt) {
    741		case 'H':
    742			control_host = optarg;
    743			break;
    744		case 'm':
    745			if (strcmp(optarg, "client") == 0)
    746				opts.mode = TEST_MODE_CLIENT;
    747			else if (strcmp(optarg, "server") == 0)
    748				opts.mode = TEST_MODE_SERVER;
    749			else {
    750				fprintf(stderr, "--mode must be \"client\" or \"server\"\n");
    751				return EXIT_FAILURE;
    752			}
    753			break;
    754		case 'p':
    755			opts.peer_cid = parse_cid(optarg);
    756			break;
    757		case 'P':
    758			control_port = optarg;
    759			break;
    760		case 'l':
    761			list_tests(test_cases);
    762			break;
    763		case 's':
    764			skip_test(test_cases, ARRAY_SIZE(test_cases) - 1,
    765				  optarg);
    766			break;
    767		case '?':
    768		default:
    769			usage();
    770		}
    771	}
    772
    773	if (!control_port)
    774		usage();
    775	if (opts.mode == TEST_MODE_UNSET)
    776		usage();
    777	if (opts.peer_cid == VMADDR_CID_ANY)
    778		usage();
    779
    780	if (!control_host) {
    781		if (opts.mode != TEST_MODE_SERVER)
    782			usage();
    783		control_host = "0.0.0.0";
    784	}
    785
    786	control_init(control_host, control_port,
    787		     opts.mode == TEST_MODE_SERVER);
    788
    789	run_tests(test_cases, &opts);
    790
    791	control_cleanup();
    792	return EXIT_SUCCESS;
    793}