diff --git a/include/haproxy/protocol-t.h b/include/haproxy/protocol-t.h index 7c59fcac9..fde7590a4 100644 --- a/include/haproxy/protocol-t.h +++ b/include/haproxy/protocol-t.h @@ -142,6 +142,7 @@ struct protocol { /* default I/O handler */ void (*default_iocb)(int fd); /* generic I/O handler (typically accept callback) */ + int (*get_info)(struct connection *conn, long long int *info, int info_num); /* Callback to get connection level statistical counters */ uint flags; /* flags describing protocol support (PROTO_F_*) */ uint nb_receivers; /* number of receivers (under proto_lock) */ diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 63be77508..faa26de3a 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -10,6 +10,11 @@ * */ +/* this is to have tcp_info defined on systems using musl + * library, such as Alpine Linux. + */ +#define _GNU_SOURCE + #include #include #include @@ -48,6 +53,7 @@ static int tcp_suspend_receiver(struct receiver *rx); static int tcp_resume_receiver(struct receiver *rx); static void tcp_enable_listener(struct listener *listener); static void tcp_disable_listener(struct listener *listener); +static int tcp_get_info(struct connection *conn, long long int *info, int info_num); /* Note: must not be declared as its list will be overwritten */ struct protocol proto_tcpv4 = { @@ -69,6 +75,7 @@ struct protocol proto_tcpv4 = { .drain = sock_drain, .check_events = sock_check_events, .ignore_events = sock_ignore_events, + .get_info = tcp_get_info, /* binding layer */ .rx_suspend = tcp_suspend_receiver, @@ -115,6 +122,7 @@ struct protocol proto_tcpv6 = { .drain = sock_drain, .check_events = sock_check_events, .ignore_events = sock_ignore_events, + .get_info = tcp_get_info, /* binding layer */ .rx_suspend = tcp_suspend_receiver, @@ -771,6 +779,64 @@ static int tcp_resume_receiver(struct receiver *rx) return -1; } +#ifdef TCP_INFO +/* Returns some tcp_info data if it's available for connection into <*info>. + * "info_num" represents the required value. + * If the function fails it returns 0, otherwise it returns 1 and "result" is filled. + */ +static int tcp_get_info(struct connection *conn, long long int *info, int info_num) +{ + struct tcp_info tcp_info; + socklen_t optlen; + + /* The fd may not be available for the tcp_info struct, and the + syscal can fail. */ + optlen = sizeof(tcp_info); + if ((conn->flags & CO_FL_FDLESS) || + getsockopt(conn->handle.fd, IPPROTO_TCP, TCP_INFO, &tcp_info, &optlen) == -1) + return 0; + + switch (info_num) { +#if defined(__APPLE__) + case 0: *info = tcp_info.tcpi_rttcur; break; + case 1: *info = tcp_info.tcpi_rttvar; break; + case 2: *info = tcp_info.tcpi_tfo_syn_data_acked; break; + case 4: *info = tcp_info.tcpi_tfo_syn_loss; break; + case 5: *info = tcp_info.tcpi_rto; break; +#else + /* all other platforms supporting TCP_INFO have these ones */ + case 0: *info = tcp_info.tcpi_rtt; break; + case 1: *info = tcp_info.tcpi_rttvar; break; +# if defined(__linux__) + /* these ones are common to all Linux versions */ + case 2: *info = tcp_info.tcpi_unacked; break; + case 3: *info = tcp_info.tcpi_sacked; break; + case 4: *info = tcp_info.tcpi_lost; break; + case 5: *info = tcp_info.tcpi_retrans; break; + case 6: *info = tcp_info.tcpi_fackets; break; + case 7: *info = tcp_info.tcpi_reordering; break; +# elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + /* the ones are found on FreeBSD, NetBSD and OpenBSD featuring TCP_INFO */ + case 2: *info = tcp_info.__tcpi_unacked; break; + case 3: *info = tcp_info.__tcpi_sacked; break; + case 4: *info = tcp_info.__tcpi_lost; break; + case 5: *info = tcp_info.__tcpi_retrans; break; + case 6: *info = tcp_info.__tcpi_fackets; break; + case 7: *info = tcp_info.__tcpi_reordering; break; +# endif +#endif // apple + default: return 0; + } + + return 1; +} +#else +static int tcp_get_info(struct connection *conn, long long int *info, int info_num) +{ + return 0; +} +#endif /* TCP_INFO */ + /* * Local variables: diff --git a/src/tcp_sample.c b/src/tcp_sample.c index 9fbf920a2..8e4eb5feb 100644 --- a/src/tcp_sample.c +++ b/src/tcp_sample.c @@ -10,11 +10,6 @@ * */ -/* this is to have tcp_info defined on systems using musl - * library, such as Alpine Linux. - */ -#define _GNU_SOURCE - #include #include #include @@ -314,61 +309,21 @@ static inline int get_tcp_info(const struct arg *args, struct sample *smp, int dir, int val) { struct connection *conn; - struct tcp_info info; - socklen_t optlen; /* strm can be null. */ if (!smp->strm) return 0; + smp->data.type = SMP_T_SINT; /* get the object associated with the stream connector.The * object can be other thing than a connection. For example, - * it be a appctx. + * it could be an appctx. */ conn = (dir == 0 ? sc_conn(smp->strm->scf) : sc_conn(smp->strm->scb)); - if (!conn) + if (!conn || !conn->ctrl->get_info || + !conn->ctrl->get_info(conn, &smp->data.u.sint, val)) return 0; - /* The fd may not be available for the tcp_info struct, and the - syscal can fail. */ - optlen = sizeof(info); - if ((conn->flags & CO_FL_FDLESS) || - getsockopt(conn->handle.fd, IPPROTO_TCP, TCP_INFO, &info, &optlen) == -1) - return 0; - - /* extract the value. */ - smp->data.type = SMP_T_SINT; - switch (val) { -#if defined(__APPLE__) - case 0: smp->data.u.sint = info.tcpi_rttcur; break; - case 1: smp->data.u.sint = info.tcpi_rttvar; break; - case 2: smp->data.u.sint = info.tcpi_tfo_syn_data_acked; break; - case 4: smp->data.u.sint = info.tcpi_tfo_syn_loss; break; - case 5: smp->data.u.sint = info.tcpi_rto; break; -#else - /* all other platforms supporting TCP_INFO have these ones */ - case 0: smp->data.u.sint = info.tcpi_rtt; break; - case 1: smp->data.u.sint = info.tcpi_rttvar; break; -# if defined(__linux__) - /* these ones are common to all Linux versions */ - case 2: smp->data.u.sint = info.tcpi_unacked; break; - case 3: smp->data.u.sint = info.tcpi_sacked; break; - case 4: smp->data.u.sint = info.tcpi_lost; break; - case 5: smp->data.u.sint = info.tcpi_retrans; break; - case 6: smp->data.u.sint = info.tcpi_fackets; break; - case 7: smp->data.u.sint = info.tcpi_reordering; break; -# elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - /* the ones are found on FreeBSD, NetBSD and OpenBSD featuring TCP_INFO */ - case 2: smp->data.u.sint = info.__tcpi_unacked; break; - case 3: smp->data.u.sint = info.__tcpi_sacked; break; - case 4: smp->data.u.sint = info.__tcpi_lost; break; - case 5: smp->data.u.sint = info.__tcpi_retrans; break; - case 6: smp->data.u.sint = info.__tcpi_fackets; break; - case 7: smp->data.u.sint = info.__tcpi_reordering; break; -# endif -#endif // apple - default: return 0; - } return 1; }