Skip to content

Commit 00c383f

Browse files
committed
MINOR: connection: add more connection error codes to cover common errno
While we get reports of connection setup errors in fc_err/bc_err, we don't have the equivalent for the recv/send/splice syscalls. Let's add provisions for new codes that cover the common errno values that recv/send/splice can return, i.e. ECONNREFUSED, ENOMEM, EBADF, EFAULT, EINVAL, ENOTCONN, ENOTSOCK, ENOBUFS, EPIPE. We also add a special case for when the poller reported the error itself. It's worth noting that EBADF/EFAULT/EINVAL will generally indicate serious bugs in the code and should not be reported. The only thing is that it's quite hard to forcefully (and reliably) trigger these errors in automated tests as the timing is critical. Using iptables to manually reset established connections in the middle of large transfers at least permits to see some ECONNRESET and/or EPIPE, but the other ones are harder to trigger.
1 parent 0f1d37a commit 00c383f

File tree

4 files changed

+79
-0
lines changed

4 files changed

+79
-0
lines changed

doc/configuration.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22727,6 +22727,18 @@ fc_err_str : string
2272722727
| 42 | "SOCKS4 Proxy handshake aborted by server" |
2272822728
| 43 | "SSL fatal error" |
2272922729
| 44 | "Reverse connect failure" |
22730+
| 45 | "Poller reported POLLERR" |
22731+
| 46 | "ECONNREFUSED returned by OS" |
22732+
| 47 | "ECONNRESET returned by OS" |
22733+
| 48 | "ENETUNREACH returned by OS" |
22734+
| 49 | "ENOMEM returned by OS" |
22735+
| 50 | "EBADF returned by OS" |
22736+
| 51 | "EFAULT returned by OS" |
22737+
| 52 | "EINVAL returned by OS" |
22738+
| 53 | "ENCONN returned by OS" |
22739+
| 54 | "ENSOCK returned by OS" |
22740+
| 55 | "ENOBUFS returned by OS" |
22741+
| 56 | "EPIPE returned by OS" |
2273022742
+----+---------------------------------------------------------------------------+
2273122743

2273222744
fc_fackets : integer

include/haproxy/connection-t.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,19 @@ enum {
241241
CO_ER_SSL_FATAL, /* SSL fatal error during a SSL_read or SSL_write */
242242

243243
CO_ER_REVERSE, /* Error during reverse connect */
244+
245+
CO_ER_POLLERR, /* we only noticed POLLERR */
246+
CO_ER_EREFUSED, /* ECONNREFUSED returned to recv/send */
247+
CO_ER_ERESET, /* ECONNRESET returned to recv/send */
248+
CO_ER_EUNREACH, /* ENETUNREACH returned to recv/send */
249+
CO_ER_ENOMEM, /* ENOMEM returned to recv/send */
250+
CO_ER_EBADF, /* EBADF returned to recv/send (serious bug) */
251+
CO_ER_EFAULT, /* EFAULT returned to recv/send (serious bug) */
252+
CO_ER_EINVAL, /* EINVAL returned to recv/send (serious bug) */
253+
CO_ER_ENCONN, /* ENCONN returned to recv/send */
254+
CO_ER_ENSOCK, /* ENSOCK returned to recv/send */
255+
CO_ER_ENOBUFS, /* ENOBUFS returned to send */
256+
CO_ER_EPIPE, /* EPIPE returned to send */
244257
};
245258

246259
/* error return codes for accept_conn() */

include/haproxy/connection.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ void conn_init(struct connection *conn, void *target);
9090
struct connection *conn_new(void *target);
9191
void conn_free(struct connection *conn);
9292
void conn_release(struct connection *conn);
93+
void conn_set_errno(struct connection *conn, int err);
9394
struct conn_hash_node *conn_alloc_hash_node(struct connection *conn);
9495
struct sockaddr_storage *sockaddr_alloc(struct sockaddr_storage **sap, const struct sockaddr_storage *orig, socklen_t len);
9596
void sockaddr_free(struct sockaddr_storage **sap);
@@ -107,6 +108,15 @@ void register_mux_proto(struct mux_proto_list *list);
107108

108109
extern struct idle_conns idle_conns[MAX_THREADS];
109110

111+
/* set conn->err_code to any CO_ER_* code if it was not set yet, otherwise
112+
* does nothing.
113+
*/
114+
static inline void conn_set_errcode(struct connection *conn, int err_code)
115+
{
116+
if (!conn->err_code)
117+
conn->err_code = err_code;
118+
}
119+
110120
/* returns true if the transport layer is ready */
111121
static inline int conn_xprt_ready(const struct connection *conn)
112122
{

src/connection.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,10 +744,54 @@ const char *conn_err_code_str(struct connection *c)
744744
case CO_ER_SSL_FATAL: return "SSL fatal error";
745745

746746
case CO_ER_REVERSE: return "Reverse connect failure";
747+
748+
case CO_ER_POLLERR: return "Poller reported POLLERR";
749+
case CO_ER_EREFUSED: return "ECONNREFUSED returned by OS";
750+
case CO_ER_ERESET: return "ECONNRESET returned by OS";
751+
case CO_ER_EUNREACH: return "ENETUNREACH returned by OS";
752+
case CO_ER_ENOMEM: return "ENOMEM returned by OS";
753+
case CO_ER_EBADF: return "EBADF returned by OS";
754+
case CO_ER_EFAULT: return "EFAULT returned by OS";
755+
case CO_ER_EINVAL: return "EINVAL returned by OS";
756+
case CO_ER_ENCONN: return "ENCONN returned by OS";
757+
case CO_ER_ENSOCK: return "ENSOCK returned by OS";
758+
case CO_ER_ENOBUFS: return "ENOBUFS returned by OS";
759+
case CO_ER_EPIPE: return "EPIPE returned by OS";
747760
}
748761
return NULL;
749762
}
750763

764+
/* Try to set conn->err_code to a meaningful value based on the errno value
765+
* passed in <err>. Values of errno are meant to be set on return from recv/
766+
* send mostly, so not all of them are handled. Any existing err_code is
767+
* preserved.
768+
*/
769+
void conn_set_errno(struct connection *conn, int err)
770+
{
771+
uchar code = 0;
772+
773+
if (conn->err_code)
774+
return;
775+
776+
switch (err) {
777+
case ECONNREFUSED: code = CO_ER_EREFUSED; break;
778+
case ECONNRESET: code = CO_ER_ERESET; break;
779+
case EHOSTUNREACH: code = CO_ER_EUNREACH; break;
780+
case ENETUNREACH: code = CO_ER_EUNREACH; break;
781+
case ENOMEM: code = CO_ER_ENOMEM; break;
782+
case EBADF: code = CO_ER_EBADF; break;
783+
case EFAULT: code = CO_ER_EFAULT; break;
784+
case EINVAL: code = CO_ER_EINVAL; break;
785+
case ENOTCONN: code = CO_ER_ENCONN; break;
786+
case ENOTSOCK: code = CO_ER_ENSOCK; break;
787+
case ENOBUFS: code = CO_ER_ENOBUFS; break;
788+
case EPIPE: code = CO_ER_EPIPE; break;
789+
default: code = CO_ER_SOCK_ERR; break;
790+
}
791+
792+
conn->err_code = code;
793+
}
794+
751795
/* Send a message over an established connection. It makes use of send() and
752796
* returns the same return code and errno. If the socket layer is not ready yet
753797
* then -1 is returned and ENOTSOCK is set into errno. If the fd is not marked

0 commit comments

Comments
 (0)