From 6c59e0a9423c9a8b92d596f4647379d0ce07f0b2 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 20 Jun 2010 11:56:30 +0200 Subject: [PATCH] [MEDIUM] session counters: add bytes_in_rate and bytes_out_rate counters These counters maintain incoming and outgoing byte rates in a stick-table, over a period which is defined in the configuration (2 ms to 24 days). They can be used to detect service abuse and enforce a certain bandwidth limits per source address for instance, and block if the rate is passed over. Since 32-bit counters are used to compute the rates, it is important not to use too long periods so that we don't have to deal with rates above 4 GB per period. Example : # block if more than 5 Megs retrieved in 30 seconds from a source. stick-table type ip size 200k expire 1m store bytes_out_rate(30s) tcp-request track-counters src tcp-request reject if { trk_bytes_out_rate gt 5000000 } # cause a 15 seconds pause to requests from sources in excess of 2 megs/30s tcp-request inspect-delay 15s tcp-request content accept if { trk_bytes_out_rate gt 2000000 } WAIT_END --- include/types/stick_table.h | 4 ++ src/session.c | 140 ++++++++++++++++++++++++++++++++++-- src/stick_table.c | 2 + 3 files changed, 140 insertions(+), 6 deletions(-) diff --git a/include/types/stick_table.h b/include/types/stick_table.h index 0501adc2f..fba6e2467 100644 --- a/include/types/stick_table.h +++ b/include/types/stick_table.h @@ -49,7 +49,9 @@ enum { STKTABLE_DT_SESS_CNT, /* cumulated number of sessions (accepted connections) */ STKTABLE_DT_SESS_RATE, /* accepted sessions rate */ STKTABLE_DT_BYTES_IN_CNT, /* cumulated bytes count from client to servers */ + STKTABLE_DT_BYTES_IN_RATE,/* bytes rate from client to servers */ STKTABLE_DT_BYTES_OUT_CNT,/* cumulated bytes count from servers to client */ + STKTABLE_DT_BYTES_OUT_RATE,/* bytes rate from servers to client */ STKTABLE_DATA_TYPES /* Number of data types, must always be last */ }; @@ -69,7 +71,9 @@ union stktable_data { unsigned int sess_cnt; struct freq_ctr_period sess_rate; unsigned long long bytes_in_cnt; + struct freq_ctr_period bytes_in_rate; unsigned long long bytes_out_cnt; + struct freq_ctr_period bytes_out_rate; }; /* known data types */ diff --git a/src/session.c b/src/session.c index 6c7a91d5c..97153dc04 100644 --- a/src/session.c +++ b/src/session.c @@ -418,11 +418,20 @@ void session_process_counters(struct session *s) s->listener->counters->bytes_in += bytes; if (s->tracked_counters) { - void *ptr = stktable_data_ptr(s->tracked_table, - s->tracked_counters, - STKTABLE_DT_BYTES_IN_CNT); + void *ptr; + + ptr = stktable_data_ptr(s->tracked_table, + s->tracked_counters, + STKTABLE_DT_BYTES_IN_CNT); if (ptr) stktable_data_cast(ptr, bytes_in_cnt) += bytes; + + ptr = stktable_data_ptr(s->tracked_table, + s->tracked_counters, + STKTABLE_DT_BYTES_IN_RATE); + if (ptr) + update_freq_ctr_period(&stktable_data_cast(ptr, bytes_in_rate), + s->tracked_table->data_arg[STKTABLE_DT_BYTES_IN_RATE].u, bytes); } } } @@ -443,11 +452,20 @@ void session_process_counters(struct session *s) s->listener->counters->bytes_out += bytes; if (s->tracked_counters) { - void *ptr = stktable_data_ptr(s->tracked_table, - s->tracked_counters, - STKTABLE_DT_BYTES_OUT_CNT); + void *ptr; + + ptr = stktable_data_ptr(s->tracked_table, + s->tracked_counters, + STKTABLE_DT_BYTES_OUT_CNT); if (ptr) stktable_data_cast(ptr, bytes_out_cnt) += bytes; + + ptr = stktable_data_ptr(s->tracked_table, + s->tracked_counters, + STKTABLE_DT_BYTES_OUT_RATE); + if (ptr) + update_freq_ctr_period(&stktable_data_cast(ptr, bytes_out_rate), + s->tracked_table->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u, bytes); } } } @@ -2422,6 +2440,59 @@ acl_fetch_src_kbytes_in(struct proxy *px, struct session *l4, void *l7, int dir, return acl_fetch_kbytes_in(&px->table, test, stktable_lookup_key(&px->table, key)); } +/* set test->i to the bytes rate from clients in the stksess entry over the + * configured period. + */ +static int +acl_fetch_bytes_in_rate(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_BYTES_IN_RATE); + if (!ptr) + return 0; /* parameter not stored */ + test->i = read_freq_ctr_period(&stktable_data_cast(ptr, bytes_in_rate), + table->data_arg[STKTABLE_DT_BYTES_IN_RATE].u); + } + return 1; +} + +/* set test->i to the bytes rate from clients from the session's tracked + * counters over the configured period. + */ +static int +acl_fetch_trk_bytes_in_rate(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_bytes_in_rate(l4->tracked_table, test, l4->tracked_counters); +} + +/* set test->i to the bytes rate from clients from the session's source address + * in the table pointed to by expr, over the configured period. + */ +static int +acl_fetch_src_bytes_in_rate(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_bytes_in_rate(&px->table, test, stktable_lookup_key(&px->table, key)); +} + /* set test->i to the number of kbytes sent to clients matching the stksess entry */ static int acl_fetch_kbytes_out(struct stktable *table, struct acl_test *test, struct stksess *ts) @@ -2473,6 +2544,59 @@ acl_fetch_src_kbytes_out(struct proxy *px, struct session *l4, void *l7, int dir return acl_fetch_kbytes_out(&px->table, test, stktable_lookup_key(&px->table, key)); } +/* set test->i to the bytes rate to clients in the stksess entry over the + * configured period. + */ +static int +acl_fetch_bytes_out_rate(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_BYTES_OUT_RATE); + if (!ptr) + return 0; /* parameter not stored */ + test->i = read_freq_ctr_period(&stktable_data_cast(ptr, bytes_out_rate), + table->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u); + } + return 1; +} + +/* set test->i to the bytes rate to clients from the session's tracked counters + * over the configured period. + */ +static int +acl_fetch_trk_bytes_out_rate(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_bytes_out_rate(l4->tracked_table, test, l4->tracked_counters); +} + +/* set test->i to the bytes rate to client from the session's source address in + * the table pointed to by expr, over the configured period. + */ +static int +acl_fetch_src_bytes_out_rate(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_bytes_out_rate(&px->table, test, stktable_lookup_key(&px->table, key)); +} + /* Note: must not be declared as its list will be overwritten */ static struct acl_kw_list acl_kws = {{ },{ @@ -2489,8 +2613,12 @@ static struct acl_kw_list acl_kws = {{ },{ { "src_sess_rate", acl_parse_int, acl_fetch_src_sess_rate, acl_match_int, ACL_USE_TCP4_VOLATILE }, { "trk_kbytes_in", acl_parse_int, acl_fetch_trk_kbytes_in, acl_match_int, ACL_USE_TCP4_VOLATILE }, { "src_kbytes_in", acl_parse_int, acl_fetch_src_kbytes_in, acl_match_int, ACL_USE_TCP4_VOLATILE }, + { "trk_bytes_in_rate", acl_parse_int, acl_fetch_trk_bytes_in_rate, acl_match_int, ACL_USE_NOTHING }, + { "src_bytes_in_rate", acl_parse_int, acl_fetch_src_bytes_in_rate, acl_match_int, ACL_USE_TCP4_VOLATILE }, { "trk_kbytes_out", acl_parse_int, acl_fetch_trk_kbytes_out, acl_match_int, ACL_USE_TCP4_VOLATILE }, { "src_kbytes_out", acl_parse_int, acl_fetch_src_kbytes_out, acl_match_int, ACL_USE_TCP4_VOLATILE }, + { "trk_bytes_out_rate", acl_parse_int, acl_fetch_trk_bytes_out_rate,acl_match_int, ACL_USE_NOTHING }, + { "src_bytes_out_rate", acl_parse_int, acl_fetch_src_bytes_out_rate,acl_match_int, ACL_USE_TCP4_VOLATILE }, { NULL, NULL, NULL, NULL }, }}; diff --git a/src/stick_table.c b/src/stick_table.c index 1801270f1..c0848c345 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -532,7 +532,9 @@ struct stktable_data_type stktable_data_types[STKTABLE_DATA_TYPES] = { [STKTABLE_DT_SESS_CNT] = { .name = "sess_cnt", .data_length = stktable_data_size(sess_cnt) }, [STKTABLE_DT_SESS_RATE] = { .name = "sess_rate", .data_length = stktable_data_size(sess_rate), .arg_type = ARG_T_DELAY }, [STKTABLE_DT_BYTES_IN_CNT] = { .name = "bytes_in_cnt", .data_length = stktable_data_size(bytes_in_cnt) }, + [STKTABLE_DT_BYTES_IN_RATE] = { .name = "bytes_in_rate", .data_length = stktable_data_size(bytes_in_rate), .arg_type = ARG_T_DELAY }, [STKTABLE_DT_BYTES_OUT_CNT] = { .name = "bytes_out_cnt", .data_length = stktable_data_size(bytes_out_cnt) }, + [STKTABLE_DT_BYTES_OUT_RATE]= { .name = "bytes_out_rate",.data_length = stktable_data_size(bytes_out_rate), .arg_type = ARG_T_DELAY }, }; /*