summaryrefslogtreecommitdiffstats
path: root/src/sock.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 05:11:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 05:11:10 +0000
commitcff6d757e3ba609c08ef2aaa00f07e53551e5bf6 (patch)
tree08c4fc3255483ad397d712edb4214ded49149fd9 /src/sock.c
parentAdding upstream version 2.9.7. (diff)
downloadhaproxy-upstream/3.0.0.tar.xz
haproxy-upstream/3.0.0.zip
Adding upstream version 3.0.0.upstream/3.0.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/sock.c')
-rw-r--r--src/sock.c134
1 files changed, 130 insertions, 4 deletions
diff --git a/src/sock.c b/src/sock.c
index 7fcdc10..df82c6e 100644
--- a/src/sock.c
+++ b/src/sock.c
@@ -30,6 +30,7 @@
#include <haproxy/listener.h>
#include <haproxy/log.h>
#include <haproxy/namespace.h>
+#include <haproxy/protocol-t.h>
#include <haproxy/proto_sockpair.h>
#include <haproxy/sock.h>
#include <haproxy/sock_inet.h>
@@ -109,6 +110,9 @@ struct connection *sock_accept_conn(struct listener *l, int *status)
goto fail_conn;
}
+ if (unlikely(port_is_restricted(addr, HA_PROTO_TCP)))
+ goto fail_conn;
+
/* Perfect, the connection was accepted */
conn = conn_new(&l->obj_type);
if (!conn)
@@ -195,14 +199,76 @@ 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;
+
+ case EPERM:
+ conn->err_code = CO_ER_SOCK_ERR;
+ send_log(be, LOG_EMERG,
+ "Proxy %s has insufficient permissions to open server socket.\n",
+ be->id);
+
+ return SF_ERR_PRXCOND;
+
+ 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 ultimately calls socket() or socketat()
- * and returns the FD or error code.
+ * 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 backend connection socket FD on success, stream_err flag needed by
+ * upper level is set as SF_ERR_NONE; -1 on failure, stream_err is set to
+ * appropriate value.
*/
-int sock_create_server_socket(struct connection *conn)
+int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err)
{
const struct netns_entry *ns = NULL;
+ int sock_fd;
#ifdef USE_NS
if (objt_server(conn->target)) {
@@ -212,7 +278,60 @@ int sock_create_server_socket(struct connection *conn)
ns = __objt_server(conn->target)->netns;
}
#endif
- return my_socketat(ns, conn->dst->ss_family, SOCK_STREAM, 0);
+ sock_fd = my_socketat(ns, conn->dst->ss_family, SOCK_STREAM, 0);
+
+ /* at first, handle common to all proto families system limits and permission related errors */
+ if (sock_fd == -1) {
+ *stream_err = sock_handle_system_err(conn, be);
+
+ return -1;
+ }
+
+ /* 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;
+ *stream_err = SF_ERR_PRXCOND; /* it is a configuration limit */
+
+ return -1;
+ }
+
+ 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;
+ *stream_err = SF_ERR_INTERNAL;
+
+ return -1;
+ }
+
+ 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;
+ *stream_err = SF_ERR_INTERNAL;
+
+ return -1;
+ }
+
+ 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);
+
+ *stream_err = SF_ERR_NONE;
+ return sock_fd;
}
/* Enables receiving on receiver <rx> once already bound. */
@@ -807,6 +926,13 @@ int sock_conn_check(struct connection *conn)
return 0;
wait:
+ /* we may arrive here due to connect() misleadingly reporting EALREADY
+ * in some corner cases while the system disagrees and reports an error
+ * on the FD.
+ */
+ if (fdtab[fd].state & FD_POLL_ERR)
+ goto out_error;
+
fd_cant_send(fd);
fd_want_send(fd);
return 0;