mirror of
http://git.haproxy.org/git/haproxy.git
synced 2026-02-05 05:03:44 +02:00
MINOR: net_helper: add sample converters to decode TCP headers
This adds the following converters, used to decode fields in an incoming tcp header: tcp.dst, tcp.flags, tcp.seq, tcp.src, tcp.win, tcp.options.mss, tcp.options.tsopt, tcp.options.tsval, tcp.options.wscale, tcp.options_list, These can be used with the tcp-ss bind option. The doc was updated accordingly.
This commit is contained in:
@@ -20583,6 +20583,18 @@ table_server_id([table]) any integer
|
||||
table_sess_cnt([table]) any integer
|
||||
table_sess_rate([table]) any integer
|
||||
table_trackers([table]) any integer
|
||||
tcp.dst binary integer
|
||||
tcp.flags binary integer
|
||||
tcp.options.mss binary integer
|
||||
tcp.options.sack binary integer
|
||||
tcp.options.tsopt binary integer
|
||||
tcp.options.tsval binary integer
|
||||
tcp.options.wscale binary integer
|
||||
tcp.options.wsopt binary integer
|
||||
tcp.options_list binary binary
|
||||
tcp.seq binary integer
|
||||
tcp.src binary integer
|
||||
tcp.win binary integer
|
||||
ub64dec string string
|
||||
ub64enc string string
|
||||
ungrpc(field_number[,field_type]) binary binary / int
|
||||
@@ -22359,6 +22371,88 @@ table_trackers([<table>])
|
||||
concurrent connections there are from a given address for example. See also
|
||||
the sc_trackers sample fetch keyword.
|
||||
|
||||
tcp.dst
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It returns an integer representing the destination
|
||||
port present in the TCP header. See also "fc_saved_syn", "tcp-ss", and
|
||||
"ip.data".
|
||||
|
||||
tcp.flags
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It returns an integer representing the TCP flags
|
||||
from this TCP header. All 8 flags from FIN to CWR are retrieved. Each flag
|
||||
may be tested using the "and()" converter. Please refer to RFC9293 for the
|
||||
value of each flag. See also "fc_saved_syn", "tcp-ss", and "ip.data".
|
||||
|
||||
tcp.options.mss
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It looks for a TCP option of kind "MSS", and if found,
|
||||
it returns an integer value corresponding to the advertised value in that
|
||||
option, otherwise zero. The MSS is the Maximum Segment Size and indicates the
|
||||
largest segment the peer may receive, in bytes. See also "fc_saved_syn",
|
||||
"tcp-ss", and "ip.data".
|
||||
|
||||
tcp.options.sack
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It looks for a TCP option of kind "Sack-Permitted",
|
||||
and if found, returns 1, otherwise zero. See also "fc_saved_syn", "tcp-ss",
|
||||
and "ip.data".
|
||||
|
||||
tcp.options.tsopt
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It looks for a TCP option of kind "Timestamp", and if
|
||||
found, returns 1, otherwise zero. See also "fc_saved_syn", "tcp-ss", and
|
||||
"ip.data".
|
||||
|
||||
tcp.options.tsval
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It looks for a TCP option of kind "Timestamp", and if
|
||||
found, returns the timestamp value emitted by the peer, otherwise does not
|
||||
return anything. Note that timestamps are 32-bit unsigned values with no
|
||||
particular unit that only the peer decides on, and timestamps are expected to
|
||||
be independent between different connections. See also "fc_saved_syn",
|
||||
"tcp-ss", and "ip.data".
|
||||
|
||||
tcp.options.wscale
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It looks for a TCP option of kind "Window Scale", and
|
||||
if found, returns the window scaling value emitted by the peer, otherwise
|
||||
zero. Note that values are not expected to be beyond 14 though no technical
|
||||
limitation prevents them from being sent. In order to detect if the window
|
||||
scale option was used, please use "tcp.options.wsopt". See also "tcp-ss",
|
||||
"fc_saved_syn", "ip.data", and "tcp.options.wsopt".
|
||||
|
||||
tcp.options.wsopt
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It looks for a TCP option of kind "Window Scale", and
|
||||
if found, returns 1 otherwise 0. See also "fc_saved_syn", "tcp-ss", "ip.data"
|
||||
"tcp.options.wscale".
|
||||
|
||||
tcp.options_list
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It builds a binary sequence of all TCP option kinds in
|
||||
the same order as they appear in the TCP header. It can produce from 0 to 60
|
||||
bytes (in the worst case). The End-of-options is not emitted. See also
|
||||
"fc_saved_syn", "tcp-ss", and "ip.data".
|
||||
|
||||
tcp.seq
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It returns an integer representing the sequence number
|
||||
used by the peer in the TCP header. Sequence numbers are 32-bit unsigned
|
||||
values. See also "fc_saved_syn", "tcp-ss", and "ip.data".
|
||||
|
||||
tcp.src
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It returns an integer representing the source port
|
||||
present in the TCP header. See also "fc_saved_syn", "tcp-ss", and "ip.data".
|
||||
|
||||
tcp.win
|
||||
This is used with an input sample representing a binary TCP header, as
|
||||
returned by "ip.data". It returns an integer representing the window size
|
||||
advertised by the peer in the TCP header. The value is provided as-is, as a
|
||||
16-bit unsigned quantity, without applying the window scaling factor. See
|
||||
also "fc_saved_syn", "tcp-ss", and "ip.data".
|
||||
|
||||
ub64dec
|
||||
This converter is the base64url variant of b64dec converter. base64url
|
||||
encoding is the "URL and Filename Safe Alphabet" variant of base64 encoding.
|
||||
@@ -24069,10 +24163,10 @@ fc_saved_syn : binary
|
||||
ipv4h=%[var(sess.syn),eth.data,bytes(0,12),hex] \
|
||||
ipv4_src=%[var(sess.syn),eth.data,ip.src] \
|
||||
ipv4_dst=%[var(sess.syn),eth.data,ip.dst] \
|
||||
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"
|
||||
tcp_spt=%[var(sess.syn),eth.data,ip.data,tcp.src] \
|
||||
tcp_dpt=%[var(sess.syn),eth.data,ip.data,tcp.dst] \
|
||||
tcp_win=%[var(sess.syn),eth.data,ip.data,tcp.win] \
|
||||
tcp_opt=%[var(sess.syn),eth.data,ip.data,bytes(20),hex]\n"
|
||||
|
||||
$ curl '0:4445'
|
||||
mac_dst=000000000000 mac_src=000000000000 proto=0800 \
|
||||
@@ -24080,8 +24174,8 @@ fc_saved_syn : binary
|
||||
tcp_spt=43970 tcp_dpt=4445 tcp_win=65495 \
|
||||
tcp_opt=0204FFD70402080A01DC0D410000000001030307
|
||||
|
||||
See also the "set-var" action, the "be2dec", "bytes", "hex", "eth.XXX" and
|
||||
"ip.XXX" converters.
|
||||
See also the "set-var" action, the "be2dec", "bytes", "hex", "eth.XXX",
|
||||
"ip.XXX", and "tcp.XXX" converters.
|
||||
|
||||
fc_settings_streams_limit : integer
|
||||
Returns the maximum number of streams allowed on the frontend connection. For
|
||||
|
||||
247
src/net_helper.c
247
src/net_helper.c
@@ -415,6 +415,240 @@ static int sample_conv_ip_ver(const struct arg *arg_p, struct sample *smp, void
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************/
|
||||
/* Converters used to process TCP headers */
|
||||
/******************************************/
|
||||
|
||||
/* returns the TCP header length in bytes if complete, otherwise zero */
|
||||
static int tcp_fullhdr_length(const struct sample *smp)
|
||||
{
|
||||
size_t ofs;
|
||||
|
||||
if (smp->data.u.str.data < 20)
|
||||
return 0;
|
||||
|
||||
/* check that header is complete */
|
||||
ofs = ((uchar)smp->data.u.str.area[12] >> 4) * 4;
|
||||
if (ofs < 20 || smp->data.u.str.data < ofs)
|
||||
return 0;
|
||||
return ofs;
|
||||
}
|
||||
|
||||
/* returns the offset in the input TCP header where option kind <opt> is first
|
||||
* seen, otherwise 0 if not found. NOP and END cannot be searched.
|
||||
*/
|
||||
static size_t tcp_fullhdr_find_opt(const struct sample *smp, uint8_t opt)
|
||||
{
|
||||
size_t len = tcp_fullhdr_length(smp);
|
||||
size_t next = 20, curr;
|
||||
|
||||
while ((curr = next) < len) {
|
||||
if (smp->data.u.str.area[next] == 0) // kind0=end of options
|
||||
break;
|
||||
/* kind1 = NOP and is a single byte, others have a length field */
|
||||
next += (smp->data.u.str.area[next] == 1) ? 1 : smp->data.u.str.area[next + 1];
|
||||
if (smp->data.u.str.area[curr] == opt && next <= len)
|
||||
return curr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns the destination port field found in an input TCP header */
|
||||
static int sample_conv_tcp_dst(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
if (smp->data.u.str.data < 20)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = read_n16(smp->data.u.str.area + 2);
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the flags field found in an input TCP header */
|
||||
static int sample_conv_tcp_flags(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
if (smp->data.u.str.data < 20)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = (uchar)smp->data.u.str.area[13];
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the MSS value of an input TCP header, or 0 if absent. Returns
|
||||
* nothing if the header is incomplete.
|
||||
*/
|
||||
static int sample_conv_tcp_options_mss(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
size_t len = tcp_fullhdr_length(smp);
|
||||
size_t ofs = tcp_fullhdr_find_opt(smp, 2 /* MSS */);
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = ofs ? read_n16(smp->data.u.str.area + ofs + 2) : 0;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns 1 if the SackPerm option is present in an input TCP header,
|
||||
* otherwise 0. Returns nothing if the header is incomplete.
|
||||
*/
|
||||
static int sample_conv_tcp_options_sack(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
size_t len = tcp_fullhdr_length(smp);
|
||||
size_t ofs = tcp_fullhdr_find_opt(smp, 4 /* sackperm */);
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = !!ofs;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns 1 if the TimeStamp option is present in an input TCP header,
|
||||
* otherwise 0. Returns nothing if the header is incomplete.
|
||||
*/
|
||||
static int sample_conv_tcp_options_tsopt(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
size_t len = tcp_fullhdr_length(smp);
|
||||
size_t ofs = tcp_fullhdr_find_opt(smp, 8 /* TS */);
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = !!ofs;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the TSval value in the TimeStamp option found in an input TCP
|
||||
* header, if found, otherwise 0. Returns nothing if the header is incomplete
|
||||
* (see also tsopt).
|
||||
*/
|
||||
static int sample_conv_tcp_options_tsval(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
size_t len = tcp_fullhdr_length(smp);
|
||||
size_t ofs = tcp_fullhdr_find_opt(smp, 8 /* TS */);
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = ofs ? read_n32(smp->data.u.str.area + ofs + 2) : 0;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the window scaling shift count from an input TCP header, otherwise 0
|
||||
* if option not found (see also wsopt). Returns nothing if the header is
|
||||
* incomplete.
|
||||
*/
|
||||
static int sample_conv_tcp_options_wscale(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
size_t len = tcp_fullhdr_length(smp);
|
||||
size_t ofs = tcp_fullhdr_find_opt(smp, 3 /* wscale */);
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = ofs ? (uchar)smp->data.u.str.area[ofs + 2] : 0;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns 1 if the WScale option is present in an input TCP header,
|
||||
* otherwise 0. Returns nothing if the header is incomplete.
|
||||
*/
|
||||
static int sample_conv_tcp_options_wsopt(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
size_t len = tcp_fullhdr_length(smp);
|
||||
size_t ofs = tcp_fullhdr_find_opt(smp, 3 /* wscale */);
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = !!ofs;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns only the TCP options kinds of an input TCP header, as a binary
|
||||
* block of one byte per option.
|
||||
*/
|
||||
static int sample_conv_tcp_options_list(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
struct buffer *trash = get_trash_chunk();
|
||||
size_t len = tcp_fullhdr_length(smp);
|
||||
size_t ofs = 20;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
while (ofs < len) {
|
||||
if (smp->data.u.str.area[ofs] == 0) // kind0=end of options
|
||||
break;
|
||||
trash->area[trash->data++] = smp->data.u.str.area[ofs];
|
||||
/* kind1 = NOP and is a single byte, others have a length field */
|
||||
if (smp->data.u.str.area[ofs] == 1)
|
||||
ofs++;
|
||||
else if (ofs + 1 <= len)
|
||||
ofs += smp->data.u.str.area[ofs + 1];
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* returns a binary block of 1 byte per option */
|
||||
smp->data.u.str = *trash;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the sequence number field found in an input TCP header */
|
||||
static int sample_conv_tcp_seq(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
if (smp->data.u.str.data < 20)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = read_n32(smp->data.u.str.area + 4);
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the source port field found in an input TCP header */
|
||||
static int sample_conv_tcp_src(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
if (smp->data.u.str.data < 20)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = read_n16(smp->data.u.str.area);
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the window field found in an input TCP header */
|
||||
static int sample_conv_tcp_win(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
if (smp->data.u.str.data < 20)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = read_n16(smp->data.u.str.area + 14);
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Note: must not be declared <const> as its list will be overwritten */
|
||||
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
||||
@@ -435,6 +669,19 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
||||
{ "ip.ttl", sample_conv_ip_ttl, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "ip.ver", sample_conv_ip_ver, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
|
||||
{ "tcp.dst", sample_conv_tcp_dst, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.flags", sample_conv_tcp_flags, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.options.mss", sample_conv_tcp_options_mss, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.options.sack", sample_conv_tcp_options_sack, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.options.tsopt", sample_conv_tcp_options_tsopt, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.options.tsval", sample_conv_tcp_options_tsval, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.options.wscale", sample_conv_tcp_options_wscale, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.options.wsopt", sample_conv_tcp_options_wsopt, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.options_list", sample_conv_tcp_options_list, 0, NULL, SMP_T_BIN, SMP_T_BIN },
|
||||
{ "tcp.seq", sample_conv_tcp_seq, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.src", sample_conv_tcp_src, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "tcp.win", sample_conv_tcp_win, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
|
||||
{ NULL, NULL, 0, 0, 0 },
|
||||
}};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user