diff options
Diffstat (limited to 'src/sock.c')
-rw-r--r-- | src/sock.c | 134 |
1 files changed, 130 insertions, 4 deletions
@@ -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; |