]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] session: support "tcp-request content" rules in backends
authorWilly Tarreau <w@1wt.eu>
Tue, 3 Aug 2010 12:02:05 +0000 (14:02 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 10 Aug 2010 12:10:58 +0000 (14:10 +0200)
Sometimes it's necessary to be able to perform some "layer 6" analysis
in the backend. TCP request rules were not available till now, although
documented in the diagram. Enable them in backend now.

doc/configuration.txt
include/types/buffers.h
src/cfgparse.c
src/proto_tcp.c
src/session.c

index d1fa1fa1fca1c6f3e611d2a096c13a572286b68d..02e765fd5317a73dd0d3e943285618fe8a1ef382 100644 (file)
@@ -5171,10 +5171,12 @@ tcp-request accept [{if | unless} <condition>]
 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
-                                 no    |    yes   |   yes  |   no
+                                 no    |    yes   |   yes  |   yes
 
   During TCP content inspection, the connection is immediately validated if the
   condition is true (when used with "if") or false (when used with "unless").
+  TCP content inspection applies very early when a connection reaches a
+  frontend, then very early when the connection is forwarded to a backend.
   Most of the time during content inspection, a condition will be in an
   uncertain state which is neither true nor false. The evaluation immediately
   stops when such a condition is encountered. It is important to understand
@@ -5197,10 +5199,12 @@ tcp-request content accept [{if | unless} <condition>]
 tcp-request content reject [{if | unless} <condition>]
   Reject a connection if/unless a content inspection condition is matched
   May be used in sections :   defaults | frontend | listen | backend
-                                 no    |    yes   |   yes  |   no
+                                 no    |    yes   |   yes  |   yes
 
   During TCP content inspection, the connection is immediately rejected if the
   condition is true (when used with "if") or false (when used with "unless").
+  TCP content inspection applies very early when a connection reaches a
+  frontend, then very early when the connection is forwarded to a backend.
   Most of the time during content inspection, a condition will be in an
   uncertain state which is neither true nor false. The evaluation immediately
   stops when such a condition is encountered. It is important to understand
@@ -5234,7 +5238,7 @@ tcp-request content reject [{if | unless} <condition>]
 tcp-request inspect-delay <timeout>
   Set the maximum allowed time to wait for data during content inspection
   May be used in sections :   defaults | frontend | listen | backend
-                                 no    |    yes   |   yes  |   no
+                                 no    |    yes   |   yes  |   yes
   Arguments :
     <timeout> is the timeout value specified in milliseconds by default, but
               can be in any other unit if the number is suffixed by the unit,
@@ -5246,6 +5250,11 @@ tcp-request inspect-delay <timeout>
   the data then analyze them. This statement simply enables withholding of
   data for at most the specified amount of time.
 
+  TCP content inspection applies very early when a connection reaches a
+  frontend, then very early when the connection is forwarded to a backend. This
+  means that a connection may experience a first delay in the frontend and a
+  second delay in the backend if both have tcp-request rules.
+
   Note that when performing content inspection, haproxy will evaluate the whole
   rules for every new chunk which gets in, taking into account the fact that
   those data are partial. If no rule matches before the aforementioned delay,
index e08669deacc3bf272d19d5ed2665e15754a8a6fa..7ad3e95270cff787335449e9bc2d0468a4e98609 100644 (file)
  * The field is blanked by buffer_init() and only by analysers themselves
  * afterwards.
  */
-#define AN_REQ_INSPECT          0x00000001  /* inspect request contents */
+#define AN_REQ_INSPECT_FE       0x00000001  /* inspect request contents in the frontend */
 #define AN_REQ_WAIT_HTTP        0x00000002  /* wait for an HTTP request */
 #define AN_REQ_HTTP_PROCESS_FE  0x00000004  /* process the frontend's HTTP part */
 #define AN_REQ_SWITCHING_RULES  0x00000008  /* apply the switching rules */
-#define AN_REQ_HTTP_PROCESS_BE  0x00000010  /* process the backend's HTTP part */
-#define AN_REQ_HTTP_INNER       0x00000020  /* inner processing of HTTP request */
-#define AN_REQ_HTTP_TARPIT      0x00000040  /* wait for end of HTTP tarpit */
-#define AN_REQ_HTTP_BODY        0x00000080  /* inspect HTTP request body */
-#define AN_REQ_STICKING_RULES   0x00000100  /* table persistence matching */
-/* unused: 0x200 */
+#define AN_REQ_INSPECT_BE       0x00000010  /* inspect request contents in the backend */
+#define AN_REQ_HTTP_PROCESS_BE  0x00000020  /* process the backend's HTTP part */
+#define AN_REQ_HTTP_INNER       0x00000040  /* inner processing of HTTP request */
+#define AN_REQ_HTTP_TARPIT      0x00000080  /* wait for end of HTTP tarpit */
+#define AN_REQ_HTTP_BODY        0x00000100  /* inspect HTTP request body */
+#define AN_REQ_STICKING_RULES   0x00000200  /* table persistence matching */
 #define AN_REQ_PRST_RDP_COOKIE  0x00000400  /* persistence on rdp cookie */
 #define AN_REQ_HTTP_XFER_BODY   0x00000800  /* forward request body */
 
index cf9a000b29dbdd31de63fb321a9b4e66f403fbd8..f313b71ec2221c98788c03a879750dee7e13e744 100644 (file)
@@ -5304,7 +5304,7 @@ out_uri_auth_compat:
 
                        if (curproxy->tcp_req.inspect_delay ||
                            !LIST_ISEMPTY(&curproxy->tcp_req.inspect_rules))
-                               curproxy->fe_req_ana |= AN_REQ_INSPECT;
+                               curproxy->fe_req_ana |= AN_REQ_INSPECT_FE;
 
                        if (curproxy->mode == PR_MODE_HTTP) {
                                curproxy->fe_req_ana |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_PROCESS_FE;
@@ -5316,6 +5316,10 @@ out_uri_auth_compat:
                }
 
                if (curproxy->cap & PR_CAP_BE) {
+                       if (curproxy->tcp_req.inspect_delay ||
+                           !LIST_ISEMPTY(&curproxy->tcp_req.inspect_rules))
+                               curproxy->be_req_ana |= AN_REQ_INSPECT_BE;
+
                        if (curproxy->mode == PR_MODE_HTTP) {
                                curproxy->be_req_ana |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_INNER | AN_REQ_HTTP_PROCESS_BE;
                                curproxy->be_rsp_ana |= AN_RES_WAIT_HTTP | AN_RES_HTTP_PROCESS_BE;
index 2ad99416a4e2d3cd44e6747764126f24dfc04e2d..788c98167b14ef89ff3c1e628f3a92f10543ea9e 100644 (file)
@@ -615,23 +615,9 @@ void tcpv6_add_listener(struct listener *listener)
 /* This function performs the TCP request analysis on the current request. It
  * returns 1 if the processing can continue on next analysers, or zero if it
  * needs more data, encounters an error, or wants to immediately abort the
- * request. It relies on buffers flags, and updates s->req->analysers. Its
- * behaviour is rather simple:
- *  - the analyser should check for errors and timeouts, and react as expected.
- *    It does not have to close anything upon error, the caller will. Note that
- *    the caller also knows how to report errors and timeouts.
- *  - if the analyser does not have enough data, it must return 0 without calling
- *    other ones. It should also probably do a buffer_write_dis() to ensure
- *    that unprocessed data will not be forwarded. But that probably depends on
- *    the protocol.
- *  - if an analyser has enough data, it just has to pass on to the next
- *    analyser without using buffer_write_dis() (enabled by default).
- *  - if an analyser thinks it has no added value anymore staying here, it must
- *    reset its bit from the analysers flags in order not to be called anymore.
- *
- * In the future, analysers should be able to indicate that they want to be
- * called after XXX bytes have been received (or transfered), and the min of
- * all's wishes will be used to ring back (unless a special condition occurs).
+ * request. It relies on buffers flags, and updates s->req->analysers. The
+ * function may be called for frontend rules and backend rules. It only relies
+ * on the backend pointer so this works for both cases.
  */
 int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit)
 {
@@ -657,21 +643,21 @@ int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit)
         * - if one rule returns KO, then return KO
         */
 
-       if (req->flags & BF_SHUTR || !s->fe->tcp_req.inspect_delay || tick_is_expired(req->analyse_exp, now_ms))
+       if (req->flags & BF_SHUTR || !s->be->tcp_req.inspect_delay || tick_is_expired(req->analyse_exp, now_ms))
                partial = 0;
        else
                partial = ACL_PARTIAL;
 
-       list_for_each_entry(rule, &s->fe->tcp_req.inspect_rules, list) {
+       list_for_each_entry(rule, &s->be->tcp_req.inspect_rules, list) {
                int ret = ACL_PAT_PASS;
 
                if (rule->cond) {
-                       ret = acl_exec_cond(rule->cond, s->fe, s, &s->txn, ACL_DIR_REQ | partial);
+                       ret = acl_exec_cond(rule->cond, s->be, s, &s->txn, ACL_DIR_REQ | partial);
                        if (ret == ACL_PAT_MISS) {
                                buffer_dont_connect(req);
                                /* just set the request timeout once at the beginning of the request */
-                               if (!tick_isset(req->analyse_exp) && s->fe->tcp_req.inspect_delay)
-                                       req->analyse_exp = tick_add_ifset(now_ms, s->fe->tcp_req.inspect_delay);
+                               if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
+                                       req->analyse_exp = tick_add_ifset(now_ms, s->be->tcp_req.inspect_delay);
                                return 0;
                        }
 
@@ -687,7 +673,7 @@ int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit)
                                buffer_abort(s->rep);
                                req->analysers = 0;
 
-                               s->fe->counters.denied_req++;
+                               s->be->counters.denied_req++;
                                if (s->listener->counters)
                                        s->listener->counters->denied_req++;
 
@@ -777,13 +763,6 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
                        return -1;
                }
 
-               if (!(curpx->cap & PR_CAP_FE)) {
-                       snprintf(err, errlen, "%s %s will be ignored because %s '%s' has no %s capability",
-                                args[0], args[1], proxy_type_str(curpx), curpx->id,
-                                "frontend");
-                       return 1;
-               }
-
                if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
                        retlen = snprintf(err, errlen,
                                          "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
index 84ffcbe4653846a2e757076cc324309c0eb2a3b4..96933d3d06512cfd7b8e49d86d8c0f8aba854020 100644 (file)
@@ -855,9 +855,11 @@ int process_switching_rules(struct session *s, struct buffer *req, int an_bit)
                                goto sw_failed;
        }
 
-       /* we don't want to run the HTTP filters again if the backend has not changed */
-       if (s->fe == s->be)
+       /* we don't want to run the TCP or HTTP filters again if the backend has not changed */
+       if (s->fe == s->be) {
+               s->req->analysers &= ~AN_REQ_INSPECT_BE;
                s->req->analysers &= ~AN_REQ_HTTP_PROCESS_BE;
+       }
 
        /* as soon as we know the backend, we must check if we have a matching forced or ignored
         * persistence rule, and report that in the session.
@@ -1327,10 +1329,10 @@ struct task *process_session(struct task *t)
                        while (ana_list && max_loops--) {
                                /* Warning! ensure that analysers are always placed in ascending order! */
 
-                               if (ana_list & AN_REQ_INSPECT) {
-                                       if (!tcp_inspect_request(s, s->req, AN_REQ_INSPECT))
+                               if (ana_list & AN_REQ_INSPECT_FE) {
+                                       if (!tcp_inspect_request(s, s->req, AN_REQ_INSPECT_FE))
                                                break;
-                                       UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_INSPECT);
+                                       UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_INSPECT_FE);
                                }
 
                                if (ana_list & AN_REQ_WAIT_HTTP) {
@@ -1351,6 +1353,12 @@ struct task *process_session(struct task *t)
                                        UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_SWITCHING_RULES);
                                }
 
+                               if (ana_list & AN_REQ_INSPECT_BE) {
+                                       if (!tcp_inspect_request(s, s->req, AN_REQ_INSPECT_BE))
+                                               break;
+                                       UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_INSPECT_BE);
+                               }
+
                                if (ana_list & AN_REQ_HTTP_PROCESS_BE) {
                                        if (!http_process_req_common(s, s->req, AN_REQ_HTTP_PROCESS_BE, s->be))
                                                break;