char value[0];
} __attribute__((packed));
+
+/* Termination events logs:
+ * Each event is stored on 8 bits: 4 bits bor the event location and
+ * 4 bits for the event type.
+ */
+
+/* Locations for termination event logs (4-bits). But only 7 locations are
+ * supported because 1 bit is reserved to distinguish frontend to backend
+ * events: the msb is set to 1 for backend events.
+ */
+enum term_event_loc {
+ tevt_loc_fd = 1,
+ tevt_loc_hs = 2,
+ tevt_loc_xprt = 3,
+ tevt_loc_muxc = 4,
+ tevt_loc_se = 5,
+ tevt_loc_strm = 6,
+};
+
+/* Types for termination event logs (4-bits) */
+enum term_event_type {
+ /* Events emitted by haproxy */
+ tevt_type_shutw = 1,
+ tevt_type_intercepted = 2,
+ tevt_type_tout = 3,
+
+ /* 4..9 unsued */
+
+ /* Events received by haproxy */
+ tevt_type_shutr = 10,
+ tevt_type_rcv_err = 11,
+ tevt_type_truncated_shutr = 12,
+ tevt_type_truncated_rcv_err= 13,
+ tevt_type_snd_err = 14,
+
+ /* 15 unsued */
+};
+
/* This structure describes a connection with its methods and data.
* A connection may be performed to proxy or server via a local or remote
* socket, and can also be made to an internal applet. It can support
enum obj_type *target; /* Listener for active reverse, server for passive. */
struct buffer name; /* Only used for passive reverse. Used as SNI when connection added to server idle pool. */
} reverse;
+
+ uint32_t term_evts_log; /* Termination events log: first 4 events reported from fd, handshake or xprt */
uint32_t mark; /* set network mark, if CO_FL_OPT_MARK is set */
uint8_t tos; /* set ip tos, if CO_FL_OPT_TOS is set */
};
int xprt_add_hs(struct connection *conn);
void register_mux_proto(struct mux_proto_list *list);
+static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, enum term_event_type type);
+
extern struct idle_conns idle_conns[MAX_THREADS];
/* set conn->err_code to any CO_ER_* code if it was not set yet, otherwise
if (!(c->flags & CO_FL_SOCK_RD_SH) && clean)
shutdown(c->handle.fd, SHUT_WR);
}
+ conn_report_term_evt(c, tevt_loc_fd, tevt_type_shutw);
}
static inline void conn_xprt_shutw(struct connection *c)
return mode;
}
+/* Must be used to report add an event in <_evt> termination events log.
+ * For now, it only handles 32-bits integers.
+ */
+#define tevt_report_event(_evts, loc, type) ({ \
+ \
+ if (!((_evts) & 0xff000000)) { \
+ (_evts) <<= 8; \
+ (_evts) |= (loc) << 4; \
+ (_evts) |= (type); \
+ } \
+ (_evts); \
+})
+
+/* Function to convert a termination events log to a string */
+static THREAD_LOCAL char tevt_evts_str[9];
+static inline const char *tevt_evts2str(uint32_t evts)
+{
+ uint32_t evt_msk = 0xff000000;
+ unsigned int evt_bits = 24;
+ int idx;
+
+ for (idx = 0; evt_msk; evt_msk >>= 8, evt_bits -= 8) {
+ unsigned char evt = (evts & evt_msk) >> evt_bits;
+ unsigned int is_back;
+
+ if (!evt)
+ continue;
+
+ /* Backend location are displayed in captial letter */
+ is_back = !!((evt >> 4) & 0x8);
+ switch ((enum term_event_loc)((evt >> 4) & ~0x8)) {
+ case tevt_loc_fd: tevt_evts_str[idx++] = (is_back ? 'F' : 'f'); break;
+ case tevt_loc_hs: tevt_evts_str[idx++] = (is_back ? 'H' : 'h'); break;
+ case tevt_loc_xprt: tevt_evts_str[idx++] = (is_back ? 'X' : 'x'); break;
+ case tevt_loc_muxc: tevt_evts_str[idx++] = (is_back ? 'M' : 'm'); break;
+ case tevt_loc_se: tevt_evts_str[idx++] = (is_back ? 'E' : 'e'); break;
+ case tevt_loc_strm: tevt_evts_str[idx++] = (is_back ? 'S' : 's'); break;
+ default: tevt_evts_str[idx++] = '-';
+ }
+
+ tevt_evts_str[idx++] = hextab[evt & 0xf];
+ }
+ tevt_evts_str[idx] = '\0';
+ return tevt_evts_str;
+}
+
+/* Report a connection event. <loc> may be "tevt_loc_fd", "tevt_loc_hs" or "tevt_loc_xprt" */
+static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, enum term_event_type type)
+{
+ if (conn_is_back(conn))
+ loc |= 0x08;
+ conn->term_evts_log = tevt_report_event(conn->term_evts_log, loc, type);
+}
+
#endif /* _HAPROXY_CONNECTION_H */
/*
conn->send_proxy_ofs = 0;
conn->handle.fd = DEAD_FD_MAGIC;
conn->err_code = CO_ER_NONE;
+ conn->term_evts_log = 0;
conn->target = target;
conn->destroy_cb = NULL;
conn->proxy_netns = NULL;
goto fail;
recv_abort:
+ conn_report_term_evt(conn, tevt_loc_hs, tevt_type_truncated_shutr);
conn->err_code = CO_ER_PRX_ABORT;
conn->flags |= CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
goto fail;
fail:
+ if (!(conn->flags & CO_FL_SOCK_RD_SH))
+ conn_report_term_evt(conn, tevt_loc_hs, tevt_type_truncated_rcv_err);
conn->flags |= CO_FL_ERROR;
return 0;
}
out_error:
/* Write error on the file descriptor */
+ conn_report_term_evt(conn, tevt_loc_hs, tevt_type_snd_err);
conn->flags |= CO_FL_ERROR;
return 0;
goto fail;
recv_abort:
+ conn_report_term_evt(conn, tevt_loc_hs, tevt_type_truncated_shutr);
conn->err_code = CO_ER_CIP_ABORT;
conn->flags |= CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
goto fail;
fail:
+ if (!(conn->flags & CO_FL_SOCK_RD_SH))
+ conn_report_term_evt(conn, tevt_loc_hs, tevt_type_truncated_rcv_err);
conn->flags |= CO_FL_ERROR;
return 0;
}
out_error:
/* Write error on the file descriptor */
+ conn_report_term_evt(conn, tevt_loc_hs, tevt_type_snd_err);
conn->flags |= CO_FL_ERROR;
if (conn->err_code == CO_ER_NONE) {
conn->err_code = CO_ER_SOCKS4_SEND;
return 0;
recv_abort:
+ conn_report_term_evt(conn, tevt_loc_hs, tevt_type_truncated_shutr);
if (conn->err_code == CO_ER_NONE) {
conn->err_code = CO_ER_SOCKS4_ABORT;
}
goto fail;
fail:
+ if (!(conn->flags & CO_FL_SOCK_RD_SH))
+ conn_report_term_evt(conn, tevt_loc_hs, tevt_type_truncated_rcv_err);
conn->flags |= CO_FL_ERROR;
return 0;
}
/* report error on POLL_ERR before connection establishment */
if ((fdtab[conn->handle.fd].state & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) {
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_rcv_err);
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
conn_set_errcode(conn, CO_ER_POLLERR);
errno = 0; /* let the caller do a getsockopt() if it wants it */
continue;
}
/* here we have another error */
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_rcv_err);
conn->flags |= CO_FL_ERROR;
conn_set_errno(conn, errno);
break;
return retval;
out_read0:
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_shutr);
conn_sock_read0(conn);
conn->flags &= ~CO_FL_WAIT_L4_CONN;
goto leave;
if (conn->flags & CO_FL_SOCK_WR_SH) {
/* it's already closed */
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_snd_err);
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH;
errno = EPIPE;
conn_set_errno(conn, errno);
continue;
/* here we have another error */
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_snd_err);
conn->flags |= CO_FL_ERROR;
conn_set_errno(conn, errno);
break;
/* report error on POLL_ERR before connection establishment */
if ((fdtab[conn->handle.fd].state & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) {
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_rcv_err);
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
conn_set_errcode(conn, CO_ER_POLLERR);
goto leave;
break;
}
else if (errno != EINTR) {
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_rcv_err);
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
conn_set_errno(conn, errno);
break;
return done;
read0:
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_shutr);
conn_sock_read0(conn);
conn->flags &= ~CO_FL_WAIT_L4_CONN;
* an error without checking.
*/
if (unlikely(!done && fdtab[conn->handle.fd].state & FD_POLL_ERR)) {
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_rcv_err);
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
conn_set_errcode(conn, CO_ER_POLLERR);
}
if (unlikely(fdtab[conn->handle.fd].state & FD_POLL_ERR)) {
/* an error was reported on the FD, we can't send anymore */
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_snd_err);
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
conn_set_errcode(conn, CO_ER_POLLERR);
errno = EPIPE;
if (conn->flags & CO_FL_SOCK_WR_SH) {
/* it's already closed */
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_snd_err);
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH;
errno = EPIPE;
conn_set_errno(conn, errno);
break;
}
else if (errno != EINTR) {
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_snd_err);
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
conn_set_errno(conn, errno);
break;
/* Write error on the file descriptor. Report it to the connection
* and disable polling on this FD.
*/
+ conn_report_term_evt(conn, tevt_loc_fd, tevt_type_truncated_rcv_err);
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
HA_ATOMIC_AND(&fdtab[fd].state, ~FD_LINGER_RISK);
fd_stop_both(fd);
HA_ATOMIC_INC(&counters_px->failed_handshake);
}
+ /* Report an HS error only on SSL error */
+ if (!(conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH)))
+ conn_report_term_evt(conn, tevt_loc_hs, tevt_type_truncated_rcv_err);
+
/* Fail on all other handshake errors */
conn->flags |= CO_FL_ERROR;
if (!conn->err_code)
ssl_sock_dump_errors(conn, NULL);
ERR_clear_error();
read0:
+ conn_report_term_evt(conn, tevt_loc_xprt, tevt_type_shutr);
conn_sock_read0(conn);
goto leave;
out_error:
+ conn_report_term_evt(conn, tevt_loc_xprt, tevt_type_rcv_err);
conn->flags |= CO_FL_ERROR;
/* Clear openssl global errors stack */
ssl_sock_dump_errors(conn, NULL);
ssl_sock_dump_errors(conn, NULL);
ERR_clear_error();
+ conn_report_term_evt(conn, tevt_loc_xprt, tevt_type_snd_err);
conn->flags |= CO_FL_ERROR;
goto leave;
}
if (conn->flags & (CO_FL_WAIT_XPRT | CO_FL_SSL_WAIT_HS))
return;
+ conn_report_term_evt(conn, tevt_loc_xprt, tevt_type_shutw);
if (!clean)
/* don't sent notify on SSL_shutdown */
SSL_set_quiet_shutdown(ctx->ssl, 1);