From: Willy Tarreau Date: Fri, 24 Jan 2014 15:08:19 +0000 (+0100) Subject: MEDIUM: tcp: report connection error at the connection level X-Git-Tag: v1.5-dev22~53 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9ce7013429fa5e8be37bf7e450c3566184c204ad;p=thirdparty%2Fhaproxy.git MEDIUM: tcp: report connection error at the connection level Now when a connection error happens, it is reported in the connection so that upper layers know exactly what happened. This is particularly useful with health checks and resources exhaustion. --- diff --git a/src/proto_tcp.c b/src/proto_tcp.c index d01be31bd9..ab8f343bbc 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -279,6 +279,8 @@ int tcp_connect_server(struct connection *conn, int data, int delack) struct proxy *be; struct conn_src *src; + conn->flags = CO_FL_WAIT_L4_CONN; /* connection in progress */ + switch (obj_type(conn->target)) { case OBJ_TYPE_PROXY: be = objt_proxy(conn->target); @@ -289,25 +291,39 @@ int tcp_connect_server(struct connection *conn, int data, int delack) be = srv->proxy; break; default: + conn->flags |= CO_FL_ERROR; return SN_ERR_INTERNAL; } if ((fd = conn->t.sock.fd = socket(conn->addr.to.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { qfprintf(stderr, "Cannot get a server socket.\n"); - if (errno == ENFILE) + if (errno == ENFILE) { + conn->err_code = CO_ER_SYS_FDLIM; send_log(be, LOG_EMERG, "Proxy %s reached system FD limit at %d. Please check system tunables.\n", be->id, maxfd); - else if (errno == EMFILE) + } + else if (errno == EMFILE) { + conn->err_code = CO_ER_PROC_FDLIM; send_log(be, LOG_EMERG, "Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n", be->id, maxfd); - else if (errno == ENOBUFS || errno == ENOMEM) + } + else if (errno == ENOBUFS || errno == ENOMEM) { + conn->err_code = CO_ER_SYS_MEMLIM; send_log(be, LOG_EMERG, "Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n", be->id, maxfd); + } + 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 SN_ERR_RESOURCE; } @@ -317,6 +333,8 @@ int tcp_connect_server(struct connection *conn, int data, int delack) */ 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 SN_ERR_PRXCOND; /* it is a configuration limit */ } @@ -324,6 +342,8 @@ int tcp_connect_server(struct connection *conn, int data, int delack) (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 SN_ERR_INTERNAL; } @@ -382,17 +402,23 @@ int tcp_connect_server(struct connection *conn, int data, int delack) attempts--; fdinfo[fd].local_port = port_range_alloc_port(src->sport_range); - if (!fdinfo[fd].local_port) + if (!fdinfo[fd].local_port) { + conn->err_code = CO_ER_PORT_RANGE; break; + } fdinfo[fd].port_range = src->sport_range; set_host_port(&sa, fdinfo[fd].local_port); ret = tcp_bind_socket(fd, flags, &sa, &conn->addr.from); + if (ret != 0) + conn->err_code = CO_ER_CANT_BIND; } while (ret != 0); /* binding NOK */ } else { ret = tcp_bind_socket(fd, flags, &src->source_addr, &conn->addr.from); + if (ret != 0) + conn->err_code = CO_ER_CANT_BIND; } if (unlikely(ret != 0)) { @@ -413,6 +439,7 @@ int tcp_connect_server(struct connection *conn, int data, int delack) "Cannot bind to tproxy source address before connect() for backend %s.\n", be->id); } + conn->flags |= CO_FL_ERROR; return SN_ERR_RESOURCE; } } @@ -440,22 +467,29 @@ int tcp_connect_server(struct connection *conn, int data, int delack) if (errno == EAGAIN || errno == EADDRINUSE || errno == EADDRNOTAVAIL) { char *msg; - if (errno == EAGAIN || errno == EADDRNOTAVAIL) + if (errno == EAGAIN || errno == EADDRNOTAVAIL) { msg = "no free ports"; - else + conn->err_code = CO_ER_FREE_PORTS; + } + else { msg = "local address already in use"; + conn->err_code = CO_ER_ADDR_INUSE; + } qfprintf(stderr,"Connect() failed for backend %s: %s.\n", be->id, msg); port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port); fdinfo[fd].port_range = NULL; close(fd); send_log(be, LOG_ERR, "Connect() failed for backend %s: %s.\n", be->id, msg); + conn->flags |= CO_FL_ERROR; return SN_ERR_RESOURCE; } else if (errno == ETIMEDOUT) { //qfprintf(stderr,"Connect(): ETIMEDOUT"); port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port); fdinfo[fd].port_range = NULL; close(fd); + conn->err_code = CO_ER_SOCK_ERR; + conn->flags |= CO_FL_ERROR; return SN_ERR_SRVTO; } else { // (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM) @@ -463,11 +497,12 @@ int tcp_connect_server(struct connection *conn, int data, int delack) port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port); fdinfo[fd].port_range = NULL; close(fd); + conn->err_code = CO_ER_SOCK_ERR; + conn->flags |= CO_FL_ERROR; return SN_ERR_SRVCL; } } - conn->flags = CO_FL_WAIT_L4_CONN; /* connection in progress */ conn->flags |= CO_FL_ADDR_TO_SET; /* Prepare to send a few handshakes related to the on-wire protocol. */ @@ -480,6 +515,7 @@ int tcp_connect_server(struct connection *conn, int data, int delack) if (conn_xprt_init(conn) < 0) { conn_force_close(conn); + conn->flags |= CO_FL_ERROR; return SN_ERR_RESOURCE; }