[MEDIUM] config: replace 'tcp-request <action>' with "tcp-request connection"

It began to be problematic to have "tcp-request" followed by an
immediate action, as sometimes it was a keyword indicating a hook
or setting ("content" or "inspect-delay") and sometimes it was an
action.

Now the prefix for connection-level tcp-requests is "tcp-request connection"
and the ones processing contents remain "tcp-request contents".

This has allowed a nice simplification of the config parser and to
clean up the doc a bit. Also now it's a bit more clear why tcp-request
connection are not allowed in backends.
This commit is contained in:
Willy Tarreau 2010-08-06 15:08:45 +02:00
parent f6efda1189
commit 68c03aba9e
2 changed files with 204 additions and 244 deletions

View File

@ -962,11 +962,12 @@ stick match - - X X
stick on - - X X
stick store-request - - X X
stick-table - - X X
tcp-request accept - X X -
tcp-request connection accept - X X -
tcp-request connection reject - X X -
tcp-request connection track-counters - X X -
tcp-request content accept - X X -
tcp-request content reject - X X -
tcp-request inspect-delay - X X -
tcp-request reject - X X -
timeout check X - X X
timeout client X X X -
timeout clitimeout (deprecated) X X X -
@ -5251,7 +5252,7 @@ stick-table type {ip | integer | string [len <length>] } size <size>
about time format and section 7 avoud ACLs.
tcp-request accept [{if | unless} <condition>]
tcp-request connection accept [{if | unless} <condition>]
Accept an incoming connection if/unless a layer 4 condition is matched
May be used in sections : defaults | frontend | listen | backend
no | yes | yes | no
@ -5280,7 +5281,108 @@ tcp-request accept [{if | unless} <condition>]
See section 7 about ACL usage.
See also : "tcp-request reject" and "tcp-request content"
See also : "tcp-request connection reject" and "tcp-request content"
tcp-request connection reject [{if | unless} <condition>]
Reject an incoming connection if/unless a layer 4 condition is matched
May be used in sections : defaults | frontend | listen | backend
no | yes | yes | no
Immediately after acceptance of a new incoming connection, it is possible to
evaluate some conditions to decide whether this connection must be accepted
or dropped. Those conditions cannot make use of any data contents because the
connection has not been read from yet, and the buffers are not yet allocated.
This can be used to selectively and very quickly accept or drop connections
from various sources with a very low overhead. If some contents need to be
inspected in order to take the decision, the "tcp-request content" statements
must be used instead.
This statement rejects the connection if the condition is true (when used
with "if") or false (when used with "unless"). It is important to understand
that "accept" and "reject" rules are evaluated in their exact declaration
order, so that it is possible to build complex rules from them. There is no
specific limit to the number of rules which may be inserted.
Note that the "if/unless" condition is optional. If no condition is set on
the action, it is simply performed unconditionally.
If no "tcp-request" rules are matched, the default action is to accept the
connection, which implies that the "tcp-request accept" statement will only
make sense when combined with another "tcp-request reject" statement.
Rejected connections do not even become a session, which is why they are
accounted separately for in the stats, as "denied connections". They are not
considered for the session rate-limit and are not logged either. The reason
is that these rules should only be used to filter extremely high connection
rates such as the ones encountered during a massive DDoS attack. Under these
conditions, the simple action of logging each event would make the system
collapse and would considerably lower the filtering capacity. If logging is
absolutely desired, then "tcp-request content" rules should be used instead.
See section 7 about ACL usage.
See also : "tcp-request connection accept" and "tcp-request content"
tcp-request connection track-counters <key> [table <table>]
[{if | unless} <condition>]
Enable tracking of session counters if/unless a layer 4 condition is matched
May be used in sections : defaults | frontend | listen | backend
no | yes | yes | no
Arguments :
<key> is the criterion the tracking key will be derived from. At the
moment, only "src" is supported. With it, the key will be the
connection's source IPv4 address.
<table> is an optional table to use instead of the one from the current
proxy. All the counters for the matches and updates for the key
will then be performed in that table.
Immediately after a new incoming connection has been accepted, it is possible
to enable tracking of some of this session's counters in a table. Doing so
serves two purposes :
- feed the entry with the session's counters that are relevant to the table
being pointed. These counters are then updated as often as possible, and
also systematically when the session ends.
- keep a pointer to the entry in the table in the session to avoid having
to perform key lookups when complex ACL rules make use of the entry,
especially when the key is expensive to compute (eg: header-based).
It is possible to evaluate some conditions to decide whether a track-counters
statement will apply or not. In this case, only the first matching rule will
apply and the other ones will be ignored. We could for instance imagine that
some hosts which are references in a white list make use of a different
counters table, or do not get accounted for. The tracking is enabled if the
condition is true (when used with "if") or false (when used with "unless").
There is no specific limit to the number of rules which may be declared.
It is important to understand that "accept", "reject" and "track-counters"
rules are evaluated in their exact declaration order, so that it is possible
to build complex rules from them. For instance, the following rule rejects
too fast connections without tracking them, to that they get accepted again
after some time despite activity, while the second one will still update the
counters when rejecting a connection.
Example:
# reject too fast connection without counting them
tcp-request connection reject if { src_conn_rate gt 10 }
tcp-request connection track-counters src
# reject too fast connection and count them
tcp-request connection track-counters src
tcp-request connection reject if { src_conn_rate gt 10 }
Note that the "if/unless" condition is optional. If no condition is set on
the action, it is simply performed unconditionally.
See section 7 about ACL usage.
See also : "tcp-request connection accept", "tcp-request connection reject",
"tcp-request content", and "stick-table".
tcp-request content accept [{if | unless} <condition>]
@ -5337,12 +5439,12 @@ tcp-request content reject [{if | unless} <condition>]
# reject SMTP connection if client speaks first
tcp-request inspect-delay 30s
acl content_present req_len gt 0
tcp-request reject if content_present
tcp-request content reject if content_present
# Forward HTTPS connection only if client speaks
tcp-request inspect-delay 30s
acl content_present req_len gt 0
tcp-request accept if content_present
tcp-request content accept if content_present
tcp-request reject
See section 7 about ACL usage.
@ -5395,106 +5497,6 @@ tcp-request inspect-delay <timeout>
"timeout client".
tcp-request reject [{if | unless} <condition>]
Reject an incoming connection if/unless a layer 4 condition is matched
May be used in sections : defaults | frontend | listen | backend
no | yes | yes | no
Immediately after acceptance of a new incoming connection, it is possible to
evaluate some conditions to decide whether this connection must be accepted
or dropped. Those conditions cannot make use of any data contents because the
connection has not been read from yet, and the buffers are not yet allocated.
This can be used to selectively and very quickly accept or drop connections
from various sources with a very low overhead. If some contents need to be
inspected in order to take the decision, the "tcp-request content" statements
must be used instead.
This statement rejects the connection if the condition is true (when used
with "if") or false (when used with "unless"). It is important to understand
that "accept" and "reject" rules are evaluated in their exact declaration
order, so that it is possible to build complex rules from them. There is no
specific limit to the number of rules which may be inserted.
Note that the "if/unless" condition is optional. If no condition is set on
the action, it is simply performed unconditionally.
If no "tcp-request" rules are matched, the default action is to accept the
connection, which implies that the "tcp-request accept" statement will only
make sense when combined with another "tcp-request reject" statement.
Rejected connections do not even become a session, which is why they are
accounted separately for in the stats, as "denied connections". They are not
considered for the session rate-limit and are not logged either. The reason
is that these rules should only be used to filter extremely high connection
rates such as the ones encountered during a massive DDoS attack. Under these
conditions, the simple action of logging each event would make the system
collapse and would considerably lower the filtering capacity. If logging is
absolutely desired, then "tcp-request content" rules should be used instead.
See section 7 about ACL usage.
See also : "tcp-request accept" and "tcp-request content"
tcp-request track-counters <key> [table <table>] [{if | unless} <condition>]
Enable tracking of session counters if/unless a layer 4 condition is matched
May be used in sections : defaults | frontend | listen | backend
no | yes | yes | no
Arguments :
<key> is the criterion the tracking key will be derived from. At the
moment, only "src" is supported. With it, the key will be the
connection's source IPv4 address.
<table> is an optional table to use instead of the one from the current
proxy. All the counters for the matches and updates for the key
will then be performed in that table.
Immediately after a new incoming connection has been accepted, it is possible
to enable tracking of some of this session's counters in a table. Doing so
serves two purposes :
- feed the entry with the session's counters that are relevant to the table
being pointed. These counters are then updated as often as possible, and
also systematically when the session ends.
- keep a pointer to the entry in the table in the session to avoid having
to perform key lookups when complex ACL rules make use of the entry,
especially when the key is expensive to compute (eg: header-based).
It is possible to evaluate some conditions to decide whether a track-counters
statement will apply or not. In this case, only the first matching rule will
apply and the other ones will be ignored. We could for instance imagine that
some hosts which are references in a white list make use of a different
counters table, or do not get accounted for. The tracking is enabled if the
condition is true (when used with "if") or false (when used with "unless").
There is no specific limit to the number of rules which may be declared.
It is important to understand that "accept", "reject" and "track-counters"
rules are evaluated in their exact declaration order, so that it is possible
to build complex rules from them. For instance, the following rule rejects
too fast connections without tracking them, to that they get accepted again
after some time despite activity, while the second one will still update the
counters when rejecting a connection.
Example:
# reject too fast connection without counting them
tcp-request reject if { src_conn_rate gt 10 }
tcp-request track-counters src
# reject too fast connection and count them
tcp-request track-counters src
tcp-request reject if { src_conn_rate gt 10 }
Note that the "if/unless" condition is optional. If no condition is set on
the action, it is simply performed unconditionally.
See section 7 about ACL usage.
See also : "tcp-request accept", "tcp-request reject", "tcp-request content",
and "stick-table".
timeout check <timeout>
Set additional check timeout, but only after a connection has been already
established.

