]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MAJOR] session: add track-counters to track counters related to the session
authorWilly Tarreau <w@1wt.eu>
Mon, 14 Jun 2010 19:04:55 +0000 (21:04 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 10 Aug 2010 16:04:12 +0000 (18:04 +0200)
This patch adds the ability to set a pointer in the session to an
entry in a stick table which holds various counters related to a
specific pattern.

Right now the syntax matches the target syntax and only the "src"
pattern can be specified, to track counters related to the session's
IPv4 source address. There is a special function to extract it and
convert it to a key. But the goal is to be able to later support as
many patterns as for the stick rules, and get rid of the specific
function.

The "track-counters" directive may only be set in a "tcp-request"
statement right now. Only the first one applies. Probably that later
we'll support multi-criteria tracking for a single session and that
we'll have to name tracking pointers.

No counter is updated right now, only the refcount is. Some subsequent
patches will have to bring that feature.

include/proto/proto_tcp.h
include/proto/session.h
include/proto/stick_table.h
include/types/proto_tcp.h
include/types/session.h
src/cfgparse.c
src/proto_tcp.c
src/session.c
src/stick_table.c

index 37d8ea8bd6420d0abd3d8bd1d216091e47c77189..1b46d37ad11544778195d0ce448d43a333652876 100644 (file)
@@ -25,6 +25,7 @@
 #include <common/config.h>
 #include <types/proto_tcp.h>
 #include <types/task.h>
+#include <proto/stick_table.h>
 
 int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct sockaddr_in *remote);
 void tcpv4_add_listener(struct listener *listener);
@@ -37,6 +38,22 @@ int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit);
 int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit);
 int tcp_exec_req_rules(struct session *s);
 
