mirror of
https://github.com/haproxy/haproxy.git
synced 2026-02-03 20:39:41 -05:00
MINOR: net_helper: add sample converters to decode IP packet headers
This adds a few converters that help decode parts of IP packets: - ip.data : returns the next header (typically TCP) - ip.df : returns the dont-fragment flags - ip.dst : returns the destination IPv4/v6 address - ip.hdr : returns only the IP header - ip.proto: returns the upper level protocol (udp/tcp) - ip.src : returns the source IPv4/v6 address - ip.tos : returns the TOS / TC field - ip.ttl : returns the TTL/HL value - ip.ver : returns the IP version (4 or 6) These can be used with the tcp-ss bind option. The doc was updated accordingly.
This commit is contained in:
parent
90d2f157f2
commit
e0a7a7ca43
2 changed files with 395 additions and 4 deletions
|
|
@ -20489,6 +20489,15 @@ htonl integer integer
|
|||
http_date([offset[,unit]]) integer string
|
||||
iif(true,false) boolean string
|
||||
in_table([table]) any boolean
|
||||
ip.data binary binary
|
||||
ip.df binary integer
|
||||
ip.dst binary address
|
||||
ip.hdr binary binary
|
||||
ip.proto binary integer
|
||||
ip.src binary address
|
||||
ip.tos binary integer
|
||||
ip.ttl binary integer
|
||||
ip.ver binary integer
|
||||
ipmask(mask4[,mask6]) address address
|
||||
json([input-code]) string string
|
||||
json_query(json_path[,output_type]) string _outtype_
|
||||
|
|
@ -21066,6 +21075,81 @@ in_table([<table>])
|
|||
elements (e.g. whether or not a source IP address or an Authorization header
|
||||
was already seen).
|
||||
|
||||
ip.data
|
||||
This is used with an input sample representing a binary Ethernet frame, as
|
||||
returned by "fc_saved_syn" combined with the "tcp-ss" bind option set to "1",
|
||||
or with the output of "eth.data". It skips the IP header and any optional
|
||||
options or extensions, and returns a block of binary data starting at the
|
||||
transport protocol (usually TCP or UDP). See also "fc_saved_syn", "tcp-ss",
|
||||
and "eth.data".
|
||||
|
||||
ip.df
|
||||
This is used with an input sample representing a binary Ethernet frame, as
|
||||
returned by "fc_saved_syn" combined with the "tcp-ss" bind option set to "1",
|
||||
or with the output of "eth.data". It returns integer value 1 if the DF (don't
|
||||
fragment) flag is set in the IP header, 0 otherwise. IPv6 does not have a DF
|
||||
flag, and doesn't fragment by default so it always returns 1. See also
|
||||
"fc_saved_syn", "tcp-ss", and "eth.data".
|
||||
|
||||
ip.dst
|
||||
This is used with an input sample representing a binary Ethernet frame, as
|
||||
returned by "fc_saved_syn" combined with the "tcp-ss" bind option set to "1",
|
||||
or with the output of "eth.data". It returns the IPv4 or IPv6 destination
|
||||
address from the IPv4/v6 header. See also "fc_saved_syn", "tcp-ss", and
|
||||
"eth.data".
|
||||
|
||||
ip.hdr
|
||||
This is used with an input sample representing a binary Ethernet frame, as
|
||||
returned by "fc_saved_syn" combined with the "tcp-ss" bind option set to "1",
|
||||
or with the output of "eth.data". It returns a block of binary data starting
|
||||
with the IP header and stopping after the last option or extension, and
|
||||
before the transport protocol header. See also "fc_saved_syn", "tcp-ss", and
|
||||
"eth.data".
|
||||
|
||||
ip.proto
|
||||
This is used with an input sample representing a binary Ethernet frame, as
|
||||
returned by "fc_saved_syn" combined with the "tcp-ss" bind option set to "1",
|
||||
or with the output of "eth.data". It returns the transport protocol number,
|
||||
usually 6 for TCP or 17 for UDP. See also "fc_saved_syn", "tcp-ss", and
|
||||
"eth.data".
|
||||
|
||||
ip.src
|
||||
This is used with an input sample representing a binary Ethernet frame, as
|
||||
returned by "fc_saved_syn" combined with the "tcp-ss" bind option set to "1",
|
||||
or with the output of "eth.data". It returns the IPv4 or IPv6 source address
|
||||
from the IPv4/v6 header. See also "fc_saved_syn", "tcp-ss", and "eth.data".
|
||||
|
||||
ip.tos
|
||||
This is used with an input sample representing a binary Ethernet frame, as
|
||||
returned by "fc_saved_syn" combined with the "tcp-ss" bind option set to "1",
|
||||
or with the output of "eth.data". It returns an integer corresponding to the
|
||||
value of the type-of-service (TOS) field in the IPv4 header or traffic class
|
||||
(TC) field in the IPv6 header. Note that in the modern internet, this field
|
||||
most often contains a DSCP (Differentiated Services Codepoint) value in the
|
||||
6 upper bits and the two lower are either not used, or used by IP ECN. Please
|
||||
refer to RFC2474 and RFC8436 for DSCP values, and RFC3168 for IP ECN fields.
|
||||
See also "fc_saved_syn", "tcp-ss", and "eth.data".
|
||||
|
||||
ip.ttl
|
||||
This is used with an input sample representing a binary Ethernet frame, as
|
||||
returned by "fc_saved_syn" combined with the "tcp-ss" bind option set to "1",
|
||||
or with the output of "eth.data". This returns an integer corresponding to
|
||||
the TTL (Time To Live) or HL (Hop Limit) field in the IPv4/IPv6 header. This
|
||||
value is usually preset to a fixed value and decremented by each router that
|
||||
the packet crosses. It can help infer how far a client connects from when the
|
||||
initial value is known. Note that most modern operating systems start with an
|
||||
initial value of 64. See also "fc_saved_syn", "tcp-ss", and "eth.data".
|
||||
|
||||
ip.ver
|
||||
This is used with an input sample representing a binary Ethernet frame, as
|
||||
returned by "fc_saved_syn" combined with the "tcp-ss" bind option set to "1",
|
||||
or with the output of "eth.data". This returns the IP version from the IP
|
||||
header, normally either 4 or 6. Note that this doesn't check whether the
|
||||
protocol number in the upper layer Ethernet frame matches, but since this is
|
||||
expected to be used with valid packets, it is expected that the operating
|
||||
system has already verified this. See also "fc_saved_syn", "tcp-ss", and
|
||||
"eth.data".
|
||||
|
||||
ipmask(<mask4>[,<mask6>])
|
||||
Apply a mask to an IP address, and use the result for lookups and storage.
|
||||
This can be used to make all hosts within a certain mask to share the same
|
||||
|
|
@ -23983,8 +24067,8 @@ fc_saved_syn : binary
|
|||
mac_src=%[var(sess.syn),eth.src,hex] \
|
||||
proto=%[var(sess.syn),eth.proto,bytes(6),be2hex(,2)] \
|
||||
ipv4h=%[var(sess.syn),eth.data,bytes(0,12),hex] \
|
||||
ipv4_src=%[var(sess.syn),bytes(26,4),be2dec(.,1)] \
|
||||
ipv4_dst=%[var(sess.syn),bytes(30,4),be2dec(.,1)] \
|
||||
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)] \
|
||||
|
|
@ -23996,8 +24080,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", and
|
||||
"eth.XXX" converters.
|
||||
See also the "set-var" action, the "be2dec", "bytes", "hex", "eth.XXX" and
|
||||
"ip.XXX" converters.
|
||||
|
||||
fc_settings_streams_limit : integer
|
||||
Returns the maximum number of streams allowed on the frontend connection. For
|
||||
|
|
|
|||
307
src/net_helper.c
307
src/net_helper.c
|
|
@ -119,6 +119,303 @@ static int sample_conv_eth_vlan(const struct arg *arg_p, struct sample *smp, voi
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************/
|
||||
/* Converters used to process IPv4/IPv6 packet headers */
|
||||
/*******************************************************/
|
||||
|
||||
/* returns the total header length for the input IP packet header (v4 or v6),
|
||||
* including all extensions if any. It corresponds to the length to skip to
|
||||
* find the TCP or UDP header. If data are missing or unparsable, it returns
|
||||
* 0.
|
||||
*/
|
||||
static size_t ip_header_length(const struct sample *smp)
|
||||
{
|
||||
size_t len;
|
||||
uchar next;
|
||||
uchar ver;
|
||||
|
||||
if (smp->data.u.str.data < 1)
|
||||
return 0;
|
||||
|
||||
ver = (uchar)smp->data.u.str.area[0] >> 4;
|
||||
if (ver == 4) {
|
||||
len = (smp->data.u.str.area[0] & 0xF) * 4;
|
||||
if (smp->data.u.str.data < len)
|
||||
return 0;
|
||||
}
|
||||
else if (ver == 6) {
|
||||
if (smp->data.u.str.data < 40)
|
||||
return 0;
|
||||
|
||||
len = 40;
|
||||
next = smp->data.u.str.area[6];
|
||||
|
||||
while (next != 6 && next != 17) {
|
||||
if (smp->data.u.str.data < len + 2)
|
||||
return 0;
|
||||
next = smp->data.u.str.area[len];
|
||||
len += (uchar)smp->data.u.str.area[len + 1] * 8 + 8;
|
||||
}
|
||||
|
||||
if (smp->data.u.str.data < len)
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* returns the payload following the input IP packet header (v4 or v6) skipping
|
||||
* all extensions if any. For IPv6, it returns the TCP or UDP next header.
|
||||
*/
|
||||
static int sample_conv_ip_data(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = ip_header_length(smp);
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
/* advance buffer by <len> */
|
||||
smp->data.u.str.area += len;
|
||||
smp->data.u.str.data -= len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the DF (don't fragment) flag from an IPv4 header, as 0 or 1. The
|
||||
* value is always one for IPv6 since DF is implicit.
|
||||
*/
|
||||
static int sample_conv_ip_df(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
uchar ver;
|
||||
uchar df;
|
||||
|
||||
if (smp->data.u.str.data < 1)
|
||||
return 0;
|
||||
|
||||
ver = (uchar)smp->data.u.str.area[0] >> 4;
|
||||
if (ver == 4) {
|
||||
if (smp->data.u.str.data < 6)
|
||||
return 0;
|
||||
df = !!(smp->data.u.str.area[6] & 0x40);
|
||||
}
|
||||
else if (ver == 6) {
|
||||
df = 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
smp->data.u.sint = df;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the IP DST address found in an input IP packet header (v4 or v6). */
|
||||
static int sample_conv_ip_dst(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
uchar ver;
|
||||
|
||||
if (smp->data.u.str.data < 1)
|
||||
return 0;
|
||||
|
||||
ver = (uchar)smp->data.u.str.area[0] >> 4;
|
||||
if (ver == 4) {
|
||||
if (smp->data.u.str.data < 20)
|
||||
return 0;
|
||||
|
||||
smp->data.u.ipv4.s_addr = read_u32(smp->data.u.str.area + 16);
|
||||
smp->data.type = SMP_T_IPV4;
|
||||
}
|
||||
else if (ver == 6) {
|
||||
if (smp->data.u.str.data < 40)
|
||||
return 0;
|
||||
|
||||
memcpy(&smp->data.u.ipv6, smp->data.u.str.area + 24, 16);
|
||||
smp->data.type = SMP_T_IPV6;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the IP header only for an input IP packet header (v4 or v6), including
|
||||
* all extensions if any. For IPv6, it includes every extension before TCP/UDP.
|
||||
*/
|
||||
static int sample_conv_ip_hdr(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = ip_header_length(smp);
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
/* truncate buffer to <len> */
|
||||
smp->data.u.str.data = len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the upper layer protocol number (TCP/UDP) for an input IP packet
|
||||
* header (v4 or v6).
|
||||
*/
|
||||
static int sample_conv_ip_proto(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
size_t len;
|
||||
uchar next;
|
||||
uchar ver;
|
||||
|
||||
if (smp->data.u.str.data < 1)
|
||||
return 0;
|
||||
|
||||
ver = (uchar)smp->data.u.str.area[0] >> 4;
|
||||
if (ver == 4) {
|
||||
if (smp->data.u.str.data < 10)
|
||||
return 0;
|
||||
next = smp->data.u.str.area[9];
|
||||
}
|
||||
else if (ver == 6) {
|
||||
/* skip all extensions */
|
||||
if (smp->data.u.str.data < 40)
|
||||
return 0;
|
||||
|
||||
len = 40;
|
||||
next = smp->data.u.str.area[6];
|
||||
|
||||
while (next != 6 && next != 17) {
|
||||
if (smp->data.u.str.data < len + 2)
|
||||
return 0;
|
||||
next = smp->data.u.str.area[len];
|
||||
len += (uchar)smp->data.u.str.area[len + 1] * 8 + 8;
|
||||
}
|
||||
|
||||
if (smp->data.u.str.data < len)
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* protocol number is in <next> */
|
||||
smp->data.u.sint = next;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the IP SRC address found in an input IP packet header (v4 or v6). */
|
||||
static int sample_conv_ip_src(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
uchar ver;
|
||||
|
||||
if (smp->data.u.str.data < 1)
|
||||
return 0;
|
||||
|
||||
ver = (uchar)smp->data.u.str.area[0] >> 4;
|
||||
if (ver == 4) {
|
||||
if (smp->data.u.str.data < 20)
|
||||
return 0;
|
||||
|
||||
smp->data.u.ipv4.s_addr = read_u32(smp->data.u.str.area + 12);
|
||||
smp->data.type = SMP_T_IPV4;
|
||||
}
|
||||
else if (ver == 6) {
|
||||
if (smp->data.u.str.data < 40)
|
||||
return 0;
|
||||
|
||||
memcpy(&smp->data.u.ipv6, smp->data.u.str.area + 8, 16);
|
||||
smp->data.type = SMP_T_IPV6;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the IP TOS/TC field found in an input IP packet header (v4 or v6). */
|
||||
static int sample_conv_ip_tos(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
uchar ver;
|
||||
|
||||
if (smp->data.u.str.data < 1)
|
||||
return 0;
|
||||
|
||||
ver = (uchar)smp->data.u.str.area[0] >> 4;
|
||||
if (ver == 4) {
|
||||
/* TOS field is at offset 1 */
|
||||
if (smp->data.u.str.data < 2)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = (uchar)smp->data.u.str.area[1];
|
||||
}
|
||||
else if (ver == 6) {
|
||||
/* TOS field is between offset 0 and 1 */
|
||||
if (smp->data.u.str.data < 2)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = (uchar)(read_n16(smp->data.u.str.area) >> 4);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OK we have the value in data.u.sint */
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the IP TTL/HL field found in an input IP packet header (v4 or v6). */
|
||||
static int sample_conv_ip_ttl(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
uchar ver;
|
||||
|
||||
if (smp->data.u.str.data < 1)
|
||||
return 0;
|
||||
|
||||
ver = (uchar)smp->data.u.str.area[0] >> 4;
|
||||
if (ver == 4) {
|
||||
if (smp->data.u.str.data < 20)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = (uchar)smp->data.u.str.area[8];
|
||||
}
|
||||
else if (ver == 6) {
|
||||
if (smp->data.u.str.data < 40)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = (uchar)smp->data.u.str.area[7];
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OK we have the value in data.u.sint */
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns the IP version found in an input IP packet header (v4 or v6). */
|
||||
static int sample_conv_ip_ver(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
if (smp->data.u.str.data < 1)
|
||||
return 0;
|
||||
|
||||
smp->data.u.sint = (uchar)smp->data.u.str.area[0] >> 4;
|
||||
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, {
|
||||
{ "eth.data", sample_conv_eth_data, 0, NULL, SMP_T_BIN, SMP_T_BIN },
|
||||
|
|
@ -128,6 +425,16 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
|||
{ "eth.src", sample_conv_eth_src, 0, NULL, SMP_T_BIN, SMP_T_BIN },
|
||||
{ "eth.vlan", sample_conv_eth_vlan, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
|
||||
{ "ip.data", sample_conv_ip_data, 0, NULL, SMP_T_BIN, SMP_T_BIN },
|
||||
{ "ip.df", sample_conv_ip_df, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "ip.dst", sample_conv_ip_dst, 0, NULL, SMP_T_BIN, SMP_T_ADDR },
|
||||
{ "ip.hdr", sample_conv_ip_hdr, 0, NULL, SMP_T_BIN, SMP_T_BIN },
|
||||
{ "ip.proto", sample_conv_ip_proto, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "ip.src", sample_conv_ip_src, 0, NULL, SMP_T_BIN, SMP_T_ADDR },
|
||||
{ "ip.tos", sample_conv_ip_tos, 0, NULL, SMP_T_BIN, SMP_T_SINT },
|
||||
{ "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 },
|
||||
|
||||
{ NULL, NULL, 0, 0, 0 },
|
||||
}};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue