2019-12-18 19:07:04 +01:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* vsock_test - vsock . ko test suite
*
* Copyright ( C ) 2017 Red Hat , Inc .
*
* Author : Stefan Hajnoczi < stefanha @ redhat . com >
*/
# include <getopt.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include <unistd.h>
2019-12-18 19:07:06 +01:00
# include <linux/kernel.h>
2019-12-18 19:07:04 +01:00
# include "timeout.h"
# include "control.h"
# include "util.h"
static void test_stream_connection_reset ( const struct test_opts * opts )
{
union {
struct sockaddr sa ;
struct sockaddr_vm svm ;
} addr = {
. svm = {
. svm_family = AF_VSOCK ,
. svm_port = 1234 ,
. svm_cid = opts - > peer_cid ,
} ,
} ;
int ret ;
int fd ;
fd = socket ( AF_VSOCK , SOCK_STREAM , 0 ) ;
timeout_begin ( TIMEOUT ) ;
do {
ret = connect ( fd , & addr . sa , sizeof ( addr . svm ) ) ;
timeout_check ( " connect " ) ;
} while ( ret < 0 & & errno = = EINTR ) ;
timeout_end ( ) ;
if ( ret ! = - 1 ) {
fprintf ( stderr , " expected connect(2) failure, got %d \n " , ret ) ;
exit ( EXIT_FAILURE ) ;
}
if ( errno ! = ECONNRESET ) {
fprintf ( stderr , " unexpected connect(2) errno %d \n " , errno ) ;
exit ( EXIT_FAILURE ) ;
}
close ( fd ) ;
}
static void test_stream_client_close_client ( const struct test_opts * opts )
{
int fd ;
fd = vsock_stream_connect ( opts - > peer_cid , 1234 ) ;
if ( fd < 0 ) {
perror ( " connect " ) ;
exit ( EXIT_FAILURE ) ;
}
send_byte ( fd , 1 , 0 ) ;
close ( fd ) ;
}
static void test_stream_client_close_server ( const struct test_opts * opts )
{
int fd ;
fd = vsock_stream_accept ( VMADDR_CID_ANY , 1234 , NULL ) ;
if ( fd < 0 ) {
perror ( " accept " ) ;
exit ( EXIT_FAILURE ) ;
}
2019-12-18 19:07:05 +01:00
/* Wait for the remote to close the connection, before check
* - EPIPE error on send .
*/
vsock_wait_remote_close ( fd ) ;
2019-12-18 19:07:04 +01:00
send_byte ( fd , - EPIPE , 0 ) ;
recv_byte ( fd , 1 , 0 ) ;
recv_byte ( fd , 0 , 0 ) ;
close ( fd ) ;
}
static void test_stream_server_close_client ( const struct test_opts * opts )
{
int fd ;
fd = vsock_stream_connect ( opts - > peer_cid , 1234 ) ;
if ( fd < 0 ) {
perror ( " connect " ) ;
exit ( EXIT_FAILURE ) ;
}
2019-12-18 19:07:05 +01:00
/* Wait for the remote to close the connection, before check
* - EPIPE error on send .
*/
vsock_wait_remote_close ( fd ) ;
2019-12-18 19:07:04 +01:00
send_byte ( fd , - EPIPE , 0 ) ;
recv_byte ( fd , 1 , 0 ) ;
recv_byte ( fd , 0 , 0 ) ;
close ( fd ) ;
}
static void test_stream_server_close_server ( const struct test_opts * opts )
{
int fd ;
fd = vsock_stream_accept ( VMADDR_CID_ANY , 1234 , NULL ) ;
if ( fd < 0 ) {
perror ( " accept " ) ;
exit ( EXIT_FAILURE ) ;
}
send_byte ( fd , 1 , 0 ) ;
close ( fd ) ;
}
/* With the standard socket sizes, VMCI is able to support about 100
* concurrent stream connections .
*/
# define MULTICONN_NFDS 100
static void test_stream_multiconn_client ( const struct test_opts * opts )
{
int fds [ MULTICONN_NFDS ] ;
int i ;
for ( i = 0 ; i < MULTICONN_NFDS ; i + + ) {
fds [ i ] = vsock_stream_connect ( opts - > peer_cid , 1234 ) ;
if ( fds [ i ] < 0 ) {
perror ( " connect " ) ;
exit ( EXIT_FAILURE ) ;
}
}
for ( i = 0 ; i < MULTICONN_NFDS ; i + + ) {
if ( i % 2 )
recv_byte ( fds [ i ] , 1 , 0 ) ;
else
send_byte ( fds [ i ] , 1 , 0 ) ;
}
for ( i = 0 ; i < MULTICONN_NFDS ; i + + )
close ( fds [ i ] ) ;
}
static void test_stream_multiconn_server ( const struct test_opts * opts )
{
int fds [ MULTICONN_NFDS ] ;
int i ;
for ( i = 0 ; i < MULTICONN_NFDS ; i + + ) {
fds [ i ] = vsock_stream_accept ( VMADDR_CID_ANY , 1234 , NULL ) ;
if ( fds [ i ] < 0 ) {
perror ( " accept " ) ;
exit ( EXIT_FAILURE ) ;
}
}
for ( i = 0 ; i < MULTICONN_NFDS ; i + + ) {
if ( i % 2 )
send_byte ( fds [ i ] , 1 , 0 ) ;
else
recv_byte ( fds [ i ] , 1 , 0 ) ;
}
for ( i = 0 ; i < MULTICONN_NFDS ; i + + )
close ( fds [ i ] ) ;
}
2019-12-18 19:07:08 +01:00
static void test_stream_msg_peek_client ( const struct test_opts * opts )
{
int fd ;
fd = vsock_stream_connect ( opts - > peer_cid , 1234 ) ;
if ( fd < 0 ) {
perror ( " connect " ) ;
exit ( EXIT_FAILURE ) ;
}
send_byte ( fd , 1 , 0 ) ;
close ( fd ) ;
}
static void test_stream_msg_peek_server ( const struct test_opts * opts )
{
int fd ;
fd = vsock_stream_accept ( VMADDR_CID_ANY , 1234 , NULL ) ;
if ( fd < 0 ) {
perror ( " accept " ) ;
exit ( EXIT_FAILURE ) ;
}
recv_byte ( fd , 1 , MSG_PEEK ) ;
recv_byte ( fd , 1 , 0 ) ;
close ( fd ) ;
}
2019-12-18 19:07:04 +01:00
static struct test_case test_cases [ ] = {
{
. name = " SOCK_STREAM connection reset " ,
. run_client = test_stream_connection_reset ,
} ,
{
. name = " SOCK_STREAM client close " ,
. run_client = test_stream_client_close_client ,
. run_server = test_stream_client_close_server ,
} ,
{
. name = " SOCK_STREAM server close " ,
. run_client = test_stream_server_close_client ,
. run_server = test_stream_server_close_server ,
} ,
{
. name = " SOCK_STREAM multiple connections " ,
. run_client = test_stream_multiconn_client ,
. run_server = test_stream_multiconn_server ,
} ,
2019-12-18 19:07:08 +01:00
{
. name = " SOCK_STREAM MSG_PEEK " ,
. run_client = test_stream_msg_peek_client ,
. run_server = test_stream_msg_peek_server ,
} ,
2019-12-18 19:07:04 +01:00
{ } ,
} ;
static const char optstring [ ] = " " ;
static const struct option longopts [ ] = {
{
. name = " control-host " ,
. has_arg = required_argument ,
. val = ' H ' ,
} ,
{
. name = " control-port " ,
. has_arg = required_argument ,
. val = ' P ' ,
} ,
{
. name = " mode " ,
. has_arg = required_argument ,
. val = ' m ' ,
} ,
{
. name = " peer-cid " ,
. has_arg = required_argument ,
. val = ' p ' ,
} ,
2019-12-18 19:07:06 +01:00
{
. name = " list " ,
. has_arg = no_argument ,
. val = ' l ' ,
} ,
{
. name = " skip " ,
. has_arg = required_argument ,
. val = ' s ' ,
} ,
2019-12-18 19:07:04 +01:00
{
. name = " help " ,
. has_arg = no_argument ,
. val = ' ? ' ,
} ,
{ } ,
} ;
static void usage ( void )
{
2019-12-18 19:07:06 +01:00
fprintf ( stderr , " Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--list] [--skip=<test_id>] \n "
2019-12-18 19:07:04 +01:00
" \n "
" Server: vsock_test --control-port=1234 --mode=server --peer-cid=3 \n "
" Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2 \n "
" \n "
" Run vsock.ko tests. Must be launched in both guest \n "
" and host. One side must use --mode=client and \n "
" the other side must use --mode=server. \n "
" \n "
" A TCP control socket connection is used to coordinate tests \n "
" between the client and the server. The server requires a \n "
" listen address and the client requires an address to \n "
" connect to. \n "
" \n "
2019-12-18 19:07:07 +01:00
" The CID of the other side must be given with --peer-cid=<cid>. \n "
" \n "
" Options: \n "
" --help This help message \n "
" --control-host <host> Server IP address to connect to \n "
" --control-port <port> Server port to listen on/connect to \n "
" --mode client|server Server or client mode \n "
" --peer-cid <cid> CID of the other side \n "
" --list List of tests that will be executed \n "
" --skip <test_id> Test ID to skip; \n "
" use multiple --skip options to skip more tests \n "
) ;
2019-12-18 19:07:04 +01:00
exit ( EXIT_FAILURE ) ;
}
int main ( int argc , char * * argv )
{
const char * control_host = NULL ;
const char * control_port = NULL ;
struct test_opts opts = {
. mode = TEST_MODE_UNSET ,
. peer_cid = VMADDR_CID_ANY ,
} ;
init_signals ( ) ;
for ( ; ; ) {
int opt = getopt_long ( argc , argv , optstring , longopts , NULL ) ;
if ( opt = = - 1 )
break ;
switch ( opt ) {
case ' H ' :
control_host = optarg ;
break ;
case ' m ' :
if ( strcmp ( optarg , " client " ) = = 0 )
opts . mode = TEST_MODE_CLIENT ;
else if ( strcmp ( optarg , " server " ) = = 0 )
opts . mode = TEST_MODE_SERVER ;
else {
fprintf ( stderr , " --mode must be \" client \" or \" server \" \n " ) ;
return EXIT_FAILURE ;
}
break ;
case ' p ' :
opts . peer_cid = parse_cid ( optarg ) ;
break ;
case ' P ' :
control_port = optarg ;
break ;
2019-12-18 19:07:06 +01:00
case ' l ' :
list_tests ( test_cases ) ;
break ;
case ' s ' :
skip_test ( test_cases , ARRAY_SIZE ( test_cases ) - 1 ,
optarg ) ;
break ;
2019-12-18 19:07:04 +01:00
case ' ? ' :
default :
usage ( ) ;
}
}
if ( ! control_port )
usage ( ) ;
if ( opts . mode = = TEST_MODE_UNSET )
usage ( ) ;
if ( opts . peer_cid = = VMADDR_CID_ANY )
usage ( ) ;
if ( ! control_host ) {
if ( opts . mode ! = TEST_MODE_SERVER )
usage ( ) ;
control_host = " 0.0.0.0 " ;
}
control_init ( control_host , control_port ,
opts . mode = = TEST_MODE_SERVER ) ;
run_tests ( test_cases , & opts ) ;
control_cleanup ( ) ;
return EXIT_SUCCESS ;
}