+/* Converts the TCPv4 source address to a stick_table key usable for table
+ * lookups. Returns either NULL if the source cannot be converted (eg: not
+ * IPv4) or a pointer to the converted result in static_table_key in the
+ * appropriate format (IP).
+ */
+static inline struct stktable_key *tcpv4_src_to_stktable_key(struct session *s)
+{
+       /* right now we only support IPv4 */
+       if (s->cli_addr.ss_family != AF_INET)
+               return NULL;
+
+       static_table_key.key = (void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr;
+       return &static_table_key;
+}
+
+
 #endif /* _PROTO_PROTO_TCP_H */
 
 /*
index c7a693e73755f9230bc0e5c8043e47158b20dde0..3d56f8e030ed324adb65ef3817eccf1a31920ffc 100644 (file)
@@ -25,6 +25,7 @@
 #include <common/config.h>
 #include <common/memory.h>
 #include <types/session.h>
+#include <proto/stick_table.h>
 
 extern struct pool_head *pool2_session;
 extern struct list sessions;
@@ -40,6 +41,31 @@ void sess_change_server(struct session *sess, struct server *newsrv);
 struct task *process_session(struct task *t);
 void sess_set_term_flags(struct session *s);
 void default_srv_error(struct session *s, struct stream_interface *si);
+int parse_track_counters(char **args, int *arg,
+                        int section_type, struct proxy *curpx,
+                        struct track_ctr_prm *prm,
+                        struct proxy *defpx, char *err, int errlen);
+
+/* Remove the refcount from the session to the tracked counters, and clear the
+ * pointer to ensure this is only performed once. The caller is responsible for
+ * ensuring that the pointer is valid first.
+ */
+static inline void session_store_counters(struct session *s)
+{
+       s->tracked_counters->ref_cnt--;
+       s->tracked_counters = NULL;
+}
+
+/* Enable tracking of session counters on stksess <ts>. The caller is
+ * responsible for ensuring that <t> and <ts> are valid pointers and that no
+ * previous tracked_counters was assigned to the session.
+ */
+static inline void session_track_counters(struct session *s, struct stktable *t, struct stksess *ts)
+{
+       ts->ref_cnt++;
+       s->tracked_table = t;
+       s->tracked_counters = ts;
+}
 
 static void inline trace_term(struct session *s, unsigned int code)
 {
index db45760f251fc24e6da4c4d7bb3912177eb0c7e5..813b3b971600325f481cd2adcd1acc0e090d2491 100644 (file)
@@ -36,6 +36,7 @@ void stksess_free(struct stktable *t, struct stksess *ts);
 
 int stktable_init(struct stktable *t);
 int stktable_parse_type(char **args, int *idx, unsigned long *type, size_t *key_size);
+struct stksess *stktable_get_entry(struct stktable *table, struct stktable_key *key);
 struct stksess *stktable_store(struct stktable *t, struct stksess *ts);
 struct stksess *stktable_touch(struct stktable *t, struct stksess *ts);
 struct stksess *stktable_lookup(struct stktable *t, struct stksess *ts);
index 54d12a7d5a2f71db6bb04934efeedda4766d61a3..a7ca56a4f8349c93a5dc9f8565f96859b1c3f338 100644 (file)
@@ -1,23 +1,23 @@
 /*
-  include/types/proto_tcp.h
-  This file contains TCP protocol definitions.
-
 Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
-  
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation, version 2.1
-  exclusively.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-*/
* include/types/proto_tcp.h
* This file contains TCP protocol definitions.
+ *
* Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
 
 #ifndef _TYPES_PROTO_TCP_H
 #define _TYPES_PROTO_TCP_H
 #include <common/mini-clist.h>
 
 #include <types/acl.h>
+#include <types/session.h>
 
 /* Layer4 accept/reject rules */
 enum {
        TCP_ACT_ACCEPT = 1,
        TCP_ACT_REJECT = 2,
+       TCP_ACT_TRK_CTR = 3,
 };
 
 struct tcp_rule {
        struct list list;
        struct acl_cond *cond;
        int action;
+       union {
+               struct track_ctr_prm trk_ctr;
+       } act_prm;
 };
 
 #endif /* _TYPES_PROTO_TCP_H */
index fdccc7b1cf8ae7c6f0ae331c494c9fa03533436a..222c30f7adc52d577d31dc1f5afdc68f1a628627 100644 (file)
@@ -184,7 +184,8 @@ struct session {
                int flags;
        } store[8];                             /* tracked stickiness values to store */
        int store_count;
-       struct stksess *tracked_src_counters;   /* tracked counters for this source */
+       struct stksess *tracked_counters;       /* counters currently being tracked by this session */
+       struct stktable *tracked_table;         /* table the counters above belong to (undefined if counters are null) */
 
        struct {
                int logwait;                    /* log fields waiting to be collected : LW_* */
@@ -236,6 +237,15 @@ struct session {
        unsigned int uniq_id;                   /* unique ID used for the traces */
 };
 
+/* parameters to configure tracked counters */
+struct track_ctr_prm {
+       int type;                               /* type of the key */
+       union {
+               struct stktable *t;             /* a pointer to the table */
+               char *n;                        /* or its name during parsing. */
+       } table;
+};
+
 
 #endif /* _TYPES_SESSION_H */
 
index f313b71ec2221c98788c03a879750dee7e13e744..871310b66b2cda70fd1b5bd0fbe9b831638ab230 100644 (file)
@@ -4688,6 +4688,7 @@ int check_config_validity()
        while (curproxy != NULL) {
                struct switching_rule *rule;
                struct sticking_rule *mrule;
+               struct tcp_rule *trule;
                struct listener *listener;
                unsigned int next_id;
 
@@ -4936,6 +4937,43 @@ int check_config_validity()
                        }
                }
 
+               /* find the target table for 'tcp-request' layer 4 rules */
+               list_for_each_entry(trule, &curproxy->tcp_req.l4_rules, list) {
+                       struct proxy *target;
+
+                       if (trule->action != TCP_ACT_TRK_CTR)
+                               continue;
+
+                       if (trule->act_prm.trk_ctr.table.n)
+                               target = findproxy(trule->act_prm.trk_ctr.table.n, 0);
+                       else
+                               target = curproxy;
+
+                       if (!target) {
+                               Alert("Proxy '%s': unable to find table '%s' referenced by track-counter.\n",
+                                     curproxy->id, trule->act_prm.trk_ctr.table.n);
+                               cfgerr++;
+                       }
+                       else if (target->table.size == 0) {
+                               Alert("Proxy '%s': table '%s' used but not configured.\n",
+                                     curproxy->id, trule->act_prm.trk_ctr.table.n ? trule->act_prm.trk_ctr.table.n : curproxy->id);
+                               cfgerr++;
+                       }
+                       else if (trule->act_prm.trk_ctr.type != target->table.type) {
+                               Alert("Proxy '%s': type of track-counters pattern not usable with type of stick-table '%s'.\n",
+                                     curproxy->id, trule->act_prm.trk_ctr.table.n ? trule->act_prm.trk_ctr.table.n : curproxy->id);
+                               cfgerr++;
+                       }
+                       else {
+                               free(trule->act_prm.trk_ctr.table.n);
+                               trule->act_prm.trk_ctr.table.t = &target->table;
+                               /* Note: if we decide to enhance the track-counters syntax, we may be able
+                                * to pass a list of counters to track and allocate them right here using
+                                * stktable_alloc_data_type().
+                                */
+                       }
+               }
+
                if (curproxy->uri_auth && !(curproxy->uri_auth->flags & ST_CONVDONE) &&
                    !LIST_ISEMPTY(&curproxy->uri_auth->req_acl) &&
                    (curproxy->uri_auth->userlist || curproxy->uri_auth->auth_realm )) {
index 24906be63ae8ce908784ab25326328eeb0629bb8..7adb0f99905b88c217c8352a91a7bf94f534b895 100644 (file)
@@ -46,6 +46,7 @@
 #include <proto/protocols.h>
 #include <proto/proto_tcp.h>
 #include <proto/proxy.h>
+#include <proto/session.h>
 #include <proto/stick_table.h>
 #include <proto/stream_sock.h>
 #include <proto/task.h>
@@ -704,6 +705,9 @@ int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit)
 int tcp_exec_req_rules(struct session *s)
 {
        struct tcp_rule *rule;
+       struct stksess *ts = s->tracked_counters;
+       struct stktable *t = NULL;
+       int result = 1;
        int ret;
 
        list_for_each_entry(rule, &s->fe->tcp_req.l4_rules, list) {
@@ -727,13 +731,29 @@ int tcp_exec_req_rules(struct session *s)
                                        s->flags |= SN_ERR_PRXCOND;
                                if (!(s->flags & SN_FINST_MASK))
                                        s->flags |= SN_FINST_R;
-                               return 0;
+                               result = 0;
+                               break;
+                       }
+                       else if (rule->action == TCP_ACT_TRK_CTR) {
+                               if (!s->tracked_counters) {
+                                       /* only the first valid track-counters directive applies.
+                                        * Also, note that right now we can only track SRC so we
+                                        * don't check how to get the key, but later we may need
+                                        * to consider rule->act_prm->trk_ctr.type.
+                                        */
+                                       t = rule->act_prm.trk_ctr.table.t;
+                                       ts = stktable_get_entry(t, tcpv4_src_to_stktable_key(s));
+                                       if (ts)
+                                               session_track_counters(s, t, ts);
+                               }
+                       }
+                       else {
+                               /* otherwise it's an accept */
+                               break;
                        }
-                       /* otherwise it's an accept */
-                       break;
                }
        }
-       return 1;
+       return result;
 }
 
 /* This function should be called to parse a line starting with the "tcp-request"
@@ -840,13 +860,29 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
 
        /* OK so we're in front of plain L4 rules */
 
