Re: [ovs-dev] [PATCH] socket: Don't fail when AF_UNIX connect() returns EAGAIN.

2024-05-02 Thread Ilya Maximets
On 4/27/24 00:42, Ihar Hrachyshka wrote:
> 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 
> ---

Hi, Ihar.  Thanks for the patch!

I was reading the original commit that introduced the error mangling:

commit 21a60ea97b492ae642d5b35430b24d137cd67f35
Author: Ben Pfaff 
Date:   Fri Dec 11 16:59:44 2009 -0800

socket-util: Clarify EAGAIN error code for make_unix_socket().

make_unix_socket() can return EAGAIN in rare circumstances, e.g. when the
server's socket listen queue is full.  A lot of OVS callers interpret
EAGAIN as a "try again" error code, but in this case it means that the
attempt to create the socket failed.  So munge EAGAIN into another error
code to prevent that misinterpretation.

IIUC, at the time there were more direct users for this function
that would not re-try the connect() and will remain in the broken
state.  Most of them were migrated to the stream- and reconnect-
wrappers, so they will actually re-connect normally now.  That's
should be fine.

There is still one direct user in syslog-direct.c, but it is using
a blocking connection, so I guess it can't fall into this condition.

So, the change is fine, but I think more information addressing the
reasoning from the original commit should be added to the commit
message.


And this patch is missing changes for the equivalent python code in
python/ovs/socket_util.py.  We should keep them in sync.

>  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. */

2 spaces between sentences.

> +&& 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 
>  #include 
>  
> +#define LISTEN_BACKLOG 64

Should this also be used in stream-unix.c ?

might also be better to define the same constant for python counterparts.

> +
>  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/te

Re: [ovs-dev] [PATCH] socket: Don't fail when AF_UNIX connect() returns EAGAIN.

2024-05-01 Thread Simon Horman
On Fri, Apr 26, 2024 at 10:42:36PM +, Ihar Hrachyshka wrote:
> 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 

Acked-by: Simon Horman 

___
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev


[ovs-dev] [PATCH] socket: Don't fail when AF_UNIX connect() returns EAGAIN.

2024-04-26 Thread Ihar Hrachyshka
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 
---
 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 
 #include 
 
+#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 0..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 
+#undef NDEBUG
+#include "socket-util.h"
+#include 
+#include 
+#include 
+#include "ovstest.h"
+#include "util.h"
+
+static void
+test_unix_socket_listen_backlog_main(int argc, char *argv[])
+{
+const char *servername;
+