1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * vsock test utilities |
4 | * |
5 | * Copyright (C) 2017 Red Hat, Inc. |
6 | * |
7 | * Author: Stefan Hajnoczi <stefanha@redhat.com> |
8 | */ |
9 | |
10 | #include <errno.h> |
11 | #include <stdio.h> |
12 | #include <stdint.h> |
13 | #include <stdlib.h> |
14 | #include <string.h> |
15 | #include <signal.h> |
16 | #include <unistd.h> |
17 | #include <assert.h> |
18 | #include <sys/epoll.h> |
19 | #include <sys/mman.h> |
20 | |
21 | #include "timeout.h" |
22 | #include "control.h" |
23 | #include "util.h" |
24 | |
25 | /* Install signal handlers */ |
26 | void init_signals(void) |
27 | { |
28 | struct sigaction act = { |
29 | .sa_handler = sigalrm, |
30 | }; |
31 | |
32 | sigaction(SIGALRM, &act, NULL); |
33 | signal(SIGPIPE, SIG_IGN); |
34 | } |
35 | |
36 | static unsigned int parse_uint(const char *str, const char *err_str) |
37 | { |
38 | char *endptr = NULL; |
39 | unsigned long n; |
40 | |
41 | errno = 0; |
42 | n = strtoul(str, &endptr, 10); |
43 | if (errno || *endptr != '\0') { |
44 | fprintf(stderr, "malformed %s \"%s\"\n" , err_str, str); |
45 | exit(EXIT_FAILURE); |
46 | } |
47 | return n; |
48 | } |
49 | |
50 | /* Parse a CID in string representation */ |
51 | unsigned int parse_cid(const char *str) |
52 | { |
53 | return parse_uint(str, err_str: "CID" ); |
54 | } |
55 | |
56 | /* Parse a port in string representation */ |
57 | unsigned int parse_port(const char *str) |
58 | { |
59 | return parse_uint(str, err_str: "port" ); |
60 | } |
61 | |
62 | /* Wait for the remote to close the connection */ |
63 | void vsock_wait_remote_close(int fd) |
64 | { |
65 | struct epoll_event ev; |
66 | int epollfd, nfds; |
67 | |
68 | epollfd = epoll_create1(0); |
69 | if (epollfd == -1) { |
70 | perror("epoll_create1" ); |
71 | exit(EXIT_FAILURE); |
72 | } |
73 | |
74 | ev.events = EPOLLRDHUP | EPOLLHUP; |
75 | ev.data.fd = fd; |
76 | if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { |
77 | perror("epoll_ctl" ); |
78 | exit(EXIT_FAILURE); |
79 | } |
80 | |
81 | nfds = epoll_wait(epollfd, &ev, 1, TIMEOUT * 1000); |
82 | if (nfds == -1) { |
83 | perror("epoll_wait" ); |
84 | exit(EXIT_FAILURE); |
85 | } |
86 | |
87 | if (nfds == 0) { |
88 | fprintf(stderr, "epoll_wait timed out\n" ); |
89 | exit(EXIT_FAILURE); |
90 | } |
91 | |
92 | assert(nfds == 1); |
93 | assert(ev.events & (EPOLLRDHUP | EPOLLHUP)); |
94 | assert(ev.data.fd == fd); |
95 | |
96 | close(epollfd); |
97 | } |
98 | |
99 | /* Bind to <bind_port>, connect to <cid, port> and return the file descriptor. */ |
100 | int vsock_bind_connect(unsigned int cid, unsigned int port, unsigned int bind_port, int type) |
101 | { |
102 | struct sockaddr_vm sa_client = { |
103 | .svm_family = AF_VSOCK, |
104 | .svm_cid = VMADDR_CID_ANY, |
105 | .svm_port = bind_port, |
106 | }; |
107 | struct sockaddr_vm sa_server = { |
108 | .svm_family = AF_VSOCK, |
109 | .svm_cid = cid, |
110 | .svm_port = port, |
111 | }; |
112 | |
113 | int client_fd, ret; |
114 | |
115 | client_fd = socket(AF_VSOCK, type, 0); |
116 | if (client_fd < 0) { |
117 | perror("socket" ); |
118 | exit(EXIT_FAILURE); |
119 | } |
120 | |
121 | if (bind(client_fd, (struct sockaddr *)&sa_client, sizeof(sa_client))) { |
122 | perror("bind" ); |
123 | exit(EXIT_FAILURE); |
124 | } |
125 | |
126 | timeout_begin(seconds: TIMEOUT); |
127 | do { |
128 | ret = connect(client_fd, (struct sockaddr *)&sa_server, sizeof(sa_server)); |
129 | timeout_check("connect" ); |
130 | } while (ret < 0 && errno == EINTR); |
131 | timeout_end(); |
132 | |
133 | if (ret < 0) { |
134 | perror("connect" ); |
135 | exit(EXIT_FAILURE); |
136 | } |
137 | |
138 | return client_fd; |
139 | } |
140 | |
141 | /* Connect to <cid, port> and return the file descriptor. */ |
142 | static int vsock_connect(unsigned int cid, unsigned int port, int type) |
143 | { |
144 | union { |
145 | struct sockaddr sa; |
146 | struct sockaddr_vm svm; |
147 | } addr = { |
148 | .svm = { |
149 | .svm_family = AF_VSOCK, |
150 | .svm_port = port, |
151 | .svm_cid = cid, |
152 | }, |
153 | }; |
154 | int ret; |
155 | int fd; |
156 | |
157 | control_expectln(str: "LISTENING" ); |
158 | |
159 | fd = socket(AF_VSOCK, type, 0); |
160 | if (fd < 0) { |
161 | perror("socket" ); |
162 | exit(EXIT_FAILURE); |
163 | } |
164 | |
165 | timeout_begin(seconds: TIMEOUT); |
166 | do { |
167 | ret = connect(fd, &addr.sa, sizeof(addr.svm)); |
168 | timeout_check("connect" ); |
169 | } while (ret < 0 && errno == EINTR); |
170 | timeout_end(); |
171 | |
172 | if (ret < 0) { |
173 | int old_errno = errno; |
174 | |
175 | close(fd); |
176 | fd = -1; |
177 | errno = old_errno; |
178 | } |
179 | return fd; |
180 | } |
181 | |
182 | int vsock_stream_connect(unsigned int cid, unsigned int port) |
183 | { |
184 | return vsock_connect(cid, port, type: SOCK_STREAM); |
185 | } |
186 | |
187 | int vsock_seqpacket_connect(unsigned int cid, unsigned int port) |
188 | { |
189 | return vsock_connect(cid, port, type: SOCK_SEQPACKET); |
190 | } |
191 | |
192 | /* Listen on <cid, port> and return the file descriptor. */ |
193 | static int vsock_listen(unsigned int cid, unsigned int port, int type) |
194 | { |
195 | union { |
196 | struct sockaddr sa; |
197 | struct sockaddr_vm svm; |
198 | } addr = { |
199 | .svm = { |
200 | .svm_family = AF_VSOCK, |
201 | .svm_port = port, |
202 | .svm_cid = cid, |
203 | }, |
204 | }; |
205 | int fd; |
206 | |
207 | fd = socket(AF_VSOCK, type, 0); |
208 | if (fd < 0) { |
209 | perror("socket" ); |
210 | exit(EXIT_FAILURE); |
211 | } |
212 | |
213 | if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) { |
214 | perror("bind" ); |
215 | exit(EXIT_FAILURE); |
216 | } |
217 | |
218 | if (listen(fd, 1) < 0) { |
219 | perror("listen" ); |
220 | exit(EXIT_FAILURE); |
221 | } |
222 | |
223 | return fd; |
224 | } |
225 | |
226 | /* Listen on <cid, port> and return the first incoming connection. The remote |
227 | * address is stored to clientaddrp. clientaddrp may be NULL. |
228 | */ |
229 | static int vsock_accept(unsigned int cid, unsigned int port, |
230 | struct sockaddr_vm *clientaddrp, int type) |
231 | { |
232 | union { |
233 | struct sockaddr sa; |
234 | struct sockaddr_vm svm; |
235 | } clientaddr; |
236 | socklen_t clientaddr_len = sizeof(clientaddr.svm); |
237 | int fd, client_fd, old_errno; |
238 | |
239 | fd = vsock_listen(cid, port, type); |
240 | |
241 | control_writeln(str: "LISTENING" ); |
242 | |
243 | timeout_begin(seconds: TIMEOUT); |
244 | do { |
245 | client_fd = accept(fd, &clientaddr.sa, &clientaddr_len); |
246 | timeout_check("accept" ); |
247 | } while (client_fd < 0 && errno == EINTR); |
248 | timeout_end(); |
249 | |
250 | old_errno = errno; |
251 | close(fd); |
252 | errno = old_errno; |
253 | |
254 | if (client_fd < 0) |
255 | return client_fd; |
256 | |
257 | if (clientaddr_len != sizeof(clientaddr.svm)) { |
258 | fprintf(stderr, "unexpected addrlen from accept(2), %zu\n" , |
259 | (size_t)clientaddr_len); |
260 | exit(EXIT_FAILURE); |
261 | } |
262 | if (clientaddr.sa.sa_family != AF_VSOCK) { |
263 | fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n" , |
264 | clientaddr.sa.sa_family); |
265 | exit(EXIT_FAILURE); |
266 | } |
267 | |
268 | if (clientaddrp) |
269 | *clientaddrp = clientaddr.svm; |
270 | return client_fd; |
271 | } |
272 | |
273 | int vsock_stream_accept(unsigned int cid, unsigned int port, |
274 | struct sockaddr_vm *clientaddrp) |
275 | { |
276 | return vsock_accept(cid, port, clientaddrp, SOCK_STREAM); |
277 | } |
278 | |
279 | int vsock_stream_listen(unsigned int cid, unsigned int port) |
280 | { |
281 | return vsock_listen(cid, port, SOCK_STREAM); |
282 | } |
283 | |
284 | int vsock_seqpacket_accept(unsigned int cid, unsigned int port, |
285 | struct sockaddr_vm *clientaddrp) |
286 | { |
287 | return vsock_accept(cid, port, clientaddrp, SOCK_SEQPACKET); |
288 | } |
289 | |
290 | /* Transmit bytes from a buffer and check the return value. |
291 | * |
292 | * expected_ret: |
293 | * <0 Negative errno (for testing errors) |
294 | * 0 End-of-file |
295 | * >0 Success (bytes successfully written) |
296 | */ |
297 | void send_buf(int fd, const void *buf, size_t len, int flags, |
298 | ssize_t expected_ret) |
299 | { |
300 | ssize_t nwritten = 0; |
301 | ssize_t ret; |
302 | |
303 | timeout_begin(seconds: TIMEOUT); |
304 | do { |
305 | ret = send(fd, buf + nwritten, len - nwritten, flags); |
306 | timeout_check(operation: "send" ); |
307 | |
308 | if (ret == 0 || (ret < 0 && errno != EINTR)) |
309 | break; |
310 | |
311 | nwritten += ret; |
312 | } while (nwritten < len); |
313 | timeout_end(); |
314 | |
315 | if (expected_ret < 0) { |
316 | if (ret != -1) { |
317 | fprintf(stderr, "bogus send(2) return value %zd (expected %zd)\n" , |
318 | ret, expected_ret); |
319 | exit(EXIT_FAILURE); |
320 | } |
321 | if (errno != -expected_ret) { |
322 | perror("send" ); |
323 | exit(EXIT_FAILURE); |
324 | } |
325 | return; |
326 | } |
327 | |
328 | if (ret < 0) { |
329 | perror("send" ); |
330 | exit(EXIT_FAILURE); |
331 | } |
332 | |
333 | if (nwritten != expected_ret) { |
334 | if (ret == 0) |
335 | fprintf(stderr, "unexpected EOF while sending bytes\n" ); |
336 | |
337 | fprintf(stderr, "bogus send(2) bytes written %zd (expected %zd)\n" , |
338 | nwritten, expected_ret); |
339 | exit(EXIT_FAILURE); |
340 | } |
341 | } |
342 | |
343 | /* Receive bytes in a buffer and check the return value. |
344 | * |
345 | * expected_ret: |
346 | * <0 Negative errno (for testing errors) |
347 | * 0 End-of-file |
348 | * >0 Success (bytes successfully read) |
349 | */ |
350 | void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret) |
351 | { |
352 | ssize_t nread = 0; |
353 | ssize_t ret; |
354 | |
355 | timeout_begin(seconds: TIMEOUT); |
356 | do { |
357 | ret = recv(fd, buf + nread, len - nread, flags); |
358 | timeout_check(operation: "recv" ); |
359 | |
360 | if (ret == 0 || (ret < 0 && errno != EINTR)) |
361 | break; |
362 | |
363 | nread += ret; |
364 | } while (nread < len); |
365 | timeout_end(); |
366 | |
367 | if (expected_ret < 0) { |
368 | if (ret != -1) { |
369 | fprintf(stderr, "bogus recv(2) return value %zd (expected %zd)\n" , |
370 | ret, expected_ret); |
371 | exit(EXIT_FAILURE); |
372 | } |
373 | if (errno != -expected_ret) { |
374 | perror("recv" ); |
375 | exit(EXIT_FAILURE); |
376 | } |
377 | return; |
378 | } |
379 | |
380 | if (ret < 0) { |
381 | perror("recv" ); |
382 | exit(EXIT_FAILURE); |
383 | } |
384 | |
385 | if (nread != expected_ret) { |
386 | if (ret == 0) |
387 | fprintf(stderr, "unexpected EOF while receiving bytes\n" ); |
388 | |
389 | fprintf(stderr, "bogus recv(2) bytes read %zd (expected %zd)\n" , |
390 | nread, expected_ret); |
391 | exit(EXIT_FAILURE); |
392 | } |
393 | } |
394 | |
395 | /* Transmit one byte and check the return value. |
396 | * |
397 | * expected_ret: |
398 | * <0 Negative errno (for testing errors) |
399 | * 0 End-of-file |
400 | * 1 Success |
401 | */ |
402 | void send_byte(int fd, int expected_ret, int flags) |
403 | { |
404 | const uint8_t byte = 'A'; |
405 | |
406 | send_buf(fd, buf: &byte, len: sizeof(byte), flags, expected_ret); |
407 | } |
408 | |
409 | /* Receive one byte and check the return value. |
410 | * |
411 | * expected_ret: |
412 | * <0 Negative errno (for testing errors) |
413 | * 0 End-of-file |
414 | * 1 Success |
415 | */ |
416 | void recv_byte(int fd, int expected_ret, int flags) |
417 | { |
418 | uint8_t byte; |
419 | |
420 | recv_buf(fd, buf: &byte, len: sizeof(byte), flags, expected_ret); |
421 | |
422 | if (byte != 'A') { |
423 | fprintf(stderr, "unexpected byte read %c\n" , byte); |
424 | exit(EXIT_FAILURE); |
425 | } |
426 | } |
427 | |
428 | /* Run test cases. The program terminates if a failure occurs. */ |
429 | void run_tests(const struct test_case *test_cases, |
430 | const struct test_opts *opts) |
431 | { |
432 | int i; |
433 | |
434 | for (i = 0; test_cases[i].name; i++) { |
435 | void (*run)(const struct test_opts *opts); |
436 | char *line; |
437 | |
438 | printf("%d - %s..." , i, test_cases[i].name); |
439 | fflush(stdout); |
440 | |
441 | /* Full barrier before executing the next test. This |
442 | * ensures that client and server are executing the |
443 | * same test case. In particular, it means whoever is |
444 | * faster will not see the peer still executing the |
445 | * last test. This is important because port numbers |
446 | * can be used by multiple test cases. |
447 | */ |
448 | if (test_cases[i].skip) |
449 | control_writeln(str: "SKIP" ); |
450 | else |
451 | control_writeln(str: "NEXT" ); |
452 | |
453 | line = control_readln(); |
454 | if (control_cmpln(line, "SKIP" , false) || test_cases[i].skip) { |
455 | |
456 | printf("skipped\n" ); |
457 | |
458 | free(line); |
459 | continue; |
460 | } |
461 | |
462 | control_cmpln(line, "NEXT" , true); |
463 | free(line); |
464 | |
465 | if (opts->mode == TEST_MODE_CLIENT) |
466 | run = test_cases[i].run_client; |
467 | else |
468 | run = test_cases[i].run_server; |
469 | |
470 | if (run) |
471 | run(opts); |
472 | |
473 | printf("ok\n" ); |
474 | } |
475 | } |
476 | |
477 | void list_tests(const struct test_case *test_cases) |
478 | { |
479 | int i; |
480 | |
481 | printf("ID\tTest name\n" ); |
482 | |
483 | for (i = 0; test_cases[i].name; i++) |
484 | printf("%d\t%s\n" , i, test_cases[i].name); |
485 | |
486 | exit(EXIT_FAILURE); |
487 | } |
488 | |
489 | void skip_test(struct test_case *test_cases, size_t test_cases_len, |
490 | const char *test_id_str) |
491 | { |
492 | unsigned long test_id; |
493 | char *endptr = NULL; |
494 | |
495 | errno = 0; |
496 | test_id = strtoul(test_id_str, &endptr, 10); |
497 | if (errno || *endptr != '\0') { |
498 | fprintf(stderr, "malformed test ID \"%s\"\n" , test_id_str); |
499 | exit(EXIT_FAILURE); |
500 | } |
501 | |
502 | if (test_id >= test_cases_len) { |
503 | fprintf(stderr, "test ID (%lu) larger than the max allowed (%lu)\n" , |
504 | test_id, test_cases_len - 1); |
505 | exit(EXIT_FAILURE); |
506 | } |
507 | |
508 | test_cases[test_id].skip = true; |
509 | } |
510 | |
511 | unsigned long hash_djb2(const void *data, size_t len) |
512 | { |
513 | unsigned long hash = 5381; |
514 | int i = 0; |
515 | |
516 | while (i < len) { |
517 | hash = ((hash << 5) + hash) + ((unsigned char *)data)[i]; |
518 | i++; |
519 | } |
520 | |
521 | return hash; |
522 | } |
523 | |
524 | size_t iovec_bytes(const struct iovec *iov, size_t iovnum) |
525 | { |
526 | size_t bytes; |
527 | int i; |
528 | |
529 | for (bytes = 0, i = 0; i < iovnum; i++) |
530 | bytes += iov[i].iov_len; |
531 | |
532 | return bytes; |
533 | } |
534 | |
535 | unsigned long iovec_hash_djb2(const struct iovec *iov, size_t iovnum) |
536 | { |
537 | unsigned long hash; |
538 | size_t iov_bytes; |
539 | size_t offs; |
540 | void *tmp; |
541 | int i; |
542 | |
543 | iov_bytes = iovec_bytes(iov, iovnum); |
544 | |
545 | tmp = malloc(iov_bytes); |
546 | if (!tmp) { |
547 | perror("malloc" ); |
548 | exit(EXIT_FAILURE); |
549 | } |
550 | |
551 | for (offs = 0, i = 0; i < iovnum; i++) { |
552 | memcpy(tmp + offs, iov[i].iov_base, iov[i].iov_len); |
553 | offs += iov[i].iov_len; |
554 | } |
555 | |
556 | hash = hash_djb2(data: tmp, len: iov_bytes); |
557 | free(tmp); |
558 | |
559 | return hash; |
560 | } |
561 | |
562 | /* Allocates and returns new 'struct iovec *' according pattern |
563 | * in the 'test_iovec'. For each element in the 'test_iovec' it |
564 | * allocates new element in the resulting 'iovec'. 'iov_len' |
565 | * of the new element is copied from 'test_iovec'. 'iov_base' is |
566 | * allocated depending on the 'iov_base' of 'test_iovec': |
567 | * |
568 | * 'iov_base' == NULL -> valid buf: mmap('iov_len'). |
569 | * |
570 | * 'iov_base' == MAP_FAILED -> invalid buf: |
571 | * mmap('iov_len'), then munmap('iov_len'). |
572 | * 'iov_base' still contains result of |
573 | * mmap(). |
574 | * |
575 | * 'iov_base' == number -> unaligned valid buf: |
576 | * mmap('iov_len') + number. |
577 | * |
578 | * 'iovnum' is number of elements in 'test_iovec'. |
579 | * |
580 | * Returns new 'iovec' or calls 'exit()' on error. |
581 | */ |
582 | struct iovec *alloc_test_iovec(const struct iovec *test_iovec, int iovnum) |
583 | { |
584 | struct iovec *iovec; |
585 | int i; |
586 | |
587 | iovec = malloc(sizeof(*iovec) * iovnum); |
588 | if (!iovec) { |
589 | perror("malloc" ); |
590 | exit(EXIT_FAILURE); |
591 | } |
592 | |
593 | for (i = 0; i < iovnum; i++) { |
594 | iovec[i].iov_len = test_iovec[i].iov_len; |
595 | |
596 | iovec[i].iov_base = mmap(NULL, iovec[i].iov_len, |
597 | PROT_READ | PROT_WRITE, |
598 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, |
599 | -1, 0); |
600 | if (iovec[i].iov_base == MAP_FAILED) { |
601 | perror("mmap" ); |
602 | exit(EXIT_FAILURE); |
603 | } |
604 | |
605 | if (test_iovec[i].iov_base != MAP_FAILED) |
606 | iovec[i].iov_base += (uintptr_t)test_iovec[i].iov_base; |
607 | } |
608 | |
609 | /* Unmap "invalid" elements. */ |
610 | for (i = 0; i < iovnum; i++) { |
611 | if (test_iovec[i].iov_base == MAP_FAILED) { |
612 | if (munmap(iovec[i].iov_base, iovec[i].iov_len)) { |
613 | perror("munmap" ); |
614 | exit(EXIT_FAILURE); |
615 | } |
616 | } |
617 | } |
618 | |
619 | for (i = 0; i < iovnum; i++) { |
620 | int j; |
621 | |
622 | if (test_iovec[i].iov_base == MAP_FAILED) |
623 | continue; |
624 | |
625 | for (j = 0; j < iovec[i].iov_len; j++) |
626 | ((uint8_t *)iovec[i].iov_base)[j] = rand() & 0xff; |
627 | } |
628 | |
629 | return iovec; |
630 | } |
631 | |
632 | /* Frees 'iovec *', previously allocated by 'alloc_test_iovec()'. |
633 | * On error calls 'exit()'. |
634 | */ |
635 | void free_test_iovec(const struct iovec *test_iovec, |
636 | struct iovec *iovec, int iovnum) |
637 | { |
638 | int i; |
639 | |
640 | for (i = 0; i < iovnum; i++) { |
641 | if (test_iovec[i].iov_base != MAP_FAILED) { |
642 | if (test_iovec[i].iov_base) |
643 | iovec[i].iov_base -= (uintptr_t)test_iovec[i].iov_base; |
644 | |
645 | if (munmap(iovec[i].iov_base, iovec[i].iov_len)) { |
646 | perror("munmap" ); |
647 | exit(EXIT_FAILURE); |
648 | } |
649 | } |
650 | } |
651 | |
652 | free(iovec); |
653 | } |
654 | |