]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MINOR] session-counters: add a general purpose counter (gpc0)
authorWilly Tarreau <w@1wt.eu>
Sun, 20 Jun 2010 10:47:25 +0000 (12:47 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 10 Aug 2010 16:04:14 +0000 (18:04 +0200)
This counter may be used to track anything. Two sets of ACLs are available
to manage it, one gets its value, and the other one increments its value
and returns it. In the second case, the entry is created if it did not
exist.

Thus it is possible for example to mark a source as being an abuser and
to keep it marked as long as it does not wait for the entry to expire :

# The rules below use gpc0 to track abusers, and reject them if
# a source has been marked as such. The track-counters statement
# automatically refreshes the entry which will not expire until a
# 1-minute silence is respected from the source. The second rule
# evaluates the second part if the first one is true, so GPC0 will
# be increased once the conn_rate is above 100/5s.
stick-table type ip size 200k expire 1m store conn_rate(5s),gpc0
tcp-request track-counters src
tcp-request reject if { trk_get_gpc0 gt 0 }
tcp-request reject if { trk_conn_rate gt 100 } { trk_inc_gpc0 gt 0}

Alternatively, it is possible to let the entry expire even in presence of
traffic by swapping the check for gpc0 and the track-counters statement :

stick-table type ip size 200k expire 1m store conn_rate(5s),gpc0
tcp-request reject if { src_get_gpc0 gt 0 }
tcp-request track-counters src
tcp-request reject if { trk_conn_rate gt 100 } { trk_inc_gpc0 gt 0}

It is also possible not to track counters at all, but entry lookups will
then be performed more often :

stick-table type ip size 200k expire 1m store conn_rate(5s),gpc0
tcp-request reject if { src_get_gpc0 gt 0 }
tcp-request reject if { src_conn_rate gt 100 } { src_inc_gpc0 gt 0}

The '0' at the end of the counter name is there because if we find that more
counters may be useful, other ones will be added.

include/types/stick_table.h
src/session.c
src/stick_table.c

index fba6e24673ec0acde293cca6e54fef9b08e84e54..a1bfc1acf98a76a0321a99f809715041f73ca858 100644 (file)
@@ -43,6 +43,7 @@ enum {
 /* The types of extra data we can store in a stick table */
 enum {
        STKTABLE_DT_SERVER_ID,    /* the server ID to use with this session if > 0 */
+       STKTABLE_DT_GPC0,         /* General Purpose Counter 0 (unsigned 32-bit integer) */
        STKTABLE_DT_CONN_CNT,     /* cumulated number of connections */
        STKTABLE_DT_CONN_RATE,    /* incoming connection rate */
        STKTABLE_DT_CONN_CUR,     /* concurrent number of connections */
@@ -65,6 +66,7 @@ enum {
 /* stick_table extra data. This is mainly used for casting or size computation */
 union stktable_data {
        int server_id;
+       unsigned int gpc0;
        unsigned int conn_cnt;
        struct freq_ctr_period conn_rate;
        unsigned int conn_cur;
index 2c115767cd1870be2786ae21edc9acde79541f9c..aaa0622b88572a460d9bc1f9da4162211981a945 100644 (file)
@@ -2102,6 +2102,106 @@ void default_srv_error(struct session *s, struct stream_interface *si)
 /*           All supported ACL keywords must be declared here.          */
 /************************************************************************/
 
+/* set test->i to the General Purpose Counter 0 value in the stksess entry <ts> */
+static int
+acl_fetch_get_gpc0(struct stktable *table, struct acl_test *test, struct stksess *ts)
+{
+       test->flags = ACL_TEST_F_VOL_TEST;
+       test->i = 0;
+       if (ts != NULL) {
+               void *ptr = stktable_data_ptr(table, ts, STKTABLE_DT_GPC0);
+               if (!ptr)
+                       return 0; /* parameter not stored */
+               test->i = stktable_data_cast(ptr, gpc0);
+       }
+       return 1;
+}
+
+/* set test->i to the General Purpose Counter 0 value from the session's tracked
+ * counters.
+ */
+static int
+acl_fetch_trk_get_gpc0(struct proxy *px, struct session *l4, void *l7, int dir,
+                      struct acl_expr *expr, struct acl_test *test)
+{
+       if (!l4->tracked_counters)
+               return 0;
+       return acl_fetch_get_gpc0(l4->tracked_table, test, l4->tracked_counters);
+}
+
+/* set test->i to the General Purpose Counter 0 value from the session's source
+ * address in the table pointed to by expr.
+ */
+static int
+acl_fetch_src_get_gpc0(struct proxy *px, struct session *l4, void *l7, int dir,
+                      struct acl_expr *expr, struct acl_test *test)
+{
+       struct stktable_key *key;
+
+       key = tcpv4_src_to_stktable_key(l4);
+       if (!key)
+               return 0; /* only TCPv4 is supported right now */
+
+       if (expr->arg_len)
+               px = find_stktable(expr->arg.str);
+
+       if (!px)
+               return 0; /* table not found */
+
+       return acl_fetch_get_gpc0(&px->table, test, stktable_lookup_key(&px->table, key));
+}
+
+/* Increment the General Purpose Counter 0 value in the stksess entry <ts> and
+ * return it into test->i.
+ */
+static int
+acl_fetch_inc_gpc0(struct stktable *table, struct acl_test *test, struct stksess *ts)
+{
+       test->flags = ACL_TEST_F_VOL_TEST;
+       test->i = 0;
+       if (ts != NULL) {
+               void *ptr = stktable_data_ptr(table, ts, STKTABLE_DT_GPC0);
+               if (!ptr)
+                       return 0; /* parameter not stored */
+               test->i = ++stktable_data_cast(ptr, gpc0);
+       }
+       return 1;
+}
+
+/* Increment the General Purpose Counter 0 value from the session's tracked
+ * counters and return it into test->i.
+ */
+static int
+acl_fetch_trk_inc_gpc0(struct proxy *px, struct session *l4, void *l7, int dir,
+                      struct acl_expr *expr, struct acl_test *test)
+{
+       if (!l4->tracked_counters)
+               return 0;
+       return acl_fetch_inc_gpc0(l4->tracked_table, test, l4->tracked_counters);
+}
+
+/* Increment the General Purpose Counter 0 value from the session's source
+ * address in the table pointed to by expr, and return it into test->i.
+ */
+static int
+acl_fetch_src_inc_gpc0(struct proxy *px, struct session *l4, void *l7, int dir,
+                      struct acl_expr *expr, struct acl_test *test)
+{
+       struct stktable_key *key;
+
+       key = tcpv4_src_to_stktable_key(l4);
+       if (!key)
+               return 0; /* only TCPv4 is supported right now */
+
+       if (expr->arg_len)
+               px = find_stktable(expr->arg.str);
+
+       if (!px)
+               return 0; /* table not found */
+
+       return acl_fetch_inc_gpc0(&px->table, test, stktable_update_key(&px->table, key));
+}
+
 /* set test->i to the cumulated number of connections in the stksess entry <ts> */
 static int
 acl_fetch_conn_cnt(struct stktable *table, struct acl_test *test, struct stksess *ts)
@@ -2594,6 +2694,10 @@ acl_fetch_src_bytes_out_rate(struct proxy *px, struct session *l4, void *l7, int
 
 /* Note: must not be declared <const> as its list will be overwritten */
 static struct acl_kw_list acl_kws = {{ },{
+       { "trk_get_gpc0",       acl_parse_int,   acl_fetch_trk_get_gpc0,      acl_match_int, ACL_USE_NOTHING },
+       { "src_get_gpc0",       acl_parse_int,   acl_fetch_src_get_gpc0,      acl_match_int, ACL_USE_TCP4_VOLATILE },
+       { "trk_inc_gpc0",       acl_parse_int,   acl_fetch_trk_inc_gpc0,      acl_match_int, ACL_USE_NOTHING },
+       { "src_inc_gpc0",       acl_parse_int,   acl_fetch_src_inc_gpc0,      acl_match_int, ACL_USE_TCP4_VOLATILE },
        { "trk_conn_cnt",       acl_parse_int,   acl_fetch_trk_conn_cnt,      acl_match_int, ACL_USE_NOTHING },
        { "src_conn_cnt",       acl_parse_int,   acl_fetch_src_conn_cnt,      acl_match_int, ACL_USE_TCP4_VOLATILE },
        { "trk_conn_rate",      acl_parse_int,   acl_fetch_trk_conn_rate,     acl_match_int, ACL_USE_NOTHING },
index 471424a83c36569444ec9cdf74b46e2b2df00f14..8b216aededc397794bfc89239e977fb028786e65 100644 (file)
@@ -546,6 +546,7 @@ int stktable_compatible_pattern(struct pattern_expr *expr, unsigned long table_t
 /* Extra data types processing */
 struct stktable_data_type stktable_data_types[STKTABLE_DATA_TYPES] = {
        [STKTABLE_DT_SERVER_ID] = { .name = "server_id", .data_length = stktable_data_size(server_id) },
+       [STKTABLE_DT_GPC0]      = { .name = "gpc0",      .data_length = stktable_data_size(gpc0)      },
        [STKTABLE_DT_CONN_CNT]  = { .name = "conn_cnt",  .data_length = stktable_data_size(conn_cnt)  },
        [STKTABLE_DT_CONN_RATE] = { .name = "conn_rate", .data_length = stktable_data_size(conn_rate), .arg_type = ARG_T_DELAY  },
        [STKTABLE_DT_CONN_CUR]  = { .name = "conn_cur",  .data_length = stktable_data_size(conn_cur)  },