xref: /openbmc/qemu/tests/qtest/netdev-socket.c (revision 55abfc1f)
1 /*
2  * QTest testcase for netdev stream and dgram
3  *
4  * Copyright (c) 2022 Red Hat, Inc.
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "qemu/osdep.h"
10 #include "qemu/sockets.h"
11 #include <glib/gstdio.h>
12 #include "../unit/socket-helpers.h"
13 #include "libqtest.h"
14 #include "qapi/qmp/qstring.h"
15 #include "qemu/sockets.h"
16 #include "qapi/qobject-input-visitor.h"
17 #include "qapi/qapi-visit-sockets.h"
18 
19 #define CONNECTION_TIMEOUT    120
20 
21 static double connection_timeout(void)
22 {
23     double load;
24     int ret = getloadavg(&load, 1);
25 
26     /*
27      * If we can't get load data, or load is low because we just started
28      * running, assume load of 1 (we are alone in this system).
29      */
30     if (ret < 1 || load < 1.0) {
31         load = 1.0;
32     }
33     /*
34      * No one wants to wait more than 10 minutes for this test. Higher load?
35      * Too bad.
36      */
37     if (load > 10.0) {
38         fprintf(stderr, "Warning: load %f higher than 10 - test might timeout\n",
39                 load);
40         load = 10.0;
41     }
42 
43     /* if load is high increase timeout as we might not get a chance to run */
44     return load * CONNECTION_TIMEOUT;
45 }
46 
47 #define EXPECT_STATE(q, e, t)                             \
48 do {                                                      \
49     char *resp = NULL;                                    \
50     g_test_timer_start();                                 \
51     do {                                                  \
52         g_free(resp);                                     \
53         resp = qtest_hmp(q, "info network");              \
54         if (t) {                                          \
55             strrchr(resp, t)[0] = 0;                      \
56         }                                                 \
57         if (g_str_equal(resp, e)) {                       \
58             break;                                        \
59         }                                                 \
60     } while (g_test_timer_elapsed() < connection_timeout()); \
61     g_assert_cmpstr(resp, ==, e);                         \
62     g_free(resp);                                         \
63 } while (0)
64 
65 static gchar *tmpdir;
66 
67 static int inet_get_free_port_socket_ipv4(int sock)
68 {
69     struct sockaddr_in addr;
70     socklen_t len;
71 
72     memset(&addr, 0, sizeof(addr));
73     addr.sin_family = AF_INET;
74     addr.sin_addr.s_addr = INADDR_ANY;
75     addr.sin_port = 0;
76     if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
77         return -1;
78     }
79 
80     len = sizeof(addr);
81     if (getsockname(sock,  (struct sockaddr *)&addr, &len) < 0) {
82         return -1;
83     }
84 
85     return ntohs(addr.sin_port);
86 }
87 
88 static int inet_get_free_port_socket_ipv6(int sock)
89 {
90     struct sockaddr_in6 addr;
91     socklen_t len;
92 
93     memset(&addr, 0, sizeof(addr));
94     addr.sin6_family = AF_INET6;
95     addr.sin6_addr = in6addr_any;
96     addr.sin6_port = 0;
97     if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
98         return -1;
99     }
100 
101     len = sizeof(addr);
102     if (getsockname(sock,  (struct sockaddr *)&addr, &len) < 0) {
103         return -1;
104     }
105 
106     return ntohs(addr.sin6_port);
107 }
108 
109 static int inet_get_free_port_multiple(int nb, int *port, bool ipv6)
110 {
111     g_autofree int *sock = g_new(int, nb);
112     int i;
113 
114     for (i = 0; i < nb; i++) {
115         sock[i] = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
116         if (sock[i] < 0) {
117             break;
118         }
119         port[i] = ipv6 ? inet_get_free_port_socket_ipv6(sock[i]) :
120                          inet_get_free_port_socket_ipv4(sock[i]);
121         if (port[i] == -1) {
122             break;
123         }
124     }
125 
126     nb = i;
127     for (i = 0; i < nb; i++) {
128         close(sock[i]);
129     }
130 
131     return nb;
132 }
133 
134 static int inet_get_free_port(bool ipv6)
135 {
136     int nb, port;
137 
138     nb = inet_get_free_port_multiple(1, &port, ipv6);
139     g_assert_cmpint(nb, ==, 1);
140 
141     return port;
142 }
143 
144 static void test_stream_inet_ipv4(void)
145 {
146     QTestState *qts0, *qts1;
147     char *expect;
148     int port;
149 
150     port = inet_get_free_port(false);
151     qts0 = qtest_initf("-nodefaults -M none "
152                        "-netdev stream,id=st0,server=true,addr.type=inet,"
153                        "addr.ipv4=on,addr.ipv6=off,"
154                        "addr.host=127.0.0.1,addr.port=%d", port);
155 
156     EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
157 
158     qts1 = qtest_initf("-nodefaults -M none "
159                        "-netdev stream,server=false,id=st0,addr.type=inet,"
160                        "addr.ipv4=on,addr.ipv6=off,"
161                        "addr.host=127.0.0.1,addr.port=%d", port);
162 
163     expect = g_strdup_printf("st0: index=0,type=stream,tcp:127.0.0.1:%d\r\n",
164                              port);
165     EXPECT_STATE(qts1, expect, 0);
166     g_free(expect);
167 
168     /* the port is unknown, check only the address */
169     EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:127.0.0.1", ':');
170 
171     qtest_quit(qts1);
172     qtest_quit(qts0);
173 }
174 
175 static void wait_stream_connected(QTestState *qts, const char *id,
176                                   SocketAddress **addr)
177 {
178     QDict *resp, *data;
179     QString *qstr;
180     QObject *obj;
181     Visitor *v = NULL;
182 
183     resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_CONNECTED");
184     g_assert_nonnull(resp);
185     data = qdict_get_qdict(resp, "data");
186     g_assert_nonnull(data);
187 
188     qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
189     g_assert_nonnull(data);
190 
191     g_assert(!strcmp(qstring_get_str(qstr), id));
192 
193     obj = qdict_get(data, "addr");
194 
195     v = qobject_input_visitor_new(obj);
196     visit_type_SocketAddress(v, NULL, addr, NULL);
197     visit_free(v);
198     qobject_unref(resp);
199 }
200 
201 static void wait_stream_disconnected(QTestState *qts, const char *id)
202 {
203     QDict *resp, *data;
204     QString *qstr;
205 
206     resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_DISCONNECTED");
207     g_assert_nonnull(resp);
208     data = qdict_get_qdict(resp, "data");
209     g_assert_nonnull(data);
210 
211     qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
212     g_assert_nonnull(data);
213 
214     g_assert(!strcmp(qstring_get_str(qstr), id));
215     qobject_unref(resp);
216 }
217 
218 static void test_stream_unix_reconnect(void)
219 {
220     QTestState *qts0, *qts1;
221     SocketAddress *addr;
222     gchar *path;
223 
224     path = g_strconcat(tmpdir, "/stream_unix_reconnect", NULL);
225     qts0 = qtest_initf("-nodefaults -M none "
226                        "-netdev stream,id=st0,server=true,addr.type=unix,"
227                        "addr.path=%s", path);
228 
229     EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
230 
231     qts1 = qtest_initf("-nodefaults -M none "
232                        "-netdev stream,server=false,id=st0,addr.type=unix,"
233                        "addr.path=%s,reconnect=1", path);
234 
235     wait_stream_connected(qts0, "st0", &addr);
236     g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
237     g_assert_cmpstr(addr->u.q_unix.path, ==, path);
238     qapi_free_SocketAddress(addr);
239 
240     /* kill server */
241     qtest_quit(qts0);
242 
243     /* check client has been disconnected */
244     wait_stream_disconnected(qts1, "st0");
245 
246     /* restart server */
247     qts0 = qtest_initf("-nodefaults -M none "
248                        "-netdev stream,id=st0,server=true,addr.type=unix,"
249                        "addr.path=%s", path);
250 
251     /* wait connection events*/
252     wait_stream_connected(qts0, "st0", &addr);
253     g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
254     g_assert_cmpstr(addr->u.q_unix.path, ==, path);
255     qapi_free_SocketAddress(addr);
256 
257     wait_stream_connected(qts1, "st0", &addr);
258     g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
259     g_assert_cmpstr(addr->u.q_unix.path, ==, path);
260     qapi_free_SocketAddress(addr);
261 
262     qtest_quit(qts1);
263     qtest_quit(qts0);
264     g_free(path);
265 }
266 
267 static void test_stream_inet_ipv6(void)
268 {
269     QTestState *qts0, *qts1;
270     char *expect;
271     int port;
272 
273     port = inet_get_free_port(true);
274     qts0 = qtest_initf("-nodefaults -M none "
275                        "-netdev stream,id=st0,server=true,addr.type=inet,"
276                        "addr.ipv4=off,addr.ipv6=on,"
277                        "addr.host=::1,addr.port=%d", port);
278 
279     EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
280 
281     qts1 = qtest_initf("-nodefaults -M none "
282                        "-netdev stream,server=false,id=st0,addr.type=inet,"
283                        "addr.ipv4=off,addr.ipv6=on,"
284                        "addr.host=::1,addr.port=%d", port);
285 
286     expect = g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n",
287                              port);
288     EXPECT_STATE(qts1, expect, 0);
289     g_free(expect);
290 
291     /* the port is unknown, check only the address */
292     EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':');
293 
294     qtest_quit(qts1);
295     qtest_quit(qts0);
296 }
297 
298 static void test_stream_unix(void)
299 {
300     QTestState *qts0, *qts1;
301     char *expect;
302     gchar *path;
303 
304     path = g_strconcat(tmpdir, "/stream_unix", NULL);
305 
306     qts0 = qtest_initf("-nodefaults -M none "
307                        "-netdev stream,id=st0,server=true,"
308                        "addr.type=unix,addr.path=%s,",
309                        path);
310 
311     EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
312 
313     qts1 = qtest_initf("-nodefaults -M none "
314                        "-netdev stream,id=st0,server=false,"
315                        "addr.type=unix,addr.path=%s",
316                        path);
317 
318     expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path);
319     EXPECT_STATE(qts1, expect, 0);
320     EXPECT_STATE(qts0, expect, 0);
321     g_free(expect);
322     g_free(path);
323 
324     qtest_quit(qts1);
325     qtest_quit(qts0);
326 }
327 
328 #ifdef CONFIG_LINUX
329 static void test_stream_unix_abstract(void)
330 {
331     QTestState *qts0, *qts1;
332     char *expect;
333     gchar *path;
334 
335     path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL);
336 
337     qts0 = qtest_initf("-nodefaults -M none "
338                        "-netdev stream,id=st0,server=true,"
339                        "addr.type=unix,addr.path=%s,"
340                        "addr.abstract=on",
341                        path);
342 
343     EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
344 
345     qts1 = qtest_initf("-nodefaults -M none "
346                        "-netdev stream,id=st0,server=false,"
347                        "addr.type=unix,addr.path=%s,addr.abstract=on",
348                        path);
349 
350     expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path);
351     EXPECT_STATE(qts1, expect, 0);
352     EXPECT_STATE(qts0, expect, 0);
353     g_free(expect);
354     g_free(path);
355 
356     qtest_quit(qts1);
357     qtest_quit(qts0);
358 }
359 #endif
360 
361 #ifndef _WIN32
362 static void test_stream_fd(void)
363 {
364     QTestState *qts0, *qts1;
365     int sock[2];
366     int ret;
367 
368     ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock);
369     g_assert_true(ret == 0);
370 
371     qts0 = qtest_initf("-nodefaults -M none "
372                        "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
373                        sock[0]);
374 
375     EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
376 
377     qts1 = qtest_initf("-nodefaults -M none "
378                        "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
379                        sock[1]);
380 
381     EXPECT_STATE(qts1, "st0: index=0,type=stream,unix:\r\n", 0);
382     EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
383 
384     qtest_quit(qts1);
385     qtest_quit(qts0);
386 
387     close(sock[0]);
388     close(sock[1]);
389 }
390 #endif
391 
392 static void test_dgram_inet(void)
393 {
394     QTestState *qts0, *qts1;
395     char *expect;
396     int port[2];
397     int nb;
398 
399     nb = inet_get_free_port_multiple(2, port, false);
400     g_assert_cmpint(nb, ==, 2);
401 
402     qts0 = qtest_initf("-nodefaults -M none "
403                        "-netdev dgram,id=st0,"
404                        "local.type=inet,local.host=127.0.0.1,local.port=%d,"
405                        "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
406                         port[0], port[1]);
407 
408     expect = g_strdup_printf("st0: index=0,type=dgram,"
409                              "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
410                              port[0], port[1]);
411     EXPECT_STATE(qts0, expect, 0);
412     g_free(expect);
413 
414     qts1 = qtest_initf("-nodefaults -M none "
415                        "-netdev dgram,id=st0,"
416                        "local.type=inet,local.host=127.0.0.1,local.port=%d,"
417                        "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
418                         port[1], port[0]);
419 
420     expect = g_strdup_printf("st0: index=0,type=dgram,"
421                              "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
422                              port[1], port[0]);
423     EXPECT_STATE(qts1, expect, 0);
424     g_free(expect);
425 
426     qtest_quit(qts1);
427     qtest_quit(qts0);
428 }
429 
430 #if !defined(_WIN32) && !defined(CONFIG_DARWIN)
431 static void test_dgram_mcast(void)
432 {
433     QTestState *qts;
434 
435     qts = qtest_initf("-nodefaults -M none "
436                       "-netdev dgram,id=st0,"
437                       "remote.type=inet,remote.host=230.0.0.1,remote.port=1234");
438 
439     EXPECT_STATE(qts, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0);
440 
441     qtest_quit(qts);
442 }
443 #endif
444 
445 #ifndef _WIN32
446 static void test_dgram_unix(void)
447 {
448     QTestState *qts0, *qts1;
449     char *expect;
450     gchar *path0, *path1;
451 
452     path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL);
453     path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL);
454 
455     qts0 = qtest_initf("-nodefaults -M none "
456                        "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
457                        "remote.type=unix,remote.path=%s",
458                        path0, path1);
459 
460     expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
461                              path0, path1);
462     EXPECT_STATE(qts0, expect, 0);
463     g_free(expect);
464 
465     qts1 = qtest_initf("-nodefaults -M none "
466                        "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
467                        "remote.type=unix,remote.path=%s",
468                        path1, path0);
469 
470 
471     expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
472                              path1, path0);
473     EXPECT_STATE(qts1, expect, 0);
474     g_free(expect);
475 
476     unlink(path0);
477     g_free(path0);
478     unlink(path1);
479     g_free(path1);
480 
481     qtest_quit(qts1);
482     qtest_quit(qts0);
483 }
484 
485 static void test_dgram_fd(void)
486 {
487     QTestState *qts0, *qts1;
488     char *expect;
489     int ret;
490     int sv[2];
491 
492     ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv);
493     g_assert_cmpint(ret, !=, -1);
494 
495     qts0 = qtest_initf("-nodefaults -M none "
496                        "-netdev dgram,id=st0,local.type=fd,local.str=%d",
497                        sv[0]);
498 
499     expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[0]);
500     EXPECT_STATE(qts0, expect, 0);
501     g_free(expect);
502 
503     qts1 = qtest_initf("-nodefaults -M none "
504                        "-netdev dgram,id=st0,local.type=fd,local.str=%d",
505                        sv[1]);
506 
507 
508     expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[1]);
509     EXPECT_STATE(qts1, expect, 0);
510     g_free(expect);
511 
512     qtest_quit(qts1);
513     qtest_quit(qts0);
514 
515     close(sv[0]);
516     close(sv[1]);
517 }
518 #endif
519 
520 int main(int argc, char **argv)
521 {
522     int ret;
523     bool has_ipv4, has_ipv6, has_afunix;
524     g_autoptr(GError) err = NULL;
525 
526     socket_init();
527     g_test_init(&argc, &argv, NULL);
528 
529     if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
530         g_error("socket_check_protocol_support() failed\n");
531     }
532 
533     tmpdir = g_dir_make_tmp("netdev-socket.XXXXXX", &err);
534     if (tmpdir == NULL) {
535         g_error("Can't create temporary directory in %s: %s",
536                 g_get_tmp_dir(), err->message);
537     }
538 
539     if (has_ipv4) {
540         qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4);
541         qtest_add_func("/netdev/dgram/inet", test_dgram_inet);
542 #if !defined(_WIN32) && !defined(CONFIG_DARWIN)
543         qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast);
544 #endif
545     }
546     if (has_ipv6) {
547         qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6);
548     }
549 
550     socket_check_afunix_support(&has_afunix);
551     if (has_afunix) {
552 #ifndef _WIN32
553         qtest_add_func("/netdev/dgram/unix", test_dgram_unix);
554 #endif
555         qtest_add_func("/netdev/stream/unix", test_stream_unix);
556         qtest_add_func("/netdev/stream/unix/reconnect",
557                        test_stream_unix_reconnect);
558 #ifdef CONFIG_LINUX
559         qtest_add_func("/netdev/stream/unix/abstract",
560                        test_stream_unix_abstract);
561 #endif
562 #ifndef _WIN32
563         qtest_add_func("/netdev/stream/fd", test_stream_fd);
564         qtest_add_func("/netdev/dgram/fd", test_dgram_fd);
565 #endif
566     }
567 
568     ret = g_test_run();
569 
570     g_rmdir(tmpdir);
571     g_free(tmpdir);
572 
573     return ret;
574 }
575