View File

@ -799,6 +799,74 @@ int tcp_exec_req_rules(struct session *s)
return result;
}
/* Parse a tcp-request rule. Return a negative value in case of failure */
static int tcp_parse_request_rule(char **args, int arg, int section_type,
struct proxy *curpx, struct proxy *defpx,
struct tcp_rule *rule, char *err, int errlen)
{
if (curpx == defpx) {
snprintf(err, errlen, "%s %s is not allowed in 'defaults' sections",
args[0], args[1]);
return -1;
}
if (!strcmp(args[arg], "accept")) {
arg++;
rule->action = TCP_ACT_ACCEPT;
}
else if (!strcmp(args[arg], "reject")) {
arg++;
rule->action = TCP_ACT_REJECT;
}
else if (strcmp(args[arg], "track-fe-counters") == 0) {
int ret;
arg++;
ret = parse_track_counters(args, &arg, section_type, curpx,
&rule->act_prm.trk_ctr, defpx, err, errlen);
if (ret < 0) /* nb: warnings are not handled yet */
return -1;
rule->action = TCP_ACT_TRK_FE_CTR;
}
else if (strcmp(args[arg], "track-be-counters") == 0) {
int ret;
arg++;
ret = parse_track_counters(args, &arg, section_type, curpx,
&rule->act_prm.trk_ctr, defpx, err, errlen);
if (ret < 0) /* nb: warnings are not handled yet */
return -1;
rule->action = TCP_ACT_TRK_BE_CTR;
}
else {
snprintf(err, errlen,
"'%s %s' expects 'accept', 'reject', 'track-fe-counters' "
"or 'track-be-counters' in %s '%s' (was '%s')",
args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]);
return -1;
}
if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
if ((rule->cond = build_acl_cond(NULL, 0, curpx, (const char **)args+arg)) == NULL) {
snprintf(err, errlen,
"error detected in %s '%s' while parsing '%s' condition",
proxy_type_str(curpx), curpx->id, args[arg]);
return -1;
}
}
else if (*args[arg]) {
snprintf(err, errlen,
"'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (was '%s')",
args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
return -1;
}
return 0;
}
/* This function should be called to parse a line starting with the "tcp-request"
* keyword.
*/
@ -809,7 +877,6 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
unsigned int val;
int retlen;
int warn = 0;
int pol = ACL_COND_NONE;
int arg;
struct tcp_rule *rule;
@ -848,70 +915,10 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
rule = (struct tcp_rule *)calloc(1, sizeof(*rule));
arg = 1;
if (!strcmp(args[1], "content")) {
if (strcmp(args[1], "content") == 0) {
arg++;
if (curpx == defpx) {
snprintf(err, errlen, "%s %s is not allowed in 'defaults' sections",
args[0], args[1]);
if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, errlen) < 0)
goto error;
}
if (!strcmp(args[2], "accept")) {
arg++;
rule->action = TCP_ACT_ACCEPT;
}
else if (!strcmp(args[2], "reject")) {
arg++;
rule->action = TCP_ACT_REJECT;
}
else if (strcmp(args[2], "track-fe-counters") == 0) {
int ret;
arg++;
ret = parse_track_counters(args, &arg, section_type, curpx,
&rule->act_prm.trk_ctr, defpx, err, errlen);
if (ret < 0) /* nb: warnings are not handled yet */
goto error;
rule->action = TCP_ACT_TRK_FE_CTR;
}
else if (strcmp(args[2], "track-be-counters") == 0) {
int ret;
arg++;
ret = parse_track_counters(args, &arg, section_type, curpx,
&rule->act_prm.trk_ctr, defpx, err, errlen);
if (ret < 0) /* nb: warnings are not handled yet */
goto error;
rule->action = TCP_ACT_TRK_BE_CTR;
}
else {
retlen = snprintf(err, errlen,
"'%s %s' expects 'accept', 'reject', 'track-fe-counters' or 'track-be-counters' in %s '%s' (was '%s')",
args[0], args[1], proxy_type_str(curpx), curpx->id, args[2]);
goto error;
}
pol = ACL_COND_NONE;
rule->cond = NULL;
if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
if ((rule->cond = build_acl_cond(NULL, 0, curpx, (const char **)args+arg)) == NULL) {
retlen = snprintf(err, errlen,
"error detected in %s '%s' while parsing '%s' condition",
proxy_type_str(curpx), curpx->id, args[arg]);
goto error;
}
}
else if (*args[arg]) {
retlen = snprintf(err, errlen,
"'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (was '%s')",
args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
goto error;
}
if (rule->cond && (rule->cond->requires & ACL_USE_RTR_ANY)) {
struct acl *acl;
@ -925,96 +932,47 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
name);
warn++;
}
LIST_INIT(&rule->list);
LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
return warn;
}
/* OK so we're in front of plain L4 rules */
if (strcmp(args[1], "accept") == 0) {
else if (strcmp(args[1], "connection") == 0) {
arg++;
rule->action = TCP_ACT_ACCEPT;
}
else if (strcmp(args[1], "reject") == 0) {
arg++;
rule->action = TCP_ACT_REJECT;
}
else if (strcmp(args[1], "track-fe-counters") == 0) {
int ret;
arg++;
ret = parse_track_counters(args, &arg, section_type, curpx,
&rule->act_prm.trk_ctr, defpx, err, errlen);
if (!(curpx->cap & PR_CAP_FE)) {
snprintf(err, errlen, "%s %s is not allowed because %s %s is not a frontend",
args[0], args[1], proxy_type_str(curpx), curpx->id);
return -1;
}
if (ret < 0) /* nb: warnings are not handled yet */
if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, errlen) < 0)
goto error;
rule->action = TCP_ACT_TRK_FE_CTR;
}
else if (strcmp(args[1], "track-be-counters") == 0) {
int ret;
if (rule->cond && (rule->cond->requires & (ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY))) {
struct acl *acl;
const char *name;
arg++;
ret = parse_track_counters(args, &arg, section_type, curpx,
&rule->act_prm.trk_ctr, defpx, err, errlen);
acl = cond_find_require(rule->cond, ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY);
name = acl ? acl->name : "(unknown)";
if (ret < 0) /* nb: warnings are not handled yet */
goto error;
rule->action = TCP_ACT_TRK_BE_CTR;
if (acl->requires & (ACL_USE_L6_ANY|ACL_USE_L7_ANY)) {
retlen = snprintf(err, errlen,
"'%s %s' may not reference acl '%s' which makes use of "
"payload in %s '%s'. Please use '%s content' for this.",
args[0], args[1], name, proxy_type_str(curpx), curpx->id, args[0]);
goto error;
}
if (acl->requires & ACL_USE_RTR_ANY)
retlen = snprintf(err, errlen,
"acl '%s' involves some response-only criteria which will be ignored.",
name);
warn++;
}
}
else {
retlen = snprintf(err, errlen,
"'%s' expects 'inspect-delay', 'content', 'accept', 'reject', 'track-fe-counters' or 'track-be-counters' in %s '%s' (was '%s')",
"'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (was '%s')",
args[0], proxy_type_str(curpx), curpx->id, args[1]);
goto error;
}
if (curpx == defpx) {
snprintf(err, errlen, "%s %s is not allowed in 'defaults' sections",
args[0], args[1]);
goto error;
}
pol = ACL_COND_NONE;
if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
if ((rule->cond = build_acl_cond(NULL, 0, curpx, (const char **)args+arg)) == NULL) {
retlen = snprintf(err, errlen,
"error detected in %s '%s' while parsing '%s' condition",
proxy_type_str(curpx), curpx->id, args[arg]);
goto error;
}
}
else if (*args[arg]) {
retlen = snprintf(err, errlen,
"'%s %s' only accepts 'if' or 'unless', in %s '%s' (was '%s')",
args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]);
goto error;
}
if (rule->cond && (rule->cond->requires & (ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY))) {
struct acl *acl;
const char *name;
acl = cond_find_require(rule->cond, ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY);
name = acl ? acl->name : "(unknown)";
if (acl->requires & (ACL_USE_L6_ANY|ACL_USE_L7_ANY)) {
retlen = snprintf(err, errlen,
"'%s %s' may not reference acl '%s' which makes use of payload in %s '%s'. Please use '%s content' for this.",
args[0], args[1], name, proxy_type_str(curpx), curpx->id, args[0]);
goto error;
}
if (acl->requires & ACL_USE_RTR_ANY)
retlen = snprintf(err, errlen,
"acl '%s' involves some response-only criteria which will be ignored.",
name);
warn++;
}
LIST_INIT(&rule->list);
LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
return warn;