MINOR: tcp_sample: implement the fc_saved_syn sample fetch function

This function retrieves the copy of a SYN packet that the system has
kept for us when bind option "tcp-ss" was set to 1 or above. It's
recommended to copy it to a local variable because it will be freed
after being read. It allows to inspect all parts of an incoming SYN
packet, provided that it was preserved (e.g. not possible with SYN
cookies). The doc provides examples of how to use it.
This commit is contained in:
Willy Tarreau 2025-12-24 18:37:11 +01:00
parent 52d60bf9ee
commit 6fb521d2f6
2 changed files with 104 additions and 2 deletions

View File

@ -17516,13 +17516,14 @@ tcp-ss <mode>
Sets the TCP Save SYN option for all incoming connections instantiated from
this listening socket. This option is available on Linux since version 4.3.
It instructs the kernel to try to keep a copy of the incoming IP packet
containing the TCP SYN flag, for later inspection. The option knows 3 modes:
containing the TCP SYN flag, for later inspection via the "fc_saved_syn"
sample fetch function. The option knows 3 modes:
- 0 SYN packet saving is disabled, this is the default
- 1 SYN packet saving is enabled, and contains IP and TCP headers
- 2 SYN packet saving is enabled, and contains ETH, IP and TCP headers
This only works for regular TCP connections, and is ignored for other
protocols (e.g. UNIX sockets). See also "fc.saved_syn".
protocols (e.g. UNIX sockets). See also "fc_saved_syn".
tcp-ut <delay>
Sets the TCP User Timeout for all incoming connections instantiated from this
@ -23277,6 +23278,7 @@ fc_retrans integer
fc_rtt(<unit>) integer
fc_rttvar(<unit>) integer
fc_sacked integer
fc_saved_syn binary
fc_settings_streams_limit integer
fc_src ip
fc_src_is_local boolean
@ -23875,6 +23877,78 @@ fc_sacked : integer
if the operating system does not support TCP_INFO, for example Linux kernels
before 2.4, the sample fetch fails.
fc_saved_syn : binary
Returns a copy of the saved SYN packet that was preserved by the system
during the incoming connection setup. This requires that the "tcp-ss" option
was present on the "bind" line, and a Linux kernel 4.3 minimum. When "tcp-ss"
is set to 1, only the IP and TCP headers are present. When "tcp-ss" is set to
2, then the Ethernet header is also present before the IP header, and may be
used to control or log source MAC address or VLANs for example. Note that
there is no guarantee that a SYN will be saved. For example, if SYN cookies
are used, the SYN packet is not preserved and the connection is established
on the matching ACK packet. In addition, the system doesn't guarantee to
preserve the copy beyond the first read. As such it is strongly recommended
to copy it into a variable in scope "sess" from a "tcp-request connection"
rule and only use that variable for further manipulations. It is worth noting
that on the loopback interface a dummy 14-byte ethernet header is constructed
by the system where both the source and destination addresses are zero, and
only the protocol is set. It is convenient to convert such samples to
hexadecimal using the "hex" converter during debugging. Example (fields
manually separated and commented below):
frontend test
mode http
bind :::4445 tcp-ss 2
tcp-request connection set-var(sess.syn) fc_saved_syn
http-request return status 200 content-type text/plain \
lf-string "%[var(sess.syn),hex]\n"
$ curl '0:4445'
000000000000 000000000000 0800 \ # MAC_DST MAC_SRC PROTO=IPv4
4500003C0A65400040063255 \ # IPv4 header, proto=6 (TCP)
7F000001 7F000001 \ # IP_SRC=127.0.0.1 IP_DST=127.0.0.1
E1F2 115D 01AF4E3E 00000000 \ # TCP_SPORT=57842 TCP_DPORT=4445, SEQ
A0 02 FFD7 FE300000 \ # OPT_LEN=20 TCP_FLAGS=SYN WIN=65495
0204FFD70402080A01C2A71A0000000001030307 # MSS=65495, TS, SACK, WSCALE 7
$ curl '[::1]:4445'
000000000000 000000000000 86DD \ # MAC_DST MAC_SRC PROTO=IPv6
6008018F00280640 \ # IPv6 header, proto=6 (TCP)
00000000000000000000000000000001 \ # SRC=::1
00000000000000000000000000000001 \ # DST=::1
9758 115D B5511F5D 00000000 \ # TCP_SPORT=38744 TCP_DPORT=4445, SEQ
A0 02 FFC4 00300000 \ # OPT_LEN=20 TCP_FLAGS=SYN WIN=65476
0204FFC40402080A9C231D680000000001030307 # MSS=65476, TS, SACK, WSCALE 7
The "bytes()" converter helps extract specific fields from the packet. The
be2dec() also permits to read chunks and emit them in integer form.
Example with IPv4 input:
frontend test
mode http
bind :4445 tcp-ss 2
tcp-request connection set-var(sess.syn) fc_saved_syn
http-request return status 200 content-type text/plain lf-string \
"mac_dst=%[var(sess.syn),bytes(0,6),hex] \
mac_src=%[var(sess.syn),bytes(6,6),hex] \
proto=%[var(sess.syn),bytes(12,2),hex] \
ipv4h=%[var(sess.syn),bytes(14,12),hex] \
ipv4_src=%[var(sess.syn),bytes(26,4),be2dec(.,1)] \
ipv4_dst=%[var(sess.syn),bytes(30,4),be2dec(.,1)] \
tcp_spt=%[var(sess.syn),bytes(34,2),be2dec(,2)] \
tcp_dpt=%[var(sess.syn),bytes(36,2),be2dec(,2)] \
tcp_win=%[var(sess.syn),bytes(48,2),be2dec(,2)] \
tcp_opt=%[var(sess.syn),bytes(54),hex]\n"
$ curl '0:4445'
mac_dst=000000000000 mac_src=000000000000 proto=0800 \
ipv4h=4500003CC9B7400040067302 ipv4_src=127.0.0.1 ipv4_dst=127.0.0.1 \
tcp_spt=43970 tcp_dpt=4445 tcp_win=65495 \
tcp_opt=0204FFD70402080A01DC0D410000000001030307
See also the "set-var" action, the "be2dec", "bytes" and "hex" converters.
fc_settings_streams_limit : integer
Returns the maximum number of streams allowed on the frontend connection. For
TCP and HTTP/1.1 connections, it is always 1. For other protocols, it depends

