[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
This commit is contained in:
Willy Tarreau 2010-06-20 11:56:30 +02:00
parent 91c43d7fe4
commit 6c59e0a942
3 changed files with 140 additions and 6 deletions

View File

@ -49,7 +49,9 @@ enum {
STKTABLE_DT_SESS_CNT, /* cumulated number of sessions (accepted connections) */ STKTABLE_DT_SESS_CNT, /* cumulated number of sessions (accepted connections) */
STKTABLE_DT_SESS_RATE, /* accepted sessions rate */ STKTABLE_DT_SESS_RATE, /* accepted sessions rate */
STKTABLE_DT_BYTES_IN_CNT, /* cumulated bytes count from client to servers */ 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_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 */ STKTABLE_DATA_TYPES /* Number of data types, must always be last */
}; };
@ -69,7 +71,9 @@ union stktable_data {
unsigned int sess_cnt; unsigned int sess_cnt;
struct freq_ctr_period sess_rate; struct freq_ctr_period sess_rate;
unsigned long long bytes_in_cnt; unsigned long long bytes_in_cnt;
struct freq_ctr_period bytes_in_rate;
unsigned long long bytes_out_cnt; unsigned long long bytes_out_cnt;
struct freq_ctr_period bytes_out_rate;
}; };
/* known data types */ /* known data types */

View File

@ -418,11 +418,20 @@ void session_process_counters(struct session *s)
s->listener->counters->bytes_in += bytes; s->listener->counters->bytes_in += bytes;
if (s->tracked_counters) { if (s->tracked_counters) {
void *ptr = stktable_data_ptr(s->tracked_table, void *ptr;
s->tracked_counters,
STKTABLE_DT_BYTES_IN_CNT); ptr = stktable_data_ptr(s->tracked_table,
s->tracked_counters,
STKTABLE_DT_BYTES_IN_CNT);
if (ptr) if (ptr)
stktable_data_cast(ptr, bytes_in_cnt) += bytes; 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; s->listener->counters->bytes_out += bytes;
if (s->tracked_counters) { if (s->tracked_counters) {
void *ptr = stktable_data_ptr(s->tracked_table, void *ptr;
s->tracked_counters,
STKTABLE_DT_BYTES_OUT_CNT); ptr = stktable_data_ptr(s->tracked_table,
s->tracked_counters,
STKTABLE_DT_BYTES_OUT_CNT);
if (ptr) if (ptr)
stktable_data_cast(ptr, bytes_out_cnt) += bytes; 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)); 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 <ts> 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 <ts> */ /* set test->i to the number of kbytes sent to clients matching the stksess entry <ts> */
static int static int
acl_fetch_kbytes_out(struct stktable *table, struct acl_test *test, struct stksess *ts) 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)); 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 <ts> 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 <const> as its list will be overwritten */ /* Note: must not be declared <const> as its list will be overwritten */
static struct acl_kw_list acl_kws = {{ },{ 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 }, { "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 }, { "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 }, { "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 }, { "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 }, { "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 }, { NULL, NULL, NULL, NULL },
}}; }};

View File

@ -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_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_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_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_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 },
}; };
/* /*