summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorRyan Thomas McBride <mcbride@cvs.openbsd.org>2004-12-04 07:58:53 +0000
committerRyan Thomas McBride <mcbride@cvs.openbsd.org>2004-12-04 07:58:53 +0000
commit6e2983ee29b875f74747069a65acc24526df1ad5 (patch)
tree09f397b8e05541bae0974d2ea6a1149d1339a7f6 /sbin
parent9c0396be58b3f3da3d725d94639fa5c35aa98e44 (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.y148
-rw-r--r--sbin/pfctl/pfctl_parser.c42
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(", ");