xref: /openbmc/qemu/tests/qtest/netdev-socket.c (revision 2cfb3b6c)
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 
15 #define CONNECTION_TIMEOUT    5
16 
17 #define EXPECT_STATE(q, e, t)                             \
18 do {                                                      \
19     char *resp = NULL;                                    \
20     g_test_timer_start();                                 \
21     do {                                                  \
22         g_free(resp);                                     \
23         resp = qtest_hmp(q, "info network");              \
24         if (t) {                                          \
25             strrchr(resp, t)[0] = 0;                      \
26         }                                                 \
27         if (g_str_equal(resp, e)) {                       \
28             break;                                        \
29         }                                                 \
30     } while (g_test_timer_elapsed() < CONNECTION_TIMEOUT); \
31     g_assert_cmpstr(resp, ==, e);                         \
32     g_free(resp);                                         \
33 } while (0)
34 
35 static gchar *tmpdir;
36 
37 static int inet_get_free_port_socket_ipv4(int sock)
38 {
39     struct sockaddr_in addr;
40     socklen_t len;
41 
42     memset(&addr, 0, sizeof(addr));
43     addr.sin_family = AF_INET;
44     addr.sin_addr.s_addr = INADDR_ANY;
45     addr.sin_port = 0;
46     if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
47         return -1;
48     }
49 
50     len = sizeof(addr);
51     if (getsockname(sock,  (struct sockaddr *)&addr, &len) < 0) {
52         return -1;
53     }
54 
55     return ntohs(addr.sin_port);
56 }
57 
58 static int inet_get_free_port_socket_ipv6(int sock)
59 {
60     struct sockaddr_in6 addr;
61     socklen_t len;
62 
63     memset(&addr, 0, sizeof(addr));
64     addr.sin6_family = AF_INET6;
65     addr.sin6_addr = in6addr_any;
66     addr.sin6_port = 0;
67     if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
68         return -1;
69     }
70 
71     len = sizeof(addr);
72     if (getsockname(sock,  (struct sockaddr *)&addr, &len) < 0) {
73         return -1;
74     }
75 
76     return ntohs(addr.sin6_port);
77 }
78 
79 static int inet_get_free_port_multiple(int nb, int *port, bool ipv6)
80 {
81     int sock[nb];
82     int i;
83 
84     for (i = 0; i < nb; i++) {
85         sock[i] = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
86         if (sock[i] < 0) {
87             break;
88         }
89         port[i] = ipv6 ? inet_get_free_port_socket_ipv6(sock[i]) :
90                          inet_get_free_port_socket_ipv4(sock[i]);
91         if (port[i] == -1) {
92             break;
93         }
94     }
95 
96     nb = i;
97     for (i = 0; i < nb; i++) {
98         closesocket(sock[i]);
99     }
100 
101     return nb;
102 }
103 
104 static int inet_get_free_port(bool ipv6)
105 {
106     int nb, port;
107 
108     nb = inet_get_free_port_multiple(1, &port, ipv6);
109     g_assert_cmpint(nb, ==, 1);
110 
111     return port;
112 }
113 
114 static void test_stream_inet_ipv4(void)
115 {
116     QTestState *qts0, *qts1;
117     char *expect;
118     int port;
119 
120     port = inet_get_free_port(false);
121     qts0 = qtest_initf("-nodefaults -M none "
122                        "-netdev stream,id=st0,server=true,addr.type=inet,"
123                        "addr.ipv4=on,addr.ipv6=off,"
124                        "addr.host=127.0.0.1,addr.port=%d", port);
125 
126     EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
127 
128     qts1 = qtest_initf("-nodefaults -M none "
129                        "-netdev stream,server=false,id=st0,addr.type=inet,"
130                        "addr.ipv4=on,addr.ipv6=off,"
131                        "addr.host=127.0.0.1,addr.port=%d", port);
132 
133     expect = g_strdup_printf("st0: index=0,type=stream,tcp:127.0.0.1:%d\r\n",
134                              port);
135     EXPECT_STATE(qts1, expect, 0);
136     g_free(expect);
137 
138     /* the port is unknown, check only the address */
139     EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:127.0.0.1", ':');
140 
141     qtest_quit(qts1);
142     qtest_quit(qts0);
143 }
144 
145 static void test_stream_inet_ipv6(void)
146 {
147     QTestState *qts0, *qts1;
148     char *expect;
149     int port;
150 
151     port = inet_get_free_port(true);
152     qts0 = qtest_initf("-nodefaults -M none "
153                        "-netdev stream,id=st0,server=true,addr.type=inet,"
154                        "addr.ipv4=off,addr.ipv6=on,"
155                        "addr.host=::1,addr.port=%d", port);
156 
157     EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
158 
159     qts1 = qtest_initf("-nodefaults -M none "
160                        "-netdev stream,server=false,id=st0,addr.type=inet,"
161                        "addr.ipv4=off,addr.ipv6=on,"
162                        "addr.host=::1,addr.port=%d", port);
163 
164     expect = g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n",
165                              port);
166     EXPECT_STATE(qts1, expect, 0);
167     g_free(expect);
168 
169     /* the port is unknown, check only the address */
170     EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':');
171 
172     qtest_quit(qts1);
173     qtest_quit(qts0);
174 }
175 
176 static void test_stream_unix(void)
177 {
178     QTestState *qts0, *qts1;
179     char *expect;
180     gchar *path;
181 
182     path = g_strconcat(tmpdir, "/stream_unix", NULL);
183 
184     qts0 = qtest_initf("-nodefaults -M none "
185                        "-netdev stream,id=st0,server=true,"
186                        "addr.type=unix,addr.path=%s,",
187                        path);
188 
189     EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
190 
191     qts1 = qtest_initf("-nodefaults -M none "
192                        "-netdev stream,id=st0,server=false,"
193                        "addr.type=unix,addr.path=%s",
194                        path);
195 
196     expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path);
197     EXPECT_STATE(qts1, expect, 0);
198     EXPECT_STATE(qts0, expect, 0);
199     g_free(expect);
200     g_free(path);
201 
202     qtest_quit(qts1);
203     qtest_quit(qts0);
204 }
205 
206 #ifdef CONFIG_LINUX
207 static void test_stream_unix_abstract(void)
208 {
209     QTestState *qts0, *qts1;
210     char *expect;
211     gchar *path;
212 
213     path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL);
214 
215     qts0 = qtest_initf("-nodefaults -M none "
216                        "-netdev stream,id=st0,server=true,"
217                        "addr.type=unix,addr.path=%s,"
218                        "addr.abstract=on",
219                        path);
220 
221     EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
222 
223     qts1 = qtest_initf("-nodefaults -M none "
224                        "-netdev stream,id=st0,server=false,"
225                        "addr.type=unix,addr.path=%s,addr.abstract=on",
226                        path);
227 
228     expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path);
229     EXPECT_STATE(qts1, expect, 0);
230     EXPECT_STATE(qts0, expect, 0);
231     g_free(expect);
232     g_free(path);
233 
234     qtest_quit(qts1);
235     qtest_quit(qts0);
236 }
237 #endif
238 
239 #ifndef _WIN32
240 static void test_stream_fd(void)
241 {
242     QTestState *qts0, *qts1;
243     int sock[2];
244     int ret;
245 
246     ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock);
247     g_assert_true(ret == 0);
248 
249     qts0 = qtest_initf("-nodefaults -M none "
250                        "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
251                        sock[0]);
252 
253     EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
254 
255     qts1 = qtest_initf("-nodefaults -M none "
256                        "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
257                        sock[1]);
258 
259     EXPECT_STATE(qts1, "st0: index=0,type=stream,unix:\r\n", 0);
260     EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
261 
262     qtest_quit(qts1);
263     qtest_quit(qts0);
264 
265     closesocket(sock[0]);
266     closesocket(sock[1]);
267 }
268 #endif
269 
270 static void test_dgram_inet(void)
271 {
272     QTestState *qts0, *qts1;
273     char *expect;
274     int port[2];
275     int nb;
276 
277     nb = inet_get_free_port_multiple(2, port, false);
278     g_assert_cmpint(nb, ==, 2);
279 
280     qts0 = qtest_initf("-nodefaults -M none "
281                        "-netdev dgram,id=st0,"
282                        "local.type=inet,local.host=127.0.0.1,local.port=%d,"
283                        "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
284                         port[0], port[1]);
285 
286     expect = g_strdup_printf("st0: index=0,type=dgram,"
287                              "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
288                              port[0], port[1]);
289     EXPECT_STATE(qts0, expect, 0);
290     g_free(expect);
291 
292     qts1 = qtest_initf("-nodefaults -M none "
293                        "-netdev dgram,id=st0,"
294                        "local.type=inet,local.host=127.0.0.1,local.port=%d,"
295                        "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
296                         port[1], port[0]);
297 
298     expect = g_strdup_printf("st0: index=0,type=dgram,"
299                              "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
300                              port[1], port[0]);
301     EXPECT_STATE(qts1, expect, 0);
302     g_free(expect);
303 
304     qtest_quit(qts1);
305     qtest_quit(qts0);
306 }
307 
308 #ifndef _WIN32
309 static void test_dgram_mcast(void)
310 {
311     QTestState *qts;
312 
313     qts = qtest_initf("-nodefaults -M none "
314                       "-netdev dgram,id=st0,"
315                       "remote.type=inet,remote.host=230.0.0.1,remote.port=1234");
316 
317     EXPECT_STATE(qts, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0);
318 
319     qtest_quit(qts);
320 }
321 
322 static void test_dgram_unix(void)
323 {
324     QTestState *qts0, *qts1;
325     char *expect;
326     gchar *path0, *path1;
327 
328     path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL);
329     path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL);
330 
331     qts0 = qtest_initf("-nodefaults -M none "
332                        "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
333                        "remote.type=unix,remote.path=%s",
334                        path0, path1);
335 
336     expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
337                              path0, path1);
338     EXPECT_STATE(qts0, expect, 0);
339     g_free(expect);
340 
341     qts1 = qtest_initf("-nodefaults -M none "
342                        "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
343                        "remote.type=unix,remote.path=%s",
344                        path1, path0);
345 
346 
347     expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
348                              path1, path0);
349     EXPECT_STATE(qts1, expect, 0);
350     g_free(expect);
351 
352     unlink(path0);
353     g_free(path0);
354     unlink(path1);
355     g_free(path1);
356 
357     qtest_quit(qts1);
358     qtest_quit(qts0);
359 }
360 
361 static void test_dgram_fd(void)
362 {
363     QTestState *qts0, *qts1;
364     char *expect;
365     int ret;
366     int sv[2];
367 
368     ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv);
369     g_assert_cmpint(ret, !=, -1);
370 
371     qts0 = qtest_initf("-nodefaults -M none "
372                        "-netdev dgram,id=st0,local.type=fd,local.str=%d",
373                        sv[0]);
374 
375     expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[0]);
376     EXPECT_STATE(qts0, expect, 0);
377     g_free(expect);
378 
379     qts1 = qtest_initf("-nodefaults -M none "
380                        "-netdev dgram,id=st0,local.type=fd,local.str=%d",
381                        sv[1]);
382 
383 
384     expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[1]);
385     EXPECT_STATE(qts1, expect, 0);
386     g_free(expect);
387 
388     qtest_quit(qts1);
389     qtest_quit(qts0);
390 
391     closesocket(sv[0]);
392     closesocket(sv[1]);
393 }
394 #endif
395 
396 int main(int argc, char **argv)
397 {
398     int ret;
399     bool has_ipv4, has_ipv6, has_afunix;
400     g_autoptr(GError) err = NULL;
401 
402     socket_init();
403     g_test_init(&argc, &argv, NULL);
404 
405     if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
406         g_error("socket_check_protocol_support() failed\n");
407     }
408 
409     tmpdir = g_dir_make_tmp("netdev-socket.XXXXXX", &err);
410     if (tmpdir == NULL) {
411         g_error("Can't create temporary directory in %s: %s",
412                 g_get_tmp_dir(), err->message);
413     }
414 
415     if (has_ipv4) {
416         qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4);
417         qtest_add_func("/netdev/dgram/inet", test_dgram_inet);
418 #ifndef _WIN32
419         qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast);
420 #endif
421     }
422     if (has_ipv6) {
423         qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6);
424     }
425 
426     socket_check_afunix_support(&has_afunix);
427     if (has_afunix) {
428 #ifndef _WIN32
429         qtest_add_func("/netdev/dgram/unix", test_dgram_unix);
430 #endif
431         qtest_add_func("/netdev/stream/unix", test_stream_unix);
432 #ifdef CONFIG_LINUX
433         qtest_add_func("/netdev/stream/unix/abstract",
434                        test_stream_unix_abstract);
435 #endif
436 #ifndef _WIN32
437         qtest_add_func("/netdev/stream/fd", test_stream_fd);
438         qtest_add_func("/netdev/dgram/fd", test_dgram_fd);
439 #endif
440     }
441 
442     ret = g_test_run();
443 
444     g_rmdir(tmpdir);
445     g_free(tmpdir);
446 
447     return ret;
448 }
449