1 #include <errno.h>
2 #include <fcntl.h>
3 #include <netdb.h>
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/select.h>
9 #include <sys/socket.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12 
13 #include <linux/vm_sockets.h>
14 
15 // This is a simple vsock 'sibling' tester. It's used to verify
16 // vsock communications between two VMs on a host.
17 
18 #define BUFSIZE 1024  // for storing some temp strings for testing
19 
20 // Checks a socket handle, and prints error / quits if failure
check_socket(char * name,int fd)21 int check_socket(char *name, int fd) {
22   if (fd < 0) {
23     fprintf(stderr, "error initializing socket: %s: %s\n", name,
24             strerror(errno));
25     exit(0);
26   }
27   return fd;
28 }
29 
30 // Checks error code is nonzero, and if so, print a message,
31 // closing a socket handle if provided as well.
check_error(char * operation,int result,int fd)32 int check_error(char *operation, int result, int fd) {
33   if (result != 0) {
34     fprintf(stderr, "error for operation %s: %s\n", operation, strerror(errno));
35     if (fd != -1) close(fd);
36     exit(0);
37   }
38   return result;
39 }
40 
41 // Main execution path for server test mode. This mode runs
42 // a listener for vsock socket on the specified port, then
43 // prints the value received before echoing the same value
44 // back to the client for testing.
main_server(int port)45 int main_server(int port) {
46   printf("Starting a vsock server on port %d\n", port);
47 
48   struct sockaddr_vm sa_listen = {
49       .svm_family = AF_VSOCK, .svm_cid = VMADDR_CID_ANY, .svm_port = port};
50   struct sockaddr_vm sa_client;
51   socklen_t socklen_client = sizeof(sa_client);
52 
53   int listen_fd = check_socket("listen_fd", socket(AF_VSOCK, SOCK_STREAM, 0));
54 
55   check_error("binding main listen socket",
56               bind(listen_fd, (struct sockaddr *)&sa_listen, sizeof(sa_listen)),
57               listen_fd);
58 
59   check_error("listen on main socket", listen(listen_fd, 1), listen_fd);
60 
61   int client_fd =
62       accept(listen_fd, (struct sockaddr *)&sa_client, &socklen_client);
63 
64   check_error("accept() on main socket", client_fd < 0, listen_fd);
65 
66   fprintf(stderr, "Connection from cid %u port %u...\n", sa_client.svm_cid,
67           sa_client.svm_port);
68 
69   close(listen_fd);
70 
71   char buf[BUFSIZE];
72   memset(buf, 0, BUFSIZE);
73   int len = read(client_fd, buf, BUFSIZE - 1);
74 
75   printf("Read %d bytes, str is '%s':\n", len, buf);
76 
77   printf("Echoing back data...\n");
78 
79   write(client_fd, buf, len);
80 
81   printf("Data sent.\n");
82 
83   close(client_fd);
84 
85   return 0;
86 }
87 
88 // Main execution path for 'client' test mode. This mode
89 // connects to specified vsock cid and port, and sends a string
90 // to a 'server', which is a peer listening on specified vsock port.
91 // Client mode also waits for server to echo back the same
92 // value and prints this when received.
main_client(int cid,int port,char * str)93 int main_client(int cid, int port, char *str) {
94   struct sockaddr_vm sa = {.svm_family = AF_VSOCK,
95                            .svm_flags = VMADDR_FLAG_TO_HOST,
96                            .svm_cid = cid,
97                            .svm_port = port};
98 
99   printf("Connecting to cid %d port %d\n", cid, port);
100 
101   int fd = check_socket("main socket", socket(AF_VSOCK, SOCK_STREAM, 0));
102 
103   check_error("connect", connect(fd, (struct sockaddr *)&sa, sizeof(sa)), fd);
104 
105   printf("Connected, sending data '%s' to server...\n", str);
106 
107   write(fd, str, strlen(str));
108 
109   printf("Data sent.  Waiting for response...\n");
110 
111   char buf[BUFSIZE];
112   memset(buf, 0, BUFSIZE);
113   int len = read(fd, buf, BUFSIZE - 1);
114 
115   printf("Read %d bytes back from server, str is '%s':\n", len, buf);
116 
117   close(fd);
118 
119   return 0;
120 }
121 
122 #define safer_atoi(x) strtol(x, NULL, 10)
123 
main(int argc,char * argv[])124 int main(int argc, char *argv[]) {
125   if (argc == 2) {
126     main_server(safer_atoi(argv[1]));
127   } else if (argc == 4) {
128     main_client(safer_atoi(argv[1]), safer_atoi(argv[2]), argv[3]);
129   } else {
130     printf(
131         "Welcome to vsock-test! This utility helps test/verify "
132         "'sibling' (vm to vm) vsock comms.\n\n"
133         "Please run this command via one of the 2 following forms:\n\n"
134         "\tvsock-test [port]\n"
135         "\t\tThis format runs a vsock server, where [port] is the "
136         "vsock port to listen on.\n\n"
137         "\tvsock-test [cid] [port] [str]\n"
138         "\t\tThis format runs a vsock client, where:\n"
139         "\t\t\t[cid] is the CID of server to connect to\n"
140         "\t\t\t[port] is vsock port to connect to\n"
141         "\t\t\t[str] is any string to send from client for testing\n\n");
142   }
143 }
144