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