-       if (strcmp(args[1], "accept") == 0)
+       if (strcmp(args[1], "accept") == 0) {
+               arg++;
                rule->action = TCP_ACT_ACCEPT;
-       else if (strcmp(args[1], "reject") == 0)
+       }
+       else if (strcmp(args[1], "reject") == 0) {
+               arg++;
                rule->action = TCP_ACT_REJECT;
+       }
+       else if (strcmp(args[1], "track-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_CTR;
+       }
        else {
                retlen = snprintf(err, errlen,
-                                 "'%s' expects 'inspect-delay', 'content', 'accept' or 'reject', in %s '%s' (was '%s')",
+                                 "'%s' expects 'inspect-delay', 'content', 'accept', 'reject', or 'track-counters' in %s '%s' (was '%s')",
                                  args[0], proxy_type_str(curpx), curpx->id, args[1]);
                goto error;
        }
@@ -857,7 +893,6 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
                goto error;
        }
 
-       arg++;
        pol = ACL_COND_NONE;
 
        if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
index 96933d3d06512cfd7b8e49d86d8c0f8aba854020..4e2bedd6525789a3b6273ee07ff547cdcd989ddc 100644 (file)
@@ -63,6 +63,8 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr)
        /* minimum session initialization required for monitor mode below */
        s->flags = 0;
        s->logs.logwait = p->to_log;
+       s->tracked_counters = NULL;
+       s->tracked_table = NULL;
 
        /* if this session comes from a known monitoring system, we want to ignore
         * it as soon as possible, which means closing it immediately for TCP, but
@@ -117,6 +119,8 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr)
         * even initializing the stream interfaces.
         */
        if ((l->options & LI_O_TCP_RULES) && !tcp_exec_req_rules(s)) {
+               if (s->tracked_counters)
+                       session_store_counters(s);
                task_free(t);
                LIST_DEL(&s->list);
                pool_free2(pool2_session, s);
@@ -176,7 +180,6 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr)
 
        /* init store persistence */
        s->store_count = 0;
