diff options
author | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2004-12-04 07:58:53 +0000 |
---|---|---|
committer | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2004-12-04 07:58:53 +0000 |
commit | 6e2983ee29b875f74747069a65acc24526df1ad5 (patch) | |
tree | 09f397b8e05541bae0974d2ea6a1149d1339a7f6 /sbin | |
parent | 9c0396be58b3f3da3d725d94639fa5c35aa98e44 (diff) |
Userland support for limiting open tcp connections per source. eg:
keep state (max-src-conn 1000, max-src-conn-rate 100/10, overflow <bad> flush)
allow a maximum of 1000 open connections or 100 new connections in 10 seconds.
The addresses of offenders are added to the <bad> table which can be used in
the ruleset, and existing states from that host are flushed.
ok deraadt@ dhartmei@
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/pfctl/parse.y | 148 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.c | 42 |
2 files changed, 182 insertions, 8 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 749e82d009e..e3a44adbd00 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.460 2004/09/21 16:59:11 aaron Exp $ */ +/* $OpenBSD: parse.y,v 1.461 2004/12/04 07:58:51 mcbride Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -118,8 +118,10 @@ struct node_icmp { }; enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, - PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_NODES, - PF_STATE_OPT_STATELOCK, PF_STATE_OPT_TIMEOUT }; + PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN, + PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES, + PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK, + PF_STATE_OPT_TIMEOUT }; enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE }; @@ -128,6 +130,16 @@ struct node_state_opt { union { u_int32_t max_states; u_int32_t max_src_states; + u_int32_t max_src_conn; + struct { + u_int32_t limit; + u_int32_t seconds; + + } max_src_conn_rate; + struct { + u_int8_t flush; + char tblname[PF_TABLE_NAME_SIZE]; + } overload; u_int32_t max_src_nodes; u_int8_t src_track; u_int32_t statelock; @@ -393,13 +405,14 @@ typedef struct { %token QUEUE PRIORITY QLIMIT %token LOAD %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE +%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH %token TAGGED TAG IFBOUND GRBOUND FLOATING STATEPOLICY %token <v.string> STRING %token <v.i> PORTBINARY %type <v.interface> interface if_list if_item_not if_item %type <v.number> number icmptype icmp6type uid gid %type <v.number> tos not yesno natpass -%type <v.i> no dir log af fragcache sourcetrack +%type <v.i> no dir log af fragcache sourcetrack flush %type <v.i> unaryop statelock %type <v.b> action nataction scrubaction %type <v.b> flags flag blockspec @@ -1577,7 +1590,7 @@ pfrule : action dir logquick interface route af proto fromto "multiple definitions"); YYERROR; } - if (o->data.max_src_nodes == 0) { + if (o->data.max_src_states == 0) { yyerror("'max-src-states' must " "be > 0"); YYERROR; @@ -1586,6 +1599,68 @@ pfrule : action dir logquick interface route af proto fromto o->data.max_src_states; r.rule_flag |= PFRULE_SRCTRACK; break; + case PF_STATE_OPT_OVERLOAD: + if (r.overload_tblname[0]) { + yyerror("multiple 'overload' " + "table definitions"); + YYERROR; + } + if (strlcpy(r.overload_tblname, + o->data.overload.tblname, + PF_TABLE_NAME_SIZE) >= + PF_TABLE_NAME_SIZE) { + yyerror("state option: " + "strlcpy"); + YYERROR; + } + if (o->data.overload.flush) + r.rule_flag |= + PFRULE_SRCTRACK_FLUSH; + break; + case PF_STATE_OPT_MAX_SRC_CONN: + if (r.max_src_conn) { + yyerror("state option " + "'max-src-conn' " + "multiple definitions"); + YYERROR; + } + if (o->data.max_src_conn == 0) { + yyerror("'max-src-conn' " + "must be > 0"); + YYERROR; + } + r.max_src_conn = + o->data.max_src_conn; + r.rule_flag |= PFRULE_SRCTRACK | + PFRULE_RULESRCTRACK; + break; + case PF_STATE_OPT_MAX_SRC_CONN_RATE: + if (r.max_src_conn_rate.limit) { + yyerror("state option " + "'max-src-conn-rate' " + "multiple definitions"); + YYERROR; + } + if (!o->data.max_src_conn_rate.limit || + !o->data.max_src_conn_rate.seconds) { + yyerror("'max-src-conn-rate' " + "values must be > 0"); + YYERROR; + } + if (o->data.max_src_conn_rate.limit > + PF_THRESHOLD_MAX) { + yyerror("'max-src-conn-rate' " + "maximum rate must be < %u", + PF_THRESHOLD_MAX); + YYERROR; + } + r.max_src_conn_rate.limit = + o->data.max_src_conn_rate.limit; + r.max_src_conn_rate.seconds = + o->data.max_src_conn_rate.seconds; + r.rule_flag |= PFRULE_SRCTRACK | + PFRULE_RULESRCTRACK; + break; case PF_STATE_OPT_MAX_SRC_NODES: if (r.max_src_nodes) { yyerror("state option " @@ -1626,7 +1701,7 @@ pfrule : action dir logquick interface route af proto fromto o = o->next; free(p); } - if (srctrack) { + if (r.rule_flag & PFRULE_SRCTRACK) { if (srctrack == PF_SRCTRACK_GLOBAL && r.max_src_nodes) { yyerror("'max-src-nodes' is " @@ -1634,6 +1709,24 @@ pfrule : action dir logquick interface route af proto fromto "'source-track global'"); YYERROR; } + if (srctrack == PF_SRCTRACK_GLOBAL && + r.max_src_nodes) { + yyerror("'max-src-conn' is " + "incompatible with " + "'source-track global'"); + YYERROR; + } + if (srctrack == PF_SRCTRACK_GLOBAL && + r.max_src_nodes) { + yyerror("'max-src-conn-rate' is " + "incompatible with " + "'source-track global'"); + YYERROR; + } + if (r.timeout[PFTM_SRC_NODE] < + r.max_src_conn_rate.seconds) + r.timeout[PFTM_SRC_NODE] = + r.max_src_conn_rate.seconds; r.rule_flag |= PFRULE_SRCTRACK; if (srctrack == PF_SRCTRACK_RULE) r.rule_flag |= PFRULE_RULESRCTRACK; @@ -2713,6 +2806,10 @@ keep : KEEP STATE state_opt_spec { } ; +flush : /* empty */ { $$ = 0; } + | FLUSH { $$ = 1; } + ; + state_opt_spec : '(' state_opt_list ')' { $$ = $2; } | /* empty */ { $$ = NULL; } ; @@ -2751,6 +2848,41 @@ state_opt_item : MAXIMUM number { $$->next = NULL; $$->tail = $$; } + | MAXSRCCONN number { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_SRC_CONN; + $$->data.max_src_conn = $2; + $$->next = NULL; + $$->tail = $$; + } + | MAXSRCCONNRATE number '/' number { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_SRC_CONN_RATE; + $$->data.max_src_conn_rate.limit = $2; + $$->data.max_src_conn_rate.seconds = $4; + } + | OVERLOAD '<' STRING '>' flush { + if (strlen($3) >= PF_TABLE_NAME_SIZE) { + yyerror("table name '%s' too long", $3); + free($3); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + if (strlcpy($$->data.overload.tblname, $3, + PF_TABLE_NAME_SIZE) >= PF_TABLE_NAME_SIZE) + errx(1, "state_opt_item: strlcpy"); + free($3); + $$->type = PF_STATE_OPT_OVERLOAD; + $$->data.overload.flush = $5; + $$->next = NULL; + $$->tail = $$; + } | MAXSRCNODES number { $$ = calloc(1, sizeof(struct node_state_opt)); if ($$ == NULL) @@ -4340,6 +4472,7 @@ lookup(char *s) { "fingerprints", FINGERPRINTS}, { "flags", FLAGS}, { "floating", FLOATING}, + { "flush", FLUSH}, { "for", FOR}, { "fragment", FRAGMENT}, { "from", FROM}, @@ -4364,6 +4497,8 @@ lookup(char *s) { "loginterface", LOGINTERFACE}, { "max", MAXIMUM}, { "max-mss", MAXMSS}, + { "max-src-conn", MAXSRCCONN}, + { "max-src-conn-rate", MAXSRCCONNRATE}, { "max-src-nodes", MAXSRCNODES}, { "max-src-states", MAXSRCSTATES}, { "min-ttl", MINTTL}, @@ -4378,6 +4513,7 @@ lookup(char *s) { "optimization", OPTIMIZATION}, { "os", OS}, { "out", OUT}, + { "overload", OVERLOAD}, { "pass", PASS}, { "port", PORT}, { "priority", PRIORITY}, diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index b978cae112e..419bbbb6170 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfctl_parser.c,v 1.207 2004/11/09 11:26:04 dhartmei Exp $ */ +/* $OpenBSD: pfctl_parser.c,v 1.208 2004/12/04 07:58:52 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -34,6 +34,8 @@ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> +#include <sys/param.h> +#include <sys/proc.h> #include <net/if.h> #include <netinet/in.h> #include <netinet/in_systm.h> @@ -470,6 +472,7 @@ print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2, } const char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES; +const char *pf_lcounters[LCNT_MAX+1] = LCNT_NAMES; const char *pf_fcounters[FCNT_MAX+1] = FCNT_NAMES; const char *pf_scounters[FCNT_MAX+1] = FCNT_NAMES; @@ -572,6 +575,18 @@ print_status(struct pf_status *s, int opts) else printf("%14s\n", ""); } + if (opts & PF_OPT_VERBOSE) { + printf("Limit Counters\n"); + for (i = 0; i < LCNT_MAX; i++) { + printf(" %-25s %14lld ", pf_lcounters[i], + s->lcounters[i]); + if (runtime > 0) + printf("%14.1f/s\n", + (double)s->lcounters[i] / (double)runtime); + else + printf("%14s\n", ""); + } + } } void @@ -591,7 +606,9 @@ print_src_node(struct pf_src_node *sn, int opts) printf(" -> "); aw.v.a.addr = sn->raddr; print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2); - printf(" (%d states)\n", sn->states); + printf(" ( states %u, connections %u, rate %u.%u/%us )\n", sn->states, + sn->conn, sn->conn_rate.count / 1000, + (sn->conn_rate.count % 1000) / 100, sn->conn_rate.seconds); if (opts & PF_OPT_VERBOSE) { sec = sn->creation % 60; sn->creation /= 60; @@ -836,12 +853,33 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose) printf("max-src-states %u", r->max_src_states); opts = 0; } + if (r->max_src_conn) { + if (!opts) + printf(", "); + printf("max-src-conn %u", r->max_src_conn); + opts = 0; + } + if (r->max_src_conn_rate.limit) { + if (!opts) + printf(", "); + printf("max-src-conn-rate %u/%u", + r->max_src_conn_rate.limit, + r->max_src_conn_rate.seconds); + opts = 0; + } if (r->max_src_nodes) { if (!opts) printf(", "); printf("max-src-nodes %u", r->max_src_nodes); opts = 0; } + if (r->overload_tblname[0]) { + if (!opts) + printf(", "); + printf("overload <%s>", r->overload_tblname); + if (r->rule_flag & PFRULE_SRCTRACK_FLUSH) + printf(" flush"); + } if (r->rule_flag & PFRULE_IFBOUND) { if (!opts) printf(", "); |