[MEDIUM] config: parse tcp layer4 rules (tcp-request accept/reject)

These rules currently only support the "accept" and "reject" actions.
They will apply on pure layer 4 and will not support any content.
This commit is contained in:
Willy Tarreau 2010-05-23 22:40:30 +02:00
parent ab786194f0
commit 1a68794418
2 changed files with 142 additions and 10 deletions

View File

@ -907,9 +907,11 @@ stick match - - X X
stick on - - X X
stick store-request - - X X
stick-table - - X X
tcp-request accept - 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 -
@ -5061,6 +5063,38 @@ stick-table type {ip | integer | string [len <length>] } size <size>
about time format.
tcp-request 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
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 accepts 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.
See section 7 about ACL usage.
See also : "tcp-request reject" and "tcp-request content"
tcp-request content accept [{if | unless} <condition>]
Accept a connection if/unless a content inspection condition is matched
May be used in sections : defaults | frontend | listen | backend
@ -5164,6 +5198,45 @@ 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 are accounted in stats but are not logged. 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"
timeout check <timeout>
Set additional check timeout, but only after a connection has been already
established.

View File

@ -1,7 +1,7 @@
/*
* AF_INET/AF_INET6 SOCK_STREAM protocol layer (tcp)
*
* Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -717,6 +717,11 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
const char *ptr = NULL;
unsigned int val;
int retlen;
int action;
int warn = 0;
int pol = ACL_COND_NONE;
struct acl_cond *cond;
struct tcp_rule *rule;
if (!*args[1]) {
snprintf(err, errlen, "missing argument for '%s' in %s '%s'",
@ -758,12 +763,6 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
}
if (!strcmp(args[1], "content")) {
int action;
int warn = 0;
int pol = ACL_COND_NONE;
struct acl_cond *cond;
struct tcp_rule *rule;
if (curpx == defpx) {
snprintf(err, errlen, "%s %s is not allowed in 'defaults' sections",
args[0], args[1]);
@ -819,9 +818,69 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
return warn;
}
snprintf(err, errlen, "unknown argument '%s' after '%s' in %s '%s'",
args[1], args[0], proxy_type_str(proxy), curpx->id);
return -1;
/* OK so we're in front of plain L4 rules */
if (!strcmp(args[1], "accept"))
action = TCP_ACT_ACCEPT;
else if (!strcmp(args[1], "reject"))
action = TCP_ACT_REJECT;
else {
retlen = snprintf(err, errlen,
"'%s' expects 'inspect-delay', 'content', 'accept' or 'reject', in %s '%s' (was '%s')",
args[0], proxy_type_str(curpx), curpx->id, args[1]);
return -1;
}
if (curpx == defpx) {
snprintf(err, errlen, "%s %s is not allowed in 'defaults' sections",
args[0], args[1]);
return -1;
}
pol = ACL_COND_NONE;
cond = NULL;
if (strcmp(args[2], "if") == 0 || strcmp(args[2], "unless") == 0) {
if ((cond = build_acl_cond(NULL, 0, curpx, (const char **)args+2)) == NULL) {
retlen = snprintf(err, errlen,
"error detected in %s '%s' while parsing '%s' condition",
proxy_type_str(curpx), curpx->id, args[2]);
return -1;
}
}
else if (*args[2]) {
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[2]);
return -1;
}
if (cond && (cond->requires & (ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY))) {
struct acl *acl;
const char *name;
acl = cond_find_require(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]);
return -1;
}
if (acl->requires & ACL_USE_RTR_ANY)
retlen = snprintf(err, errlen,
"acl '%s' involves some response-only criteria which will be ignored.",
name);
warn++;
}
rule = (struct tcp_rule *)calloc(1, sizeof(*rule));
rule->cond = cond;
rule->action = action;
LIST_INIT(&rule->list);
LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
return warn;
}