[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.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 240a548..7aa12a7 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -907,9 +907,11 @@
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 @@
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 @@
"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.
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index fc020c9..b753c3b 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -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 @@
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 @@
}
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 @@
return warn;
}
+ /* 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++;
+ }
+
- snprintf(err, errlen, "unknown argument '%s' after '%s' in %s '%s'",
- args[1], args[0], proxy_type_str(proxy), curpx->id);
- return -1;
+ 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;
}