View File

@ -465,6 +465,31 @@ smp_fetch_fc_reordering(const struct arg *args, struct sample *smp, const char *
#endif
#endif // TCP_INFO
#ifdef TCP_SAVED_SYN
/* Try to retrieve the saved SYN packet header that has been enabled on a
* TCP listener via the "tcp-ss" bind option.
*/
static int
smp_fetch_fc_saved_syn(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct connection *conn = objt_conn(smp->sess->origin);
int ret;
if (!conn || !conn->ctrl->get_opt)
return 0;
chunk_reset(&trash);
ret = conn->ctrl->get_opt(conn, IPPROTO_TCP, TCP_SAVED_SYN, trash.area, trash.size);
if (ret < 0)
return 0;
trash.data = ret;
smp->data.type = SMP_T_BIN;
smp->data.u.str = trash;
return 1;
}
#endif
/* Validates the data unit argument passed to "accept_date" fetch. Argument 0 support an
* optional string representing the unit of the result: "s" for seconds, "ms" for
* milliseconds and "us" for microseconds.
@ -579,6 +604,9 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
{ "src", smp_fetch_src, 0, NULL, SMP_T_ADDR, SMP_USE_L4CLI },
{ "src_is_local", smp_fetch_src_is_local, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
{ "src_port", smp_fetch_sport, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
#ifdef TCP_SAVED_SYN
{ "fc_saved_syn", smp_fetch_fc_saved_syn, 0, NULL, SMP_T_BIN, SMP_USE_L4CLI },
#endif
#ifdef TCP_INFO
{ "fc_rtt", smp_fetch_fc_rtt, ARG1(0,STR), val_fc_time_value, SMP_T_SINT, SMP_USE_L4CLI },
{ "fc_rttvar", smp_fetch_fc_rttvar, ARG1(0,STR), val_fc_time_value, SMP_T_SINT, SMP_USE_L4CLI },