417296c8d8
The netdev-socket test intermittently fails on our s390x CI runner: 633/659 ERROR:../tests/qtest/netdev-socket.c:197:test_stream_unix: assertion failed (resp == expect): ("st0: index=0,type=stream,connection error\r\n" == "st0: index=0,type=stream,unix:/tmp/netdev-socket.GZUG01/stream_unix\r\n") ERROR 633/659 qemu:qtest+qtest-xtensa / qtest-xtensa/netdev-socket ERROR 5.47s killed by signal 6 SIGABRT This may just be because when the machine is under heavy load running the CI tests it hits the timeout before the QEMU under test has started to the point of being able to respond to HMP queries. Bump the timeout to 60 seconds to see if the intermittent goes away. Acked-by: Thomas Huth <thuth@redhat.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20230207165119.1479132-1-peter.maydell@linaro.org
449 lines
12 KiB
C
449 lines
12 KiB
C
/*
|
|
* QTest testcase for netdev stream and dgram
|
|
*
|
|
* Copyright (c) 2022 Red Hat, Inc.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/sockets.h"
|
|
#include <glib/gstdio.h>
|
|
#include "../unit/socket-helpers.h"
|
|
#include "libqtest.h"
|
|
|
|
#define CONNECTION_TIMEOUT 60
|
|
|
|
#define EXPECT_STATE(q, e, t) \
|
|
do { \
|
|
char *resp = NULL; \
|
|
g_test_timer_start(); \
|
|
do { \
|
|
g_free(resp); \
|
|
resp = qtest_hmp(q, "info network"); \
|
|
if (t) { \
|
|
strrchr(resp, t)[0] = 0; \
|
|
} \
|
|
if (g_str_equal(resp, e)) { \
|
|
break; \
|
|
} \
|
|
} while (g_test_timer_elapsed() < CONNECTION_TIMEOUT); \
|
|
g_assert_cmpstr(resp, ==, e); \
|
|
g_free(resp); \
|
|
} while (0)
|
|
|
|
static gchar *tmpdir;
|
|
|
|
static int inet_get_free_port_socket_ipv4(int sock)
|
|
{
|
|
struct sockaddr_in addr;
|
|
socklen_t len;
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
addr.sin_port = 0;
|
|
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
len = sizeof(addr);
|
|
if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return ntohs(addr.sin_port);
|
|
}
|
|
|
|
static int inet_get_free_port_socket_ipv6(int sock)
|
|
{
|
|
struct sockaddr_in6 addr;
|
|
socklen_t len;
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin6_family = AF_INET6;
|
|
addr.sin6_addr = in6addr_any;
|
|
addr.sin6_port = 0;
|
|
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
len = sizeof(addr);
|
|
if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return ntohs(addr.sin6_port);
|
|
}
|
|
|
|
static int inet_get_free_port_multiple(int nb, int *port, bool ipv6)
|
|
{
|
|
int sock[nb];
|
|
int i;
|
|
|
|
for (i = 0; i < nb; i++) {
|
|
sock[i] = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
|
|
if (sock[i] < 0) {
|
|
break;
|
|
}
|
|
port[i] = ipv6 ? inet_get_free_port_socket_ipv6(sock[i]) :
|
|
inet_get_free_port_socket_ipv4(sock[i]);
|
|
if (port[i] == -1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
nb = i;
|
|
for (i = 0; i < nb; i++) {
|
|
closesocket(sock[i]);
|
|
}
|
|
|
|
return nb;
|
|
}
|
|
|
|
static int inet_get_free_port(bool ipv6)
|
|
{
|
|
int nb, port;
|
|
|
|
nb = inet_get_free_port_multiple(1, &port, ipv6);
|
|
g_assert_cmpint(nb, ==, 1);
|
|
|
|
return port;
|
|
}
|
|
|
|
static void test_stream_inet_ipv4(void)
|
|
{
|
|
QTestState *qts0, *qts1;
|
|
char *expect;
|
|
int port;
|
|
|
|
port = inet_get_free_port(false);
|
|
qts0 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,id=st0,server=true,addr.type=inet,"
|
|
"addr.ipv4=on,addr.ipv6=off,"
|
|
"addr.host=127.0.0.1,addr.port=%d", port);
|
|
|
|
EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
|
|
|
|
qts1 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,server=false,id=st0,addr.type=inet,"
|
|
"addr.ipv4=on,addr.ipv6=off,"
|
|
"addr.host=127.0.0.1,addr.port=%d", port);
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=stream,tcp:127.0.0.1:%d\r\n",
|
|
port);
|
|
EXPECT_STATE(qts1, expect, 0);
|
|
g_free(expect);
|
|
|
|
/* the port is unknown, check only the address */
|
|
EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:127.0.0.1", ':');
|
|
|
|
qtest_quit(qts1);
|
|
qtest_quit(qts0);
|
|
}
|
|
|
|
static void test_stream_inet_ipv6(void)
|
|
{
|
|
QTestState *qts0, *qts1;
|
|
char *expect;
|
|
int port;
|
|
|
|
port = inet_get_free_port(true);
|
|
qts0 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,id=st0,server=true,addr.type=inet,"
|
|
"addr.ipv4=off,addr.ipv6=on,"
|
|
"addr.host=::1,addr.port=%d", port);
|
|
|
|
EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
|
|
|
|
qts1 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,server=false,id=st0,addr.type=inet,"
|
|
"addr.ipv4=off,addr.ipv6=on,"
|
|
"addr.host=::1,addr.port=%d", port);
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n",
|
|
port);
|
|
EXPECT_STATE(qts1, expect, 0);
|
|
g_free(expect);
|
|
|
|
/* the port is unknown, check only the address */
|
|
EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':');
|
|
|
|
qtest_quit(qts1);
|
|
qtest_quit(qts0);
|
|
}
|
|
|
|
static void test_stream_unix(void)
|
|
{
|
|
QTestState *qts0, *qts1;
|
|
char *expect;
|
|
gchar *path;
|
|
|
|
path = g_strconcat(tmpdir, "/stream_unix", NULL);
|
|
|
|
qts0 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,id=st0,server=true,"
|
|
"addr.type=unix,addr.path=%s,",
|
|
path);
|
|
|
|
EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
|
|
|
|
qts1 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,id=st0,server=false,"
|
|
"addr.type=unix,addr.path=%s",
|
|
path);
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path);
|
|
EXPECT_STATE(qts1, expect, 0);
|
|
EXPECT_STATE(qts0, expect, 0);
|
|
g_free(expect);
|
|
g_free(path);
|
|
|
|
qtest_quit(qts1);
|
|
qtest_quit(qts0);
|
|
}
|
|
|
|
#ifdef CONFIG_LINUX
|
|
static void test_stream_unix_abstract(void)
|
|
{
|
|
QTestState *qts0, *qts1;
|
|
char *expect;
|
|
gchar *path;
|
|
|
|
path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL);
|
|
|
|
qts0 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,id=st0,server=true,"
|
|
"addr.type=unix,addr.path=%s,"
|
|
"addr.abstract=on",
|
|
path);
|
|
|
|
EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
|
|
|
|
qts1 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,id=st0,server=false,"
|
|
"addr.type=unix,addr.path=%s,addr.abstract=on",
|
|
path);
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path);
|
|
EXPECT_STATE(qts1, expect, 0);
|
|
EXPECT_STATE(qts0, expect, 0);
|
|
g_free(expect);
|
|
g_free(path);
|
|
|
|
qtest_quit(qts1);
|
|
qtest_quit(qts0);
|
|
}
|
|
#endif
|
|
|
|
#ifndef _WIN32
|
|
static void test_stream_fd(void)
|
|
{
|
|
QTestState *qts0, *qts1;
|
|
int sock[2];
|
|
int ret;
|
|
|
|
ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock);
|
|
g_assert_true(ret == 0);
|
|
|
|
qts0 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,id=st0,addr.type=fd,addr.str=%d",
|
|
sock[0]);
|
|
|
|
EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
|
|
|
|
qts1 = qtest_initf("-nodefaults -M none "
|
|
"-netdev stream,id=st0,addr.type=fd,addr.str=%d",
|
|
sock[1]);
|
|
|
|
EXPECT_STATE(qts1, "st0: index=0,type=stream,unix:\r\n", 0);
|
|
EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
|
|
|
|
qtest_quit(qts1);
|
|
qtest_quit(qts0);
|
|
|
|
closesocket(sock[0]);
|
|
closesocket(sock[1]);
|
|
}
|
|
#endif
|
|
|
|
static void test_dgram_inet(void)
|
|
{
|
|
QTestState *qts0, *qts1;
|
|
char *expect;
|
|
int port[2];
|
|
int nb;
|
|
|
|
nb = inet_get_free_port_multiple(2, port, false);
|
|
g_assert_cmpint(nb, ==, 2);
|
|
|
|
qts0 = qtest_initf("-nodefaults -M none "
|
|
"-netdev dgram,id=st0,"
|
|
"local.type=inet,local.host=127.0.0.1,local.port=%d,"
|
|
"remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
|
|
port[0], port[1]);
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=dgram,"
|
|
"udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
|
|
port[0], port[1]);
|
|
EXPECT_STATE(qts0, expect, 0);
|
|
g_free(expect);
|
|
|
|
qts1 = qtest_initf("-nodefaults -M none "
|
|
"-netdev dgram,id=st0,"
|
|
"local.type=inet,local.host=127.0.0.1,local.port=%d,"
|
|
"remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
|
|
port[1], port[0]);
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=dgram,"
|
|
"udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
|
|
port[1], port[0]);
|
|
EXPECT_STATE(qts1, expect, 0);
|
|
g_free(expect);
|
|
|
|
qtest_quit(qts1);
|
|
qtest_quit(qts0);
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
static void test_dgram_mcast(void)
|
|
{
|
|
QTestState *qts;
|
|
|
|
qts = qtest_initf("-nodefaults -M none "
|
|
"-netdev dgram,id=st0,"
|
|
"remote.type=inet,remote.host=230.0.0.1,remote.port=1234");
|
|
|
|
EXPECT_STATE(qts, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0);
|
|
|
|
qtest_quit(qts);
|
|
}
|
|
|
|
static void test_dgram_unix(void)
|
|
{
|
|
QTestState *qts0, *qts1;
|
|
char *expect;
|
|
gchar *path0, *path1;
|
|
|
|
path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL);
|
|
path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL);
|
|
|
|
qts0 = qtest_initf("-nodefaults -M none "
|
|
"-netdev dgram,id=st0,local.type=unix,local.path=%s,"
|
|
"remote.type=unix,remote.path=%s",
|
|
path0, path1);
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
|
|
path0, path1);
|
|
EXPECT_STATE(qts0, expect, 0);
|
|
g_free(expect);
|
|
|
|
qts1 = qtest_initf("-nodefaults -M none "
|
|
"-netdev dgram,id=st0,local.type=unix,local.path=%s,"
|
|
"remote.type=unix,remote.path=%s",
|
|
path1, path0);
|
|
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
|
|
path1, path0);
|
|
EXPECT_STATE(qts1, expect, 0);
|
|
g_free(expect);
|
|
|
|
unlink(path0);
|
|
g_free(path0);
|
|
unlink(path1);
|
|
g_free(path1);
|
|
|
|
qtest_quit(qts1);
|
|
qtest_quit(qts0);
|
|
}
|
|
|
|
static void test_dgram_fd(void)
|
|
{
|
|
QTestState *qts0, *qts1;
|
|
char *expect;
|
|
int ret;
|
|
int sv[2];
|
|
|
|
ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv);
|
|
g_assert_cmpint(ret, !=, -1);
|
|
|
|
qts0 = qtest_initf("-nodefaults -M none "
|
|
"-netdev dgram,id=st0,local.type=fd,local.str=%d",
|
|
sv[0]);
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[0]);
|
|
EXPECT_STATE(qts0, expect, 0);
|
|
g_free(expect);
|
|
|
|
qts1 = qtest_initf("-nodefaults -M none "
|
|
"-netdev dgram,id=st0,local.type=fd,local.str=%d",
|
|
sv[1]);
|
|
|
|
|
|
expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[1]);
|
|
EXPECT_STATE(qts1, expect, 0);
|
|
g_free(expect);
|
|
|
|
qtest_quit(qts1);
|
|
qtest_quit(qts0);
|
|
|
|
closesocket(sv[0]);
|
|
closesocket(sv[1]);
|
|
}
|
|
#endif
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int ret;
|
|
bool has_ipv4, has_ipv6, has_afunix;
|
|
g_autoptr(GError) err = NULL;
|
|
|
|
socket_init();
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
|
|
g_error("socket_check_protocol_support() failed\n");
|
|
}
|
|
|
|
tmpdir = g_dir_make_tmp("netdev-socket.XXXXXX", &err);
|
|
if (tmpdir == NULL) {
|
|
g_error("Can't create temporary directory in %s: %s",
|
|
g_get_tmp_dir(), err->message);
|
|
}
|
|
|
|
if (has_ipv4) {
|
|
qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4);
|
|
qtest_add_func("/netdev/dgram/inet", test_dgram_inet);
|
|
#ifndef _WIN32
|
|
qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast);
|
|
#endif
|
|
}
|
|
if (has_ipv6) {
|
|
qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6);
|
|
}
|
|
|
|
socket_check_afunix_support(&has_afunix);
|
|
if (has_afunix) {
|
|
#ifndef _WIN32
|
|
qtest_add_func("/netdev/dgram/unix", test_dgram_unix);
|
|
#endif
|
|
qtest_add_func("/netdev/stream/unix", test_stream_unix);
|
|
#ifdef CONFIG_LINUX
|
|
qtest_add_func("/netdev/stream/unix/abstract",
|
|
test_stream_unix_abstract);
|
|
#endif
|
|
#ifndef _WIN32
|
|
qtest_add_func("/netdev/stream/fd", test_stream_fd);
|
|
qtest_add_func("/netdev/dgram/fd", test_dgram_fd);
|
|
#endif
|
|
}
|
|
|
|
ret = g_test_run();
|
|
|
|
g_rmdir(tmpdir);
|
|
g_free(tmpdir);
|
|
|
|
return ret;
|
|
}
|