POSIX defines EINPROGRESS as the return value for non-blocking connect() [1]. But in Linux, AF_UNIX connect() returns EAGAIN instead of EINPROGRESS. (but only for AF_UNIX sockets!) [2]
Both cases should be handled the same way - by returning the `fd` and letting the caller to complete connection asynchronously. [1]: https://pubs.opengroup.org/onlinepubs/9699919799.2016edition/functions/connect.html [2]: see `connect(2)` on Linux for details Signed-off-by: Ihar Hrachyshka <ihrac...@redhat.com> --- lib/socket-util-unix.c | 8 +-- lib/socket-util.c | 2 +- lib/socket-util.h | 2 + tests/.gitignore | 1 + tests/automake.mk | 3 +- tests/library.at | 5 ++ tests/test-unix-socket-listen-backlog.c | 73 +++++++++++++++++++++++++ 7 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 tests/test-unix-socket-listen-backlog.c diff --git a/lib/socket-util-unix.c b/lib/socket-util-unix.c index 59f63fcce..1543aa2e2 100644 --- a/lib/socket-util-unix.c +++ b/lib/socket-util-unix.c @@ -363,7 +363,10 @@ make_unix_socket(int style, bool nonblock, error = make_sockaddr_un(connect_path, &un, &un_len, &dirfd, linkname); if (!error && connect(fd, (struct sockaddr*) &un, un_len) - && errno != EINPROGRESS) { + /* POSIX connect() returns EINPROGRESS for all non-blocking + * sockets. Linux connect() returns either EAGAIN - for AF_UNIX + * sockets - or EINPROGRESS - for other families. Handle both. */ + && errno != EINPROGRESS && errno != EAGAIN) { error = errno; } free_sockaddr_un(dirfd, linkname); @@ -376,9 +379,6 @@ make_unix_socket(int style, bool nonblock, return fd; error: - if (error == EAGAIN) { - error = EPROTO; - } if (bind_path) { fatal_signal_unlink_file_now(bind_path); } diff --git a/lib/socket-util.c b/lib/socket-util.c index 2d89fce85..bc9628b38 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -760,7 +760,7 @@ inet_open_passive(int style, const char *target, int default_port, } /* Listen. */ - if (style == SOCK_STREAM && listen(fd, 64) < 0) { + if (style == SOCK_STREAM && listen(fd, LISTEN_BACKLOG) < 0) { error = sock_errno(); VLOG_ERR("%s: listen: %s", target, sock_strerror(error)); goto error; diff --git a/lib/socket-util.h b/lib/socket-util.h index 4eec627e3..03219633d 100644 --- a/lib/socket-util.h +++ b/lib/socket-util.h @@ -28,6 +28,8 @@ #include <netinet/in_systm.h> #include <netinet/ip.h> +#define LISTEN_BACKLOG 64 + struct ds; int set_nonblocking(int fd); diff --git a/tests/.gitignore b/tests/.gitignore index 3a8c45975..c32b2c7cc 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -66,6 +66,7 @@ /test-timeval /test-type-props /test-unix-socket +/test-unix-socket-listen-backlog /test-util /test-uuid /test-uuidset diff --git a/tests/automake.mk b/tests/automake.mk index 04f48f2d8..fc74cfa7d 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -493,7 +493,8 @@ tests_ovstest_SOURCES = \ if !WIN32 tests_ovstest_SOURCES += \ - tests/test-unix-socket.c + tests/test-unix-socket.c \ + tests/test-unix-socket-listen-backlog.c endif if LINUX diff --git a/tests/library.at b/tests/library.at index d962e1b3f..fff92bc93 100644 --- a/tests/library.at +++ b/tests/library.at @@ -219,6 +219,11 @@ AT_CHECK([mkdir $longname || exit 77]) AT_CHECK([cd $longname && $PYTHON3 $abs_srcdir/test-unix-socket.py ../$longname/socket socket]) AT_CLEANUP +AT_SETUP([unix socket, listen backlog]) +AT_SKIP_IF([test "$IS_WIN32" = "yes"]) +AT_CHECK([ovstest test-unix-socket-listen-backlog x x]) +AT_CLEANUP + AT_SETUP([ovs_assert]) if test "$IS_WIN32" = "yes"; then exit_status=9 diff --git a/tests/test-unix-socket-listen-backlog.c b/tests/test-unix-socket-listen-backlog.c new file mode 100644 index 000000000..d1a73ba39 --- /dev/null +++ b/tests/test-unix-socket-listen-backlog.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010, 2012, 2014 Nicira, Inc. + * Copyright (c) 2024 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#undef NDEBUG +#include "socket-util.h" +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include "ovstest.h" +#include "util.h" + +static void +test_unix_socket_listen_backlog_main(int argc, char *argv[]) +{ + const char *servername; + const char *clientname; + int serversock; + int clientsocks[LISTEN_BACKLOG + 1]; + + set_program_name(argv[0]); + + if (argc != 3) { + ovs_fatal(0, "usage: %s SERVERSOCKET CLIENTSOCKET", argv[0]); + } + servername = argv[1]; + clientname = argv[2]; + + signal(SIGALRM, SIG_DFL); + alarm(5); + + /* Create a listening socket under name 'serversocket'. */ + serversock = make_unix_socket(SOCK_STREAM, false, servername, NULL); + if (serversock < 0) { + ovs_fatal(-serversock, "%s: bind failed", servername); + } + if (listen(serversock, 1)) { + ovs_fatal(errno, "%s: listen failed", servername); + } + + /* Connect to 'clientname' (which should be the same file, perhaps under a + * different name). Connect enough times to overflow listen backlog. The + * last attempt should succeed, even though listen backlog is full and + * connect() returns EAGAIN (on Linux) or EINPROGRESS (on POSIX). */ + for (int i = 0; i < sizeof(clientsocks) / sizeof(clientsocks[0]); i++) { + clientsocks[i] = make_unix_socket(SOCK_STREAM, true, NULL, clientname); + if (clientsocks[i] < 0) { + ovs_fatal(-clientsocks[i], "%s: connect failed", clientname); + } + } + + for (int i = 0; i < sizeof(clientsocks) / sizeof(clientsocks[0]); i++) { + close(clientsocks[i]); + } + close(serversock); +} + +OVSTEST_REGISTER("test-unix-socket-listen-backlog", + test_unix_socket_listen_backlog_main); -- 2.41.0 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev