]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: proto: make common fd checks in sock_create_server_socket
authorValentine Krasnobaeva <vkrasnobaeva@haproxy.com>
Tue, 23 Apr 2024 21:37:43 +0000 (23:37 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 30 Apr 2024 19:39:24 +0000 (21:39 +0200)
quic_connect_server(), tcp_connect_server(), uxst_connect_server() duplicate
same code to check different ERRNOs, that socket() and setns() may return.
They also duplicate some runtime condition checks, applied to the obtained
server socket fd.

So, in order to remove these duplications and to improve code readability,
let's encapsulate socket() and setns() ERRNOs handling in
sock_handle_system_err(). It must be called just before fd's runtime condition
checks, which we also move in sock_create_server_socket by the same reason.

include/haproxy/sock.h
src/proto_quic.c
src/proto_tcp.c
src/proto_uxst.c
src/sock.c

index b8bd731b07a75cbf80d7f43ce74af525ef567f7b..ba8e9d021dfa233a71fd0df2014df67b8e1fb94e 100644 (file)
@@ -30,7 +30,7 @@
 #include <haproxy/listener-t.h>
 #include <haproxy/sock-t.h>
 
-int sock_create_server_socket(struct connection *conn);
+int sock_create_server_socket(struct connection *conn, struct proxy *be);
 void sock_enable(struct receiver *rx);
 void sock_disable(struct receiver *rx);
 void sock_unbind(struct receiver *rx);
index d7559e737051b1ea94a780a2a3a2be5a71bdf6f2..c616f14e8e2028d21833088913a5d413f82f06b0 100644 (file)
@@ -301,67 +301,12 @@ int quic_connect_server(struct connection *conn, int flags)
                return SF_ERR_INTERNAL;
        }
 
-       fd = conn->handle.fd = sock_create_server_socket(conn);
-
-       if (fd == -1) {
-               qfprintf(stderr, "Cannot get a server socket.\n");
-
-               if (errno == ENFILE) {
-                       conn->err_code = CO_ER_SYS_FDLIM;
-                       send_log(be, LOG_EMERG,
-                                "Proxy %s reached system FD limit (maxsock=%d). Please check system tunables.\n",
-                                be->id, global.maxsock);
-               }
-               else if (errno == EMFILE) {
-                       conn->err_code = CO_ER_PROC_FDLIM;
-                       send_log(be, LOG_EMERG,
-                                "Proxy %s reached process FD limit (maxsock=%d). Please check 'ulimit-n' and restart.\n",
-                                be->id, global.maxsock);
-               }
-               else if (errno == ENOBUFS || errno == ENOMEM) {
-                       conn->err_code = CO_ER_SYS_MEMLIM;
-                       send_log(be, LOG_EMERG,
-                                "Proxy %s reached system memory limit (maxsock=%d). Please check system tunables.\n",
-                                be->id, global.maxsock);
-               }
-               else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
-                       conn->err_code = CO_ER_NOPROTO;
-               }
-               else
-                       conn->err_code = CO_ER_SOCK_ERR;
-
-               /* this is a resource error */
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_RESOURCE;
-       }
-
-       if (fd >= global.maxsock) {
-               /* do not log anything there, it's a normal condition when this option
-                * is used to serialize connections to a server !
-                */
-               ha_alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
-               close(fd);
-               conn->err_code = CO_ER_CONF_FDLIM;
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_PRXCOND; /* it is a configuration limit */
-       }
-
-       if (fd_set_nonblock(fd) == -1) {
-               qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
-               close(fd);
-               conn->err_code = CO_ER_SOCK_ERR;
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_INTERNAL;
-       }
-
-       if (master == 1 && fd_set_cloexec(fd) == -1) {
-               ha_alert("Cannot set CLOEXEC on client socket.\n");
-               close(fd);
-               conn->err_code = CO_ER_SOCK_ERR;
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_INTERNAL;
-       }
+       /* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
+       fd = conn->handle.fd = sock_create_server_socket(conn, be);
+       if (fd & SF_ERR_MASK)
+               return fd;
 
+       /* FD is ok, perform protocol specific settings */
        /* allow specific binding :
         * - server-specific at first
         * - proxy-specific next
index 45ce27f11e01724fbcfb95e9d39ab319efe543c0..b1a369871c21721fce6c1268db276985f32ff939 100644 (file)
@@ -298,68 +298,14 @@ int tcp_connect_server(struct connection *conn, int flags)
                return SF_ERR_INTERNAL;
        }
 
-       fd = conn->handle.fd = sock_create_server_socket(conn);
 
-       if (fd == -1) {
-               qfprintf(stderr, "Cannot get a server socket.\n");
 
-               if (errno == ENFILE) {
-                       conn->err_code = CO_ER_SYS_FDLIM;
-                       send_log(be, LOG_EMERG,
-                                "Proxy %s reached system FD limit (maxsock=%d). Please check system tunables.\n",
-                                be->id, global.maxsock);
-               }
-               else if (errno == EMFILE) {
-                       conn->err_code = CO_ER_PROC_FDLIM;
-                       send_log(be, LOG_EMERG,
-                                "Proxy %s reached process FD limit (maxsock=%d). Please check 'ulimit-n' and restart.\n",
-                                be->id, global.maxsock);
-               }
-               else if (errno == ENOBUFS || errno == ENOMEM) {
-                       conn->err_code = CO_ER_SYS_MEMLIM;
-                       send_log(be, LOG_EMERG,
-                                "Proxy %s reached system memory limit (maxsock=%d). Please check system tunables.\n",
-                                be->id, global.maxsock);
-               }
-               else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
-                       conn->err_code = CO_ER_NOPROTO;
-               }
-               else
-                       conn->err_code = CO_ER_SOCK_ERR;
-
-               /* this is a resource error */
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_RESOURCE;
-       }
-
-       if (fd >= global.maxsock) {
-               /* do not log anything there, it's a normal condition when this option
-                * is used to serialize connections to a server !
-                */
-               ha_alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
-               close(fd);
-               conn->err_code = CO_ER_CONF_FDLIM;
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_PRXCOND; /* it is a configuration limit */
-       }
-
-       if (fd_set_nonblock(fd) == -1 ||
-           (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) == -1)) {
-               qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
-               close(fd);
-               conn->err_code = CO_ER_SOCK_ERR;
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_INTERNAL;
-       }
-
-       if (master == 1 && fd_set_cloexec(fd) == -1) {
-               ha_alert("Cannot set CLOEXEC on client socket.\n");
-               close(fd);
-               conn->err_code = CO_ER_SOCK_ERR;
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_INTERNAL;
-       }
+       /* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
+       fd = conn->handle.fd = sock_create_server_socket(conn, be);
+       if ((fd & SF_ERR_MASK) == fd)
+               return fd;
 
+       /* FD is OK, continue with protocol specific settings */
        if (be->options & PR_O_TCP_SRV_KA) {
                setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
 
index cd584aa9abb27414bb5d68100e31abc78c397c59..45953d3b1f4affe10dc81a9c3d27dcc69c0ad631 100644 (file)
@@ -239,66 +239,12 @@ static int uxst_connect_server(struct connection *conn, int flags)
                return SF_ERR_INTERNAL;
        }
 
-       fd = conn->handle.fd = sock_create_server_socket(conn);
-       if (fd == -1) {
-               qfprintf(stderr, "Cannot get a server socket.\n");
-
-               if (errno == ENFILE) {
-                       conn->err_code = CO_ER_SYS_FDLIM;
-                       send_log(be, LOG_EMERG,
-                                "Proxy %s reached system FD limit (maxsock=%d). Please check system tunables.\n",
-                                be->id, global.maxsock);
-               }
-               else if (errno == EMFILE) {
-                       conn->err_code = CO_ER_PROC_FDLIM;
-                       send_log(be, LOG_EMERG,
-                                "Proxy %s reached process FD limit (maxsock=%d). Please check 'ulimit-n' and restart.\n",
-                                be->id, global.maxsock);
-               }
-               else if (errno == ENOBUFS || errno == ENOMEM) {
-                       conn->err_code = CO_ER_SYS_MEMLIM;
-                       send_log(be, LOG_EMERG,
-                                "Proxy %s reached system memory limit (maxsock=%d). Please check system tunables.\n",
-                                be->id, global.maxsock);
-               }
-               else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
-                       conn->err_code = CO_ER_NOPROTO;
-               }
-               else
-                       conn->err_code = CO_ER_SOCK_ERR;
-
-               /* this is a resource error */
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_RESOURCE;
-       }
-
-       if (fd >= global.maxsock) {
-               /* do not log anything there, it's a normal condition when this option
-                * is used to serialize connections to a server !
-                */
-               ha_alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
-               close(fd);
-               conn->err_code = CO_ER_CONF_FDLIM;
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_PRXCOND; /* it is a configuration limit */
-       }
-
-       if (fd_set_nonblock(fd) == -1) {
-               qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
-               close(fd);
-               conn->err_code = CO_ER_SOCK_ERR;
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_INTERNAL;
-       }
-
-       if (master == 1 && fd_set_cloexec(fd) == -1) {
-               ha_alert("Cannot set CLOEXEC on client socket.\n");
-               close(fd);
-               conn->err_code = CO_ER_SOCK_ERR;
-               conn->flags |= CO_FL_ERROR;
-               return SF_ERR_INTERNAL;
-       }
+       /* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
+       fd = conn->handle.fd = sock_create_server_socket(conn, be);
+       if (fd & SF_ERR_MASK)
+               return fd;
 
+       /* FD is ok, continue with protocol specific settings */
        if (global.tune.server_sndbuf)
                 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf));
 
index 215fddb5178a91a14d3837ad16a1c371cc67ffd1..a134505918ca5fac4b59e88ddfeba21ecebb55c3 100644 (file)
@@ -195,13 +195,63 @@ struct connection *sock_accept_conn(struct listener *l, int *status)
        goto done;
 }
 
+/* Common code to handle in one place different ERRNOs, that socket() et setns()
+ * may return
+ */
+static int sock_handle_system_err(struct connection *conn, struct proxy *be)
+{
+       qfprintf(stderr, "Cannot get a server socket.\n");
+
+       conn->flags |= CO_FL_ERROR;
+       conn->err_code = CO_ER_SOCK_ERR;
+
+       switch(errno) {
+               case ENFILE:
+                       conn->err_code = CO_ER_SYS_FDLIM;
+                       send_log(be, LOG_EMERG,
+                                "Proxy %s reached system FD limit (maxsock=%d). "
+                                "Please check system tunables.\n", be->id, global.maxsock);
+
+                       return SF_ERR_RESOURCE;
+
+               case EMFILE:
+                       conn->err_code = CO_ER_PROC_FDLIM;
+                       send_log(be, LOG_EMERG,
+                                "Proxy %s reached process FD limit (maxsock=%d). "
+                                "Please check 'ulimit-n' and restart.\n", be->id, global.maxsock);
+
+                       return SF_ERR_RESOURCE;
+
+               case ENOBUFS:
+               case ENOMEM:
+                       conn->err_code = CO_ER_SYS_MEMLIM;
+                       send_log(be, LOG_EMERG,
+                                "Proxy %s reached system memory limit (maxsock=%d). "
+                                "Please check system tunables.\n", be->id, global.maxsock);
+
+                       return SF_ERR_RESOURCE;
+
+               case EAFNOSUPPORT:
+               case EPROTONOSUPPORT:
+                       conn->err_code = CO_ER_NOPROTO;
+                       break;
+
+               default:
+                       send_log(be, LOG_EMERG,
+                                "Proxy %s cannot create a server socket: %s\n",
+                                be->id, strerror(errno));
+       }
+
+       return SF_ERR_INTERNAL;
+}
+
 /* Create a socket to connect to the server in conn->dst (which MUST be valid),
  * using the configured namespace if needed, or the one passed by the proxy
  * protocol if required to do so. It then calls socket() or socketat(). On
  * success, checks if mark or tos sockopts need to be set on the file handle.
  * Returns the FD or error code.
  */
-int sock_create_server_socket(struct connection *conn)
+int sock_create_server_socket(struct connection *conn, struct proxy *be)
 {
        const struct netns_entry *ns = NULL;
        int sock_fd;
@@ -215,14 +265,52 @@ int sock_create_server_socket(struct connection *conn)
        }
 #endif
        sock_fd = my_socketat(ns, conn->dst->ss_family, SOCK_STREAM, 0);
-       if (sock_fd == -1)
-               goto end;
+
+       /* at first, handle common to all proto families system limits and permission related errors */
+       if (sock_fd == -1) {
+               return sock_handle_system_err(conn, be);
+       }
+
+       /* now perform some runtime condition checks */
+       if (sock_fd >= global.maxsock) {
+               /* do not log anything there, it's a normal condition when this option
+                * is used to serialize connections to a server !
+                */
+               ha_alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
+               send_log(be, LOG_EMERG, "socket(): not enough free sockets. Raise -n argument. Giving up.\n");
+               close(sock_fd);
+               conn->err_code = CO_ER_CONF_FDLIM;
+               conn->flags |= CO_FL_ERROR;
+
+               return SF_ERR_PRXCOND; /* it is a configuration limit */
+       }
+
+       if (fd_set_nonblock(sock_fd) == -1 ||
+               ((conn->ctrl->sock_prot == IPPROTO_TCP) && (setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) == -1))) {
+               qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
+               send_log(be, LOG_EMERG, "Cannot set client socket to non blocking mode.\n");
+               close(sock_fd);
+               conn->err_code = CO_ER_SOCK_ERR;
+               conn->flags |= CO_FL_ERROR;
+
+               return SF_ERR_INTERNAL;
+       }
+
+       if (master == 1 && fd_set_cloexec(sock_fd) == -1) {
+               ha_alert("Cannot set CLOEXEC on client socket.\n");
+               send_log(be, LOG_EMERG, "Cannot set CLOEXEC on client socket.\n");
+               close(sock_fd);
+               conn->err_code = CO_ER_SOCK_ERR;
+               conn->flags |= CO_FL_ERROR;
+
+               return SF_ERR_INTERNAL;
+       }
+
        if (conn->flags & CO_FL_OPT_MARK)
                sock_set_mark(sock_fd, conn->ctrl->fam->sock_family, conn->mark);
        if (conn->flags & CO_FL_OPT_TOS)
                sock_set_tos(sock_fd, conn->dst, conn->tos);
 
- end:
        return sock_fd;
 }