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 d01be31..ab8f343 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -279,6 +279,8 @@
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 @@
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 @@
*/
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 @@
(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 @@
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 @@
"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 @@
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 @@
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 @@
if (conn_xprt_init(conn) < 0) {
conn_force_close(conn);
+ conn->flags |= CO_FL_ERROR;
return SN_ERR_RESOURCE;
}