[MINOR] tcp: add per-source connection rate limiting

This change makes use of the stick-tables to keep track of any source
address activity. Two ACLs make it possible to check the count of an
entry or update it and act accordingly. The typical usage will be to
reject a TCP request upon match of an excess value.
This commit is contained in:
Willy Tarreau 2010-06-05 19:13:27 +02:00
parent 41883e2041
commit a975b8f381
2 changed files with 112 additions and 0 deletions

View File

@ -6265,9 +6265,36 @@ src <ip_address>
certain resources such as statistics. Note that it is the TCP-level source
address which is used, and not the address of a client behind a proxy.
src_count <integer>
src_count(backend) <integer>
Returns the number of occurrences of the source IPv4 address in the current
backend's stick-table or in the designated stick-table. If the address is not
found, zero is returned.
src_port <integer>
Applies to the client's TCP source port. This has a very limited usage.
src_update_count <integer>
src_update_count(backend) <integer>
Creates or updates the entry associated to the source IPv4 address in the
current backend's stick-table or in the designated stick-table. This table
must be configured to store the "conn_cum" data type, otherwise the match
will be ignored. The current count is incremented by one, and the expiration
timer refreshed. The updated count is returned, so this match can't return
zero. This is used to reject service abusers based on their source address.
Example :
# This frontend limits incoming SSH connections to 3 per 10 second for
# each source address, and rejects excess connections until a 10 second
# silence is observed. At most 20 addresses are tracked.
listen ssh
bind :22
mode tcp
maxconn 100
stick-table type ip size 20 expire 10s store conn_cum
tcp-request content reject if { src_update_count gt 3 }
server local 127.0.0.1:22
srv_is_up(<server>)
srv_is_up(<backend>/<server>)
Returns true when the designated server is UP, and false when it is either

View File

@ -46,7 +46,9 @@
#include <proto/protocols.h>
#include <proto/proto_tcp.h>
#include <proto/proxy.h>
#include <proto/stick_table.h>
#include <proto/stream_sock.h>
#include <proto/task.h>
#ifdef CONFIG_HAP_CTTPROXY
#include <import/ip_tproxy.h>
@ -1018,6 +1020,87 @@ pattern_fetch_dport(struct proxy *px, struct session *l4, void *l7, int dir,
return 1;
}
/* set test->i to the number of connections from the session's source address
* in the table pointed to by expr.
*/
static int
acl_fetch_src_count(struct proxy *px, struct session *l4, void *l7, int dir,
struct acl_expr *expr, struct acl_test *test)
{
struct stksess *ts;
/* right now we only support IPv4 */
if (l4->cli_addr.ss_family != AF_INET)
return 0;
if (expr->arg_len) {
/* another table was designated, we must look for it */
for (px = proxy; px; px = px->next)
if (strcmp(px->id, expr->arg.str) == 0)
break;
}
if (!px)
return 0; /* table not found */
static_table_key.key = (void *)&((struct sockaddr_in *)&l4->frt_addr)->sin_addr;
test->flags = ACL_TEST_F_VOL_TEST;
test->i = 0;
if ((ts = stktable_lookup_key(&px->table, &static_table_key)) != NULL) {
void *ptr = stktable_data_ptr(&px->table, ts, STKTABLE_DT_CONN_CUM);
if (!ptr)
return 0; /* parameter not stored */
test->i = stktable_data_cast(ptr, conn_cum);
}
return 1;
}
/* set test->i to the number of connections from the session's source address
* in the table pointed to by expr, after updating it.
*/
static int
acl_fetch_src_update_count(struct proxy *px, struct session *l4, void *l7, int dir,
struct acl_expr *expr, struct acl_test *test)
{
struct stksess *ts;
void *ptr;
/* right now we only support IPv4 */
if (l4->cli_addr.ss_family != AF_INET)
return 0;
if (expr->arg_len) {
/* another table was designated, we must look for it */
for (px = proxy; px; px = px->next)
if (strcmp(px->id, expr->arg.str) == 0)
break;
}
if (!px)
return 0;
static_table_key.key = (void *)&((struct sockaddr_in *)&l4->frt_addr)->sin_addr;
if ((ts = stktable_lookup_key(&px->table, &static_table_key)) == NULL) {
/* entry does not exist, initialize a new one */
ts = stksess_new(&px->table, &static_table_key);
if (!ts)
return 0;
stktable_store(&px->table, ts);
}
else if (px->table.expire) {
/* if entries can expire, let's update the entry and the table */
ts->expire = tick_add(now_ms, MS_TO_TICKS(px->table.expire));
px->table.exp_task->expire = px->table.exp_next = tick_first(ts->expire, px->table.exp_next);
task_queue(px->table.exp_task);
}
ptr = stktable_data_ptr(&px->table, ts, STKTABLE_DT_CONN_CUM);
if (!ptr)
return 0; /* parameter not stored in this table */
test->i = ++stktable_data_cast(ptr, conn_cum);
test->flags = ACL_TEST_F_VOL_TEST;
return 1;
}
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
@ -1030,6 +1113,8 @@ static struct acl_kw_list acl_kws = {{ },{
{ "src", acl_parse_ip, acl_fetch_src, acl_match_ip, ACL_USE_TCP4_PERMANENT|ACL_MAY_LOOKUP },
{ "dst", acl_parse_ip, acl_fetch_dst, acl_match_ip, ACL_USE_TCP4_PERMANENT|ACL_MAY_LOOKUP },
{ "dst_port", acl_parse_int, acl_fetch_dport, acl_match_int, ACL_USE_TCP_PERMANENT },
{ "src_count", acl_parse_int, acl_fetch_src_count,acl_match_int, ACL_USE_TCP4_PERMANENT },
{ "src_update_count", acl_parse_int, acl_fetch_src_update_count, acl_match_int, ACL_USE_TCP4_PERMANENT },
{ NULL, NULL, NULL, NULL },
}};