-       s->tracked_src_counters = NULL;
 
        /* Adjust some socket options */
        if (unlikely(fcntl(cfd, F_SETFL, O_NONBLOCK) == -1)) {
@@ -257,6 +260,8 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr)
                        /* work is finished, we can release everything (eg: monitoring) */
                        pool_free2(pool2_buffer, s->rep);
                        pool_free2(pool2_buffer, s->req);
+                       if (s->tracked_counters)
+                               session_store_counters(s);
                        task_free(t);
                        LIST_DEL(&s->list);
                        pool_free2(pool2_session, s);
@@ -279,6 +284,8 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr)
        pool_free2(pool2_buffer, s->req);
  out_free_task:
        p->feconn--;
+       if (s->tracked_counters)
+               session_store_counters(s);
        task_free(t);
  out_free_session:
        LIST_DEL(&s->list);
@@ -341,6 +348,9 @@ void session_free(struct session *s)
                pool_free2(fe->req_cap_pool, txn->req.cap);
        }
 
+       if (s->tracked_counters)
+               session_store_counters(s);
+
        list_for_each_entry_safe(bref, back, &s->back_refs, users) {
                /* we have to unlink all watchers. We must not relink them if
                 * this session was the last one in the list.
@@ -2040,6 +2050,56 @@ void default_srv_error(struct session *s, struct stream_interface *si)
                s->flags |= fin;
 }
 
+
+/* Parse a "track-counters" line starting with "track-counters" in args[arg-1].
+ * Returns the number of warnings emitted, or -1 in case of fatal errors. The
+ * <prm> struct is fed with the table name if any. If unspecified, the caller
+ * will assume that the current proxy's table is used.
+ */
+int parse_track_counters(char **args, int *arg,
+                        int section_type, struct proxy *curpx,
+                        struct track_ctr_prm *prm,
+                        struct proxy *defpx, char *err, int errlen)
+{
+       int pattern_type = 0;
+
+       /* parse the arguments of "track-counters" before the condition in the
+        * following form :
+        *      track-counters src [ table xxx ] [ if|unless ... ]
+        */
+       while (args[*arg]) {
+               if (strcmp(args[*arg], "src") == 0) {
+                       prm->type = STKTABLE_TYPE_IP;
+                       pattern_type = 1;
+               }
+               else if (strcmp(args[*arg], "table") == 0) {
+                       if (!args[*arg + 1]) {
+                               snprintf(err, errlen,
+                                        "missing table for track-counter in %s '%s'.",
+                                        proxy_type_str(curpx), curpx->id);
+                               return -1;
+                       }
+                       /* we copy the table name for now, it will be resolved later */
+                       prm->table.n = strdup(args[*arg + 1]);
+                       (*arg)++;
+               }
+               else {
+                       /* unhandled keywords are handled by the caller */
+                       break;
+               }
+               (*arg)++;
+       }
+
+       if (!pattern_type) {
+               snprintf(err, errlen,
+                        "track-counter key not specified in %s '%s' (found %s, only 'src' is supported).",
+                        proxy_type_str(curpx), curpx->id, quote_arg(args[*arg]));
+               return -1;
+       }
+
+       return 0;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8
index 09cb96beb19c57d04efa2547621b652296ac2bec..f490384234a473fa81b28c6abdfe7fff0af8b071 100644 (file)
@@ -225,6 +225,30 @@ struct stksess *stktable_store(struct stktable *t, struct stksess *ts)
        return ts;
 }
 
+/* Returns a valid or initialized stksess for the specified stktable_key in the
+ * specified table, or NULL if the key was NULL, or if no entry was found nor
+ * could be created. The entry's expiration is updated.
+ */
+struct stksess *stktable_get_entry(struct stktable *table, struct stktable_key *key)
+{
+       struct stksess *ts;
+
+       if (!key)
+               return NULL;
+
+       ts = stktable_lookup_key(table, key);
+       if (ts == NULL) {
+               /* entry does not exist, initialize a new one */
+               ts = stksess_new(table, key);
+               if (!ts)
+                       return NULL;
+               stktable_store(table, ts);
+       }
+       else
+               stktable_touch(table, ts);
+       return ts;
+}
+
 /*
  * Trash expired sticky sessions from table <t>. The next expiration date is
  * returned.