From 1a68794418fea904d19efd8258468f02b4b95ba2 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 23 May 2010 22:40:30 +0200 Subject: [PATCH] [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. --- doc/configuration.txt | 73 +++++++++++++++++++++++++++++++++++++++ src/proto_tcp.c | 79 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 142 insertions(+), 10 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 240a54898..7aa12a71a 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -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 ] } size about time format. +tcp-request accept [{if | unless} ] + 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} ] 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 client". +tcp-request reject [{if | unless} ] + 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 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 fc020c969..b753c3bbd 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 + * Copyright 2000-2010 Willy Tarreau * * 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; }