summaryrefslogtreecommitdiff
path: root/sbin/pfctl/parse.y
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/pfctl/parse.y
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/pfctl/parse.y')
-rw-r--r--sbin/pfctl/parse.y148
1 files changed, 142 insertions, 6 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},