summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Thomas McBride <mcbride@cvs.openbsd.org>2003-12-15 00:02:05 +0000
committerRyan Thomas McBride <mcbride@cvs.openbsd.org>2003-12-15 00:02:05 +0000
commitbcbefdbeb6961a98675a03e10371e908592d2742 (patch)
tree44fc5938d025a365526a21723a1004d25f125611
parent7177de71616eff6b6f4d44f5b1c99fe17c82545e (diff)
Add support to track stateful connections by source ip. This allows us
to: - Ensure that clients get a consistent IP mapping with load-balanced translation/routing rules - Limit the number of simultaneous connections a client can make - Limit the number of clients which can connect through a rule ok dhartmei@ deraadt@
-rw-r--r--sbin/pfctl/parse.y253
-rw-r--r--sbin/pfctl/pf_print_state.c7
-rw-r--r--sbin/pfctl/pfctl.89
-rw-r--r--sbin/pfctl/pfctl.c79
-rw-r--r--sbin/pfctl/pfctl_parser.c99
-rw-r--r--sbin/pfctl/pfctl_parser.h5
-rw-r--r--share/man/man4/pf.458
-rw-r--r--share/man/man5/pf.conf.551
-rw-r--r--sys/net/pf.c661
-rw-r--r--sys/net/pf_ioctl.c97
-rw-r--r--sys/net/pfvar.h62
11 files changed, 1116 insertions, 265 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 93a76bc77b3..5fa0985374d 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.424 2003/11/29 10:05:55 dhartmei Exp $ */
+/* $OpenBSD: parse.y,v 1.425 2003/12/15 00:02:03 mcbride Exp $ */
/*
* Copyright (c) 2001 Markus Friedl. All rights reserved.
@@ -112,11 +112,17 @@ struct node_icmp {
struct node_icmp *tail;
};
-enum { PF_STATE_OPT_MAX=0, PF_STATE_OPT_NOSYNC=1, PF_STATE_OPT_TIMEOUT=2 };
+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_TIMEOUT };
+
struct node_state_opt {
int type;
union {
u_int32_t max_states;
+ u_int32_t max_src_states;
+ u_int32_t max_src_nodes;
+ u_int8_t src_track;
struct {
int number;
u_int32_t seconds;
@@ -151,6 +157,7 @@ struct filter_opts {
#define FOM_ICMP 0x02
#define FOM_TOS 0x04
#define FOM_KEEP 0x08
+#define FOM_SRCTRACK 0x10
struct node_uid *uid;
struct node_gid *gid;
struct {
@@ -211,6 +218,18 @@ struct table_opts {
struct node_tinithead init_nodes;
} table_opts;
+struct pool_opts {
+ int marker;
+#define POM_TYPE 0x01
+#define POM_STICKYADDRESS 0x02
+ u_int8_t opts;
+ int type;
+ int staticport;
+ struct pf_poolhashkey *key;
+
+} pool_opts;
+
+
struct node_hfsc_opts hfsc_opts;
int yyerror(const char *, ...);
@@ -308,7 +327,6 @@ typedef struct {
struct peer src, dst;
struct node_os *src_os;
} fromto;
- struct pf_poolhashkey *hashkey;
struct {
struct node_host *host;
u_int8_t rt;
@@ -321,10 +339,6 @@ typedef struct {
struct range rport;
} *redirection;
struct {
- int type;
- struct pf_poolhashkey *key;
- } pooltype;
- struct {
int action;
struct node_state_opt *options;
} keep_state;
@@ -332,6 +346,7 @@ typedef struct {
u_int8_t log;
u_int8_t quick;
} logquick;
+ struct pf_poolhashkey *hashkey;
struct node_queue *queue;
struct node_queue_opt queue_options;
struct node_queue_bw queue_bwspec;
@@ -341,6 +356,7 @@ typedef struct {
struct queue_opts queue_opts;
struct scrub_opts scrub_opts;
struct table_opts table_opts;
+ struct pool_opts pool_opts;
struct node_hfsc_opts hfsc_opts;
} v;
int lineno;
@@ -373,18 +389,18 @@ typedef struct {
%token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
%token QUEUE PRIORITY QLIMIT
%token LOAD
+%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
%token TAGGED TAG
%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
-%type <v.i> staticport unaryop
+%type <v.i> no dir log af fragcache sourcetrack
+%type <v.i> unaryop
%type <v.b> action nataction flags flag blockspec
%type <v.range> port rport
%type <v.hashkey> hashkey
-%type <v.pooltype> pooltype
%type <v.proto> proto proto_list proto_item
%type <v.icmp> icmpspec
%type <v.icmp> icmp_list icmp_item
@@ -417,6 +433,7 @@ typedef struct {
%type <v.queue_opts> queue_opts queue_opt queue_opts_l
%type <v.scrub_opts> scrub_opts scrub_opt scrub_opts_l
%type <v.table_opts> table_opts table_opt table_opts_l
+%type <v.pool_opts> pool_opts pool_opt pool_opts_l
%%
ruleset : /* empty */
@@ -878,6 +895,7 @@ antispoof_opt : label {
not : '!' { $$ = 1; }
| /* empty */ { $$ = 0; }
+ ;
tabledef : TABLE '<' STRING '>' table_opts {
struct node_host *h, *nh;
@@ -1141,6 +1159,7 @@ bandwidth : STRING {
}
$$.bw_absolute = (u_int32_t)bps;
}
+ ;
scheduler : CBQ {
$$.qtype = ALTQT_CBQ;
@@ -1329,6 +1348,7 @@ pfrule : action dir logquick interface route af proto fromto
struct pf_rule r;
struct node_state_opt *o;
struct node_proto *proto;
+ int srctrack = 0;
if (check_rulestate(PFCTL_STATE_FILTER))
YYERROR;
@@ -1425,6 +1445,38 @@ pfrule : action dir logquick interface route af proto fromto
}
r.rule_flag |= PFRULE_NOSYNC;
break;
+ case PF_STATE_OPT_SRCTRACK:
+ if (srctrack) {
+ yyerror("state option "
+ "'source-track' "
+ "multiple definitons");
+ YYERROR;
+ }
+ srctrack = 1;
+ r.rule_flag |= o->data.src_track;
+ break;
+ case PF_STATE_OPT_MAX_SRC_STATES:
+ if (r.max_src_states) {
+ yyerror("state option "
+ "'max-src-states' "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.max_src_states =
+ o->data.max_src_states;
+ r.rule_flag |= PFRULE_SRCTRACK;
+ break;
+ case PF_STATE_OPT_MAX_SRC_NODES:
+ if (r.max_src_nodes) {
+ yyerror("state option "
+ "'max-src-nodes' "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.max_src_nodes =
+ o->data.max_src_nodes;
+ r.rule_flag |= PFRULE_SRCTRACK;
+ break;
case PF_STATE_OPT_TIMEOUT:
if (r.timeout[o->data.timeout.number]) {
yyerror("state timeout %s "
@@ -1467,17 +1519,17 @@ pfrule : action dir logquick interface route af proto fromto
"matching address family found.");
YYERROR;
}
- if (r.rpool.opts == PF_POOL_NONE && (
- $5.host->next != NULL ||
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) ==
+ PF_POOL_NONE && ($5.host->next != NULL ||
$5.host->addr.type == PF_ADDR_TABLE))
r.rpool.opts = PF_POOL_ROUNDROBIN;
- if (r.rpool.opts != PF_POOL_ROUNDROBIN)
- if (disallow_table($5.host, "tables "
- "are only supported in round-robin "
- "routing pools"))
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN &&
+ disallow_table($5.host, "tables are only "
+ "supported in round-robin routing pools"))
YYERROR;
if ($5.host->next != NULL) {
- if (r.rpool.opts !=
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
PF_POOL_ROUNDROBIN) {
yyerror("r.rpool.opts must "
"be PF_POOL_ROUNDROBIN");
@@ -1720,6 +1772,7 @@ if_item : STRING {
af : /* empty */ { $$ = 0; }
| INET { $$ = AF_INET; }
| INET6 { $$ = AF_INET6; }
+ ;
proto : /* empty */ { $$ = NULL; }
| PROTO proto_item { $$ = $2; }
@@ -2380,11 +2433,22 @@ tos : TOS STRING {
}
;
+sourcetrack : SOURCETRACK {
+ $$ = PFRULE_SRCTRACK;
+ }
+ | SOURCETRACK GLOBAL {
+ $$ = PFRULE_SRCTRACK;
+ }
+ | SOURCETRACK RULE {
+ $$ = PFRULE_SRCTRACK ^ PFRULE_RULESRCTRACK;
+ }
+ ;
+
keep : KEEP STATE state_opt_spec {
$$.action = PF_STATE_NORMAL;
$$.options = $3;
}
- | MODULATE STATE state_opt_spec {
+ | MODULATE STATE state_opt_spec {
$$.action = PF_STATE_MODULATE;
$$.options = $3;
}
@@ -2423,6 +2487,33 @@ state_opt_item : MAXIMUM number {
$$->next = NULL;
$$->tail = $$;
}
+ | MAXSRCSTATES number {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_STATES;
+ $$->data.max_src_states = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | MAXSRCNODES number {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_NODES;
+ $$->data.max_src_nodes = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | sourcetrack {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_SRCTRACK;
+ $$->data.src_track = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
| STRING number {
int i;
@@ -2511,14 +2602,14 @@ redir_host_list : host { $$ = $1; }
;
redirpool : /* empty */ { $$ = NULL; }
- | ARROW redirspec {
+ | ARROW redirspec {
$$ = calloc(1, sizeof(struct redirection));
if ($$ == NULL)
err(1, "redirection: calloc");
$$->host = $2;
$$->rport.a = $$->rport.b = $$->rport.t = 0;
}
- | ARROW redirspec PORT rport {
+ | ARROW redirspec PORT rport {
$$ = calloc(1, sizeof(struct redirection));
if ($$ == NULL)
err(1, "redirection: calloc");
@@ -2574,35 +2665,63 @@ hashkey : /* empty */
}
;
-pooltype : /* empty */
- {
- $$.type = PF_POOL_NONE;
- $$.key = NULL;
+pool_opts : { bzero(&pool_opts, sizeof pool_opts); }
+ pool_opts_l
+ { $$ = pool_opts; }
+ | /* empty */ {
+ bzero(&pool_opts, sizeof pool_opts);
+ $$ = pool_opts;
}
- | BITMASK
- {
- $$.type = PF_POOL_BITMASK;
- $$.key = NULL;
+ ;
+
+pool_opts_l : pool_opts_l pool_opt
+ | pool_opt
+ ;
+
+pool_opt : BITMASK {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_BITMASK;
}
- | RANDOM
- {
- $$.type = PF_POOL_RANDOM;
- $$.key = NULL;
+ | RANDOM {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_RANDOM;
}
- | SOURCEHASH hashkey
- {
- $$.type = PF_POOL_SRCHASH;
- $$.key = $2;
+ | SOURCEHASH hashkey {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_SRCHASH;
+ pool_opts.key = $2;
}
- | ROUNDROBIN
- {
- $$.type = PF_POOL_ROUNDROBIN;
- $$.key = NULL;
+ | ROUNDROBIN {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_ROUNDROBIN;
+ }
+ | STATICPORT {
+ if (pool_opts.staticport) {
+ yyerror("static-port cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.staticport = 1;
+ }
+ | STICKYADDRESS {
+ if (filter_opts.marker & POM_STICKYADDRESS) {
+ yyerror("sticky-address cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.marker |= POM_STICKYADDRESS;
+ pool_opts.opts |= PF_POOL_STICKYADDR;
}
- ;
-
-staticport : /* empty */ { $$ = 0; }
- | STATICPORT { $$ = 1; }
;
redirection : /* empty */ { $$ = NULL; }
@@ -2644,8 +2763,7 @@ nataction : no NAT natpass {
}
;
-natrule : nataction interface af proto fromto tag redirpool pooltype
- staticport
+natrule : nataction interface af proto fromto tag redirpool pool_opts
{
struct pf_rule r;
@@ -2727,15 +2845,17 @@ natrule : nataction interface af proto fromto tag redirpool pooltype
}
r.rpool.opts = $8.type;
- if (r.rpool.opts == PF_POOL_NONE)
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) ==
+ PF_POOL_NONE && ($7->host->next != NULL ||
+ $7->host->addr.type == PF_ADDR_TABLE))
r.rpool.opts = PF_POOL_ROUNDROBIN;
- if (r.rpool.opts != PF_POOL_ROUNDROBIN)
- if (disallow_table($7->host, "tables "
- "are only supported in round-robin "
- "redirection pools"))
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN &&
+ disallow_table($7->host, "tables are only "
+ "supported in round-robin redirction pools"))
YYERROR;
- if ($7->host->next) {
- if (r.rpool.opts !=
+ if ($7->host->next != NULL) {
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
PF_POOL_ROUNDROBIN) {
yyerror("only round-robin "
"valid for multiple "
@@ -2758,7 +2878,10 @@ natrule : nataction interface af proto fromto tag redirpool pooltype
memcpy(&r.rpool.key, $8.key,
sizeof(struct pf_poolhashkey));
- if ($9 != 0) {
+ if ($8.opts)
+ r.rpool.opts |= $8.opts;
+
+ if ($8.staticport) {
if (r.action != PF_NAT) {
yyerror("the 'static-port' option is "
"only valid with nat rules");
@@ -2924,6 +3047,7 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag
tag : /* empty */ { $$ = NULL; }
| TAG STRING { $$ = $2; }
+ ;
route_host : STRING {
struct node_host *n;
@@ -2985,24 +3109,24 @@ route : /* empty */ {
$$.rt = PF_FASTROUTE;
$$.pool_opts = 0;
}
- | ROUTETO routespec pooltype {
+ | ROUTETO routespec pool_opts {
$$.host = $2;
$$.rt = PF_ROUTETO;
- $$.pool_opts = $3.type;
+ $$.pool_opts = $3.type | $3.opts;
if ($3.key != NULL)
$$.key = $3.key;
}
- | REPLYTO routespec pooltype {
+ | REPLYTO routespec pool_opts {
$$.host = $2;
$$.rt = PF_REPLYTO;
- $$.pool_opts = $3.type;
+ $$.pool_opts = $3.type | $3.opts;
if ($3.key != NULL)
$$.key = $3.key;
}
- | DUPTO routespec pooltype {
+ | DUPTO routespec pool_opts {
$$.host = $2;
$$.rt = PF_DUPTO;
- $$.pool_opts = $3.type;
+ $$.pool_opts = $3.type | $3.opts;
if ($3.key != NULL)
$$.key = $3.key;
}
@@ -3016,6 +3140,7 @@ timeout_spec : STRING number
yyerror("unknown timeout %s", $1);
YYERROR;
}
+
}
;
@@ -3032,6 +3157,7 @@ limit_spec : STRING number
YYERROR;
}
}
+ ;
limit_list : limit_list comma limit_spec
| limit_spec
@@ -3048,6 +3174,7 @@ yesno : NO { $$ = 0; }
else
YYERROR;
}
+ ;
unaryop : '=' { $$ = PF_OP_EQ; }
| '!' '=' { $$ = PF_OP_NE; }
@@ -3939,6 +4066,7 @@ lookup(char *s)
{ "for", FOR},
{ "fragment", FRAGMENT},
{ "from", FROM},
+ { "global", GLOBAL},
{ "group", GROUP},
{ "hfsc", HFSC},
{ "icmp-type", ICMPTYPE},
@@ -3956,6 +4084,8 @@ lookup(char *s)
{ "loginterface", LOGINTERFACE},
{ "max", MAXIMUM},
{ "max-mss", MAXMSS},
+ { "max-src-nodes", MAXSRCNODES},
+ { "max-src-states", MAXSRCSTATES},
{ "min-ttl", MINTTL},
{ "modulate", MODULATE},
{ "nat", NAT},
@@ -3990,11 +4120,14 @@ lookup(char *s)
{ "return-rst", RETURNRST},
{ "round-robin", ROUNDROBIN},
{ "route-to", ROUTETO},
+ { "rule", RULE},
{ "scrub", SCRUB},
{ "set", SET},
{ "source-hash", SOURCEHASH},
+ { "source-track", SOURCETRACK},
{ "state", STATE},
{ "static-port", STATICPORT},
+ { "sticky-address", STICKYADDRESS},
{ "synproxy", SYNPROXY},
{ "table", TABLE},
{ "tag", TAG},
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
index 58ce23a9aee..22f1d0399f9 100644
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_print_state.c,v 1.33 2003/07/06 22:01:28 deraadt Exp $ */
+/* $OpenBSD: pf_print_state.c,v 1.34 2003/12/15 00:02:03 mcbride Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -256,6 +256,11 @@ print_state(struct pf_state *s, int opts)
printf(", anchor %u", s->anchor.nr);
if (s->rule.nr != -1)
printf(", rule %u", s->rule.nr);
+ if (s->src_node != NULL)
+ printf(", source-track");
+ if (s->nat_src_node != NULL)
+ printf(", sticky-address");
+ printf("\n");
printf("\n");
}
}
diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
index fb73ce222aa..4271e7ce716 100644
--- a/sbin/pfctl/pfctl.8
+++ b/sbin/pfctl/pfctl.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pfctl.8,v 1.102 2003/09/18 09:18:51 jmc Exp $
+.\" $OpenBSD: pfctl.8,v 1.103 2003/12/15 00:02:03 mcbride Exp $
.\"
.\" Copyright (c) 2001 Kjell Wooding. All rights reserved.
.\"
@@ -173,6 +173,8 @@ Flush the queue rules.
Flush the filter rules.
.It Fl F Ar state
Flush the state table (NAT and filter).
+.It Fl F Ar Sources
+Flush the source tracking table.
.It Fl F Ar info
Flush the filter information (statistics that are not bound to rules).
.It Fl F Ar Tables
@@ -261,8 +263,13 @@ is specified as well, the named rulesets currently loaded in the specified
anchor are shown instead.
.It Fl s Ar state
Show the contents of the state table.
+.It Fl s Ar Sources
+Show the contents of the source tracking table.
.It Fl s Ar info
Show filter information (statistics and counters).
+When used together with
+.Fl v ,
+source tracking statistics are also shown.
.It Fl s Ar labels
Show per-rule statistics (label, evaluations, packets, bytes) of
filter rules with labels, useful for accounting.
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index e615d24ea60..db3194958d4 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfctl.c,v 1.192 2003/11/14 13:51:09 henning Exp $ */
+/* $OpenBSD: pfctl.c,v 1.193 2003/12/15 00:02:03 mcbride Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -60,6 +60,7 @@ int pfctl_clear_stats(int, int);
int pfctl_clear_rules(int, int, char *, char *);
int pfctl_clear_nat(int, int, char *, char *);
int pfctl_clear_altq(int, int);
+int pfctl_clear_src_nodes(int, int);
int pfctl_clear_states(int, int);
int pfctl_kill_states(int, int);
int pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int,
@@ -67,8 +68,9 @@ int pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int,
void pfctl_print_rule_counters(struct pf_rule *, int);
int pfctl_show_rules(int, int, int, char *, char *);
int pfctl_show_nat(int, int, char *, char *);
+int pfctl_show_src_nodes(int, int);
int pfctl_show_states(int, u_int8_t, int);
-int pfctl_show_status(int);
+int pfctl_show_status(int, int);
int pfctl_show_timeouts(int);
int pfctl_show_limits(int);
int pfctl_debug(int, u_int32_t, int);
@@ -156,12 +158,13 @@ static const struct {
};
static const char *clearopt_list[] = {
- "nat", "queue", "rules", "state", "info", "Tables", "osfp", "all", NULL
+ "nat", "queue", "rules", "Sources",
+ "state", "info", "Tables", "osfp", "all", NULL
};
static const char *showopt_list[] = {
- "nat", "queue", "rules", "Anchors", "state", "info", "labels",
- "timeouts", "memory", "Tables", "osfp", "all", NULL
+ "nat", "queue", "rules", "Anchors", "Sources", "state", "info",
+ "labels", "timeouts", "memory", "Tables", "ospf", "all", NULL
};
static const char *tblcmdopt_list[] = {
@@ -346,6 +349,16 @@ pfctl_clear_altq(int dev, int opts)
}
int
+pfctl_clear_src_nodes(int dev, int opts)
+{
+ if (ioctl(dev, DIOCCLRSRCNODES))
+ err(1, "DIOCCLRSRCNODES");
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "source tracking entries cleared\n");
+ return (0);
+}
+
+int
pfctl_clear_states(int dev, int opts)
{
if (ioctl(dev, DIOCCLRSTATES))
@@ -702,6 +715,46 @@ pfctl_show_nat(int dev, int opts, char *anchorname, char *rulesetname)
}
int
+pfctl_show_src_nodes(int dev, int opts)
+{
+ struct pfioc_src_nodes psn;
+ struct pf_src_node *p;
+ char *inbuf = NULL, *newinbuf = NULL;
+ unsigned len = 0;
+ int i;
+
+ memset(&psn, 0, sizeof(psn));
+ for (;;) {
+ psn.psn_len = len;
+ if (len) {
+ newinbuf = realloc(inbuf, len);
+ if (newinbuf == NULL)
+ err(1, "realloc");
+ psn.psn_buf = inbuf = newinbuf;
+ }
+ if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) {
+ warn("DIOCGETSRCNODES");
+ return (-1);
+ }
+ if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len)
+ break;
+ if (len == 0 && psn.psn_len == 0)
+ return (0);
+ if (len == 0 && psn.psn_len != 0)
+ len = psn.psn_len;
+ if (psn.psn_len == 0)
+ return (0); /* no src_nodes */
+ len *= 2;
+ }
+ p = psn.psn_src_nodes;
+ for (i = 0; i < psn.psn_len; i += sizeof(*p)) {
+ print_src_node(p, opts);
+ p++;
+ }
+ return (0);
+}
+
+int
pfctl_show_states(int dev, u_int8_t proto, int opts)
{
struct pfioc_states ps;
@@ -743,7 +796,7 @@ pfctl_show_states(int dev, u_int8_t proto, int opts)
}
int
-pfctl_show_status(int dev)
+pfctl_show_status(int dev, int opts)
{
struct pf_status status;
@@ -751,7 +804,7 @@ pfctl_show_status(int dev)
warn("DIOCGETSTATUS");
return (-1);
}
- print_status(&status);
+ print_status(&status, opts);
return (0);
}
@@ -1518,8 +1571,11 @@ main(int argc, char *argv[])
case 's':
pfctl_show_states(dev, 0, opts);
break;
+ case 'S':
+ pfctl_show_src_nodes(dev, opts);
+ break;
case 'i':
- pfctl_show_status(dev);
+ pfctl_show_status(dev, opts);
break;
case 't':
pfctl_show_timeouts(dev);
@@ -1535,7 +1591,8 @@ main(int argc, char *argv[])
pfctl_show_nat(dev, opts, anchorname, rulesetname);
pfctl_show_altq(dev, opts, 0);
pfctl_show_states(dev, 0, opts);
- pfctl_show_status(dev);
+ pfctl_show_src_nodes(dev, opts);
+ pfctl_show_status(dev, opts);
pfctl_show_rules(dev, opts, 1, anchorname, rulesetname);
pfctl_show_timeouts(dev);
pfctl_show_limits(dev);
@@ -1566,6 +1623,9 @@ main(int argc, char *argv[])
case 's':
pfctl_clear_states(dev, opts);
break;
+ case 'S':
+ pfctl_clear_src_nodes(dev, opts);
+ break;
case 'i':
pfctl_clear_stats(dev, opts);
break;
@@ -1574,6 +1634,7 @@ main(int argc, char *argv[])
pfctl_clear_nat(dev, opts, anchorname, rulesetname);
pfctl_clear_altq(dev, opts);
pfctl_clear_states(dev, opts);
+ pfctl_clear_src_nodes(dev, opts);
pfctl_clear_stats(dev, opts);
pfctl_clear_tables(anchorname, rulesetname, opts);
pfctl_clear_fingerprints(dev, opts);
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 437e028cd28..7cb363c1de5 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfctl_parser.c,v 1.181 2003/11/14 15:32:33 henning Exp $ */
+/* $OpenBSD: pfctl_parser.c,v 1.182 2003/12/15 00:02:03 mcbride Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -193,6 +193,7 @@ const struct pf_timeout pf_timeouts[] = {
{ "interval", PFTM_INTERVAL },
{ "adaptive.start", PFTM_ADAPTIVE_START },
{ "adaptive.end", PFTM_ADAPTIVE_END },
+ { "src.track", PFTM_SRC_NODE },
{ NULL, 0 }
};
@@ -459,15 +460,18 @@ print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2,
printf(" round-robin");
break;
}
+ if (pool->opts & PF_POOL_STICKYADDR)
+ printf(" sticky-address");
if (id == PF_NAT && p1 == 0 && p2 == 0)
printf(" static-port");
}
const char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES;
const char *pf_fcounters[FCNT_MAX+1] = FCNT_NAMES;
+const char *pf_scounters[FCNT_MAX+1] = FCNT_NAMES;
void
-print_status(struct pf_status *s)
+print_status(struct pf_status *s, int opts)
{
char statline[80];
time_t runtime;
@@ -537,6 +541,20 @@ print_status(struct pf_status *s)
else
printf("%14s\n", "");
}
+ if (opts & PF_OPT_VERBOSE) {
+ printf("Source Tracking Table\n");
+ printf(" %-25s %14u %14s\n", "current entries",
+ s->src_nodes, "");
+ for (i = 0; i < SCNT_MAX; i++) {
+ printf(" %-25s %14lld ", pf_scounters[i],
+ s->scounters[i]);
+ if (runtime > 0)
+ printf("%14.1f/s\n",
+ (double)s->scounters[i] / (double)runtime);
+ else
+ printf("%14s\n", "");
+ }
+ }
printf("Counters\n");
for (i = 0; i < PFRES_MAX; i++) {
printf(" %-25s %14llu ", pf_reasons[i],
@@ -550,6 +568,57 @@ print_status(struct pf_status *s)
}
void
+print_src_node(struct pf_src_node *sn, int opts)
+{
+ struct pf_addr_wrap aw;
+ int min, sec;
+
+ memset(&aw, 0, sizeof(aw));
+ if (sn->af == AF_INET)
+ aw.v.a.mask.addr32[0] = 0xffffffff;
+ else
+ memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
+
+ aw.v.a.addr = sn->addr;
+ print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
+ printf(" -> ");
+ aw.v.a.addr = sn->raddr;
+ print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
+ printf(" (%d states)\n", sn->states);
+ if (opts & PF_OPT_VERBOSE) {
+ sec = sn->creation % 60;
+ sn->creation /= 60;
+ min = sn->creation % 60;
+ sn->creation /= 60;
+ printf(" age %.2u:%.2u:%.2u", sn->creation, min, sec);
+ if (sn->states == 0) {
+ sec = sn->expire % 60;
+ sn->expire /= 60;
+ min = sn->expire % 60;
+ sn->expire /= 60;
+ printf(", expires in %.2u:%.2u:%.2u",
+ sn->expire, min, sec);
+ }
+ printf(", %u pkts, %u bytes", sn->packets, sn->bytes);
+ switch (sn->ruletype) {
+ case PF_NAT:
+ if (sn->rule.nr != -1)
+ printf(", nat rule %u", sn->rule.nr);
+ break;
+ case PF_RDR:
+ if (sn->rule.nr != -1)
+ printf(", rdr rule %u", sn->rule.nr);
+ break;
+ case PF_PASS:
+ if (sn->rule.nr != -1)
+ printf(", filter rule %u", sn->rule.nr);
+ break;
+ }
+ printf("\n");
+ }
+}
+
+void
print_rule(struct pf_rule *r, int verbose)
{
static const char *actiontypes[] = { "pass", "block", "scrub", "nat",
@@ -705,10 +774,12 @@ print_rule(struct pf_rule *r, int verbose)
else if (r->keep_state == PF_STATE_SYNPROXY)
printf(" synproxy state");
opts = 0;
- if (r->max_states)
+ if (r->max_states || r->max_src_nodes || r->max_src_states)
opts = 1;
if (r->rule_flag & PFRULE_NOSYNC)
opts = 1;
+ if (r->rule_flag & PFRULE_SRCTRACK)
+ opts = 1;
for (i = 0; !opts && i < PFTM_MAX; ++i)
if (r->timeout[i])
opts = 1;
@@ -724,6 +795,28 @@ print_rule(struct pf_rule *r, int verbose)
printf("no-sync");
opts = 0;
}
+ if (r->rule_flag & PFRULE_SRCTRACK) {
+ if (!opts)
+ printf(", ");
+ printf("source-track");
+ if (r->rule_flag & PFRULE_RULESRCTRACK)
+ printf(" rule");
+ else
+ printf(" global");
+ opts = 0;
+ }
+ if (r->max_src_states) {
+ if (!opts)
+ printf(", ");
+ printf("max-src-states %u", r->max_src_states);
+ opts = 0;
+ }
+ if (r->max_src_nodes) {
+ if (!opts)
+ printf(", ");
+ printf("max-src-nodes %u", r->max_src_nodes);
+ opts = 0;
+ }
for (i = 0; i < PFTM_MAX; ++i)
if (r->timeout[i]) {
if (!opts)
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index ebe2c14eae1..c0a710c533e 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfctl_parser.h,v 1.69 2003/11/14 13:51:09 henning Exp $ */
+/* $OpenBSD: pfctl_parser.h,v 1.70 2003/12/15 00:02:03 mcbride Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -161,9 +161,10 @@ int parse_flags(char *);
int pfctl_load_anchors(int, int, struct pfr_buffer *);
void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int);
+void print_src_node(struct pf_src_node *, int);
void print_rule(struct pf_rule *, int);
void print_tabledef(const char *, int, int, struct node_tinithead *);
-void print_status(struct pf_status *);
+void print_status(struct pf_status *, int);
int eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *,
struct node_queue_opt *);
diff --git a/share/man/man4/pf.4 b/share/man/man4/pf.4
index bf5d7cdc0de..67dceff05d8 100644
--- a/share/man/man4/pf.4
+++ b/share/man/man4/pf.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pf.4,v 1.40 2003/10/04 17:18:56 mcbride Exp $
+.\" $OpenBSD: pf.4,v 1.41 2003/12/15 00:02:03 mcbride Exp $
.\"
.\" Copyright (C) 2001, Kjell Wooding. All rights reserved.
.\"
@@ -246,15 +246,17 @@ Specifies the interface for which statistics are accumulated.
.It Dv DIOCGETSTATUS Fa "struct pf_status"
.Bd -literal
struct pf_status {
- u_int64_t counters[PFRES_MAX];
- u_int64_t fcounters[FCNT_MAX];
- u_int64_t pcounters[2][2][3];
- u_int64_t bcounters[2][2];
- u_int32_t running;
- u_int32_t states;
- u_int32_t since;
- u_int32_t debug;
- char ifname[IFNAMSIZ];
+ u_int64_t counters[PFRES_MAX];
+ u_int64_t fcounters[FCNT_MAX];
+ u_int64_t scounters[SCNT_MAX];
+ u_int64_t pcounters[2][2][3];
+ u_int64_t bcounters[2][2];
+ u_int32_t running;
+ u_int32_t states;
+ u_int32_t src_nodes;
+ u_int32_t since;
+ u_int32_t debug;
+ char ifname[IFNAMSIZ];
};
.Ed
.Pp
@@ -638,6 +640,42 @@ The rest of the structure members will come back filled.
Get the whole list by repeatedly incrementing the
.Va fp_getnum
number until the ioctl returns EBUSY.
+.It Dv DIOCGETSRCNODES Fa "struct pfioc_src_nodes"
+.Bd -literal
+struct pfioc_src_nodes {
+ int psn_len;
+ union {
+ caddr_t psu_buf;
+ struct pf_src_node *psu_src_nodes;
+ } psn_u;
+#define psn_buf psn_u.psu_buf
+#define psn_src_nodes psn_u.psu_src_nodes
+};
+.Ed
+.Pp
+Get the list of source nodes kept by the
+.Ar sticky-address
+and
+.Ar source-track
+options.
+The ioctl must be called once with
+.Va psn_len
+set to 0,
+If the ioctl returns without error,
+.Va psn_len
+will be set to the size of the buffer required to hold all the
+.Va pf_src_node
+structures held in the table.
+A buffer of this size should then be allocated, and a pointer to this buffer
+placed in
+.Va psn_buf .
+The ioctl must then be called again to fill this buffer with the actual
+source node data.
+After the ioctl call
+.Va psn_len
+will be set to the length of the buffer actually used.
+.It Dv DIOCCLRSRCNODES Fa "struct pfioc_table"
+Clear the tree of source tracking nodes.
.El
.Sh EXAMPLES
The following example demonstrates how to use the DIOCNATLOOK command
diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
index 0e177a27b51..93e591f064e 100644
--- a/share/man/man5/pf.conf.5
+++ b/share/man/man5/pf.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pf.conf.5,v 1.284 2003/11/29 10:05:55 dhartmei Exp $
+.\" $OpenBSD: pf.conf.5,v 1.285 2003/12/15 00:02:03 mcbride Exp $
.\"
.\" Copyright (c) 2002, Daniel Hartmeier
.\" All rights reserved.
@@ -234,6 +234,9 @@ command.
Interval between purging expired states and fragments.
.It Ar frag
Seconds before an unassembled fragment is expired.
+.It Ar src.track
+Length of time to retain a source-tracking entry after the last state
+expires.
.El
.Pp
When a packet matches a stateful connection, the seconds to live for the
@@ -1560,6 +1563,24 @@ option prevents
.Xr pf 4
from modifying the source port on TCP and UDP packets.
.El
+.Pp
+Additionally, the
+.Ar sticky-address
+option can be specified to help ensure that multiple connections from the
+same source are mapped to the same redirection address. This option can be
+used with the
+.Ar random
+and
+.Ar round-robin
+pool options.
+Note that by default these associations are destroyed as soon as there are
+no longer states which refer to them; in order to make the mappings last
+beyond the lifetime of the states, increase the global options with
+.Ar set timeout source-track
+See
+.Sx STATEFUL TRACKING OPTIONS
+for more ways to control the source tracking.
+
.Sh STATEFUL INSPECTION
.Xr pf 4
is a stateful packet filter, which means it can track the state of
@@ -1763,17 +1784,31 @@ Prevent state changes for states created by this rule from appearing on the
interface.
.It Ar <timeout> <seconds>
Changes the timeout values used for states created by this rule.
+.Pp
+When the
+.Ar source-tracking
+keyword is specified, the number of states per source ip is tracked.
+The following limits can be set:
+.Pp
+.Bl -tag -width xxxx -compact
+.It Ar max-src-nodes
+Limits the maximum number of source addresses which can simultaneously
+have state table entries.
+.It Ar max-src-states
+Limits the maximum number of simultaneous state entries that a single
+source address can greate with this rule.
+.El
For a list of all valid timeout names, see
.Sx OPTIONS
above.
.Pp
Multiple options can be specified, separated by commas:
.Bd -literal
-pass in proto tcp from any to any \e
+pass in proto tcp from any to any
port www flags S/SA keep state \e
- (max 100, tcp.established 60, tcp.closing 5)
+ (max 100, source-track rule, max-src-nodes 75, \e
+ max-src-states 3, tcp.established 60, tcp.closing 5)
.Ed
-.El
.Sh OPERATING SYSTEM FINGERPRINTING
Passive OS Fingerprinting is a mechanism to inspect nuances of a TCP
connection's initial SYN packet and guess at the host's operating system.
@@ -2446,7 +2481,9 @@ tos = "tos" ( "lowdelay" | "throughput" | "reliability" |
[ "0x" ] number )
state-opts = state-opt [ [ "," ] state-opts ]
-state-opt = ( "max" number | "no-sync" | timeout )
+state-opt = ( "max" number | "no-sync" | timeout |
+ "source-track" [ ( "rule" | "global" ) ] |
+ "max-src-nodes" number | "max-src-states" number)
fragmentation = [ "fragment reassemble" | "fragment crop" |
"fragment drop-ovl" ]
@@ -2457,7 +2494,7 @@ timeout = ( "tcp.first" | "tcp.opening" | "tcp.established" |
"udp.first" | "udp.single" | "udp.multiple" |
"icmp.first" | "icmp.error" |
"other.first" | "other.single" | "other.multiple" |
- "frag" | "interval" |
+ "frag" | "interval" | "src.track" |
"adaptive.start" | "adaptive.end" ) number
limit-list = limit-item [ [ "," ] limit-list ]
@@ -2465,7 +2502,7 @@ limit-item = ( "states" | "frags" ) number
pooltype = ( "bitmask" | "random" |
"source-hash" [ ( hex-key | string-key ) ] |
- "round-robin" )
+ "round-robin" ) [ sticky-address ]
subqueue = string | "{" queue-list "}"
queue-list = string [ [ "," ] string ]
diff --git a/sys/net/pf.c b/sys/net/pf.c
index 7a82b833300..0392850a2fa 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.407 2003/12/12 20:05:45 cedric Exp $ */
+/* $OpenBSD: pf.c,v 1.408 2003/12/15 00:02:03 mcbride Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -107,7 +107,7 @@ u_int32_t ticket_pabuf;
struct timeout pf_expire_to; /* expire timeout */
-struct pool pf_rule_pl, pf_addr_pl;
+struct pool pf_src_tree_pl, pf_rule_pl, pf_addr_pl;
struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl;
void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t);
@@ -138,7 +138,7 @@ struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *,
struct pf_addr *, u_int16_t, struct pf_addr *,
u_int16_t, int);
struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *,
- int, int, struct ifnet *,
+ int, int, struct ifnet *, struct pf_src_node **,
struct pf_addr *, u_int16_t,
struct pf_addr *, u_int16_t,
struct pf_addr *, u_int16_t *);
@@ -175,16 +175,16 @@ int pf_test_state_other(struct pf_state **, int,
struct ifnet *, struct pf_pdesc *);
struct pf_tag *pf_get_tag(struct mbuf *);
int pf_match_tag(struct mbuf *, struct pf_rule *,
- struct pf_rule *, struct pf_rule *,
- struct pf_tag *, int *);
+ struct pf_rule *, struct pf_tag *, int *);
void pf_hash(struct pf_addr *, struct pf_addr *,
struct pf_poolhashkey *, sa_family_t);
-int pf_map_addr(u_int8_t, struct pf_pool *,
+int pf_map_addr(u_int8_t, struct pf_rule *,
struct pf_addr *, struct pf_addr *,
- struct pf_addr *);
-int pf_get_sport(sa_family_t, u_int8_t, struct pf_pool *,
+ struct pf_addr *, struct pf_src_node **);
+int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *,
struct pf_addr *, struct pf_addr *, u_int16_t,
- struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t);
+ struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t,
+ struct pf_src_node **);
void pf_route(struct mbuf **, struct pf_rule *, int,
struct ifnet *, struct pf_state *);
void pf_route6(struct mbuf **, struct pf_rule *, int,
@@ -235,20 +235,67 @@ struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] =
(s)->lan.addr.addr32[3] != (s)->gwy.addr.addr32[3])) || \
(s)->lan.port != (s)->gwy.port
+static __inline int pf_src_compare(struct pf_src_node *, struct pf_src_node *);
static __inline int pf_state_compare_lan_ext(struct pf_state *,
- struct pf_state *);
+ struct pf_state *);
static __inline int pf_state_compare_ext_gwy(struct pf_state *,
- struct pf_state *);
+ struct pf_state *);
+struct pf_src_tree tree_src_tracking;
struct pf_state_tree_lan_ext tree_lan_ext;
struct pf_state_tree_ext_gwy tree_ext_gwy;
+RB_GENERATE(pf_src_tree, pf_src_node, entry, pf_src_compare);
RB_GENERATE(pf_state_tree_lan_ext, pf_state,
entry_lan_ext, pf_state_compare_lan_ext);
RB_GENERATE(pf_state_tree_ext_gwy, pf_state,
entry_ext_gwy, pf_state_compare_ext_gwy);
static __inline int
+pf_src_compare(struct pf_src_node *a, struct pf_src_node *b)
+{
+ int diff;
+
+ if (a->rule.ptr > b->rule.ptr)
+ return (1);
+ if (a->rule.ptr < b->rule.ptr)
+ return (-1);
+ if ((diff = a->af - b->af) != 0)
+ return (diff);
+ switch (a->af) {
+#ifdef INET
+ case AF_INET:
+ if (a->addr.addr32[0] > b->addr.addr32[0])
+ return (1);
+ if (a->addr.addr32[0] < b->addr.addr32[0])
+ return (-1);
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ if (a->addr.addr32[3] > b->addr.addr32[3])
+ return (1);
+ if (a->addr.addr32[3] < b->addr.addr32[3])
+ return (-1);
+ if (a->addr.addr32[2] > b->addr.addr32[2])
+ return (1);
+ if (a->addr.addr32[2] < b->addr.addr32[2])
+ return (-1);
+ if (a->addr.addr32[1] > b->addr.addr32[1])
+ return (1);
+ if (a->addr.addr32[1] < b->addr.addr32[1])
+ return (-1);
+ if (a->addr.addr32[0] > b->addr.addr32[0])
+ return (1);
+ if (a->addr.addr32[0] < b->addr.addr32[0])
+ return (-1);
+ break;
+#endif /* INET6 */
+ }
+ return (0);
+}
+
+static __inline int
pf_state_compare_lan_ext(struct pf_state *a, struct pf_state *b)
{
int diff;
@@ -428,6 +475,61 @@ pf_find_state(struct pf_state *key, u_int8_t tree)
}
int
+pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule,
+ struct pf_addr *src, sa_family_t af)
+{
+ struct pf_src_node k;
+
+ if (*sn == NULL) {
+ k.af = af;
+ PF_ACPY(&k.addr, src, af);
+ if (rule->rule_flag & PFRULE_RULESRCTRACK ||
+ rule->rpool.opts & PF_POOL_STICKYADDR)
+ k.rule.ptr = rule;
+ else
+ k.rule.ptr = NULL;
+ pf_status.scounters[SCNT_SRC_NODE_SEARCH]++;
+ *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k);
+ }
+ if (*sn == NULL) {
+ if (!rule->max_src_nodes ||
+ rule->src_nodes < rule->max_src_nodes)
+ (*sn) = pool_get(&pf_src_tree_pl, PR_NOWAIT);
+ if ((*sn) == NULL)
+ return (-1);
+ bzero(*sn, sizeof(struct pf_src_node));
+ (*sn)->af = af;
+ if (rule->rule_flag & PFRULE_RULESRCTRACK ||
+ rule->rpool.opts & PF_POOL_STICKYADDR)
+ (*sn)->rule.ptr = rule;
+ else
+ (*sn)->rule.ptr = NULL;
+ PF_ACPY(&(*sn)->addr, src, af);
+ if (RB_INSERT(pf_src_tree,
+ &tree_src_tracking, *sn) != NULL) {
+ if (pf_status.debug >= PF_DEBUG_MISC) {
+ printf("pf: src_tree insert failed: ");
+ pf_print_host(&(*sn)->addr, 0, af);
+ printf("\n");
+ }
+ pool_put(&pf_src_tree_pl, *sn);
+ return (-1);
+ }
+ (*sn)->creation = time.tv_sec;
+ (*sn)->ruletype = rule->action;
+ if ((*sn)->rule.ptr != NULL)
+ (*sn)->rule.ptr->src_nodes++;
+ pf_status.scounters[SCNT_SRC_NODE_INSERT]++;
+ pf_status.src_nodes++;
+ } else {
+ if (rule->max_src_states &&
+ (*sn)->states >= rule->max_src_states)
+ return (-1);
+ }
+ return (0);
+}
+
+int
pf_insert_state(struct pf_state *state)
{
/* Thou MUST NOT insert multiple duplicate keys */
@@ -445,6 +547,7 @@ pf_insert_state(struct pf_state *state)
state->af);
printf("\n");
}
+ pf_src_tree_remove_state(state);
return (-1);
}
@@ -463,6 +566,7 @@ pf_insert_state(struct pf_state *state)
printf("\n");
}
RB_REMOVE(pf_state_tree_lan_ext, &tree_lan_ext, state);
+ pf_src_tree_remove_state(state);
return (-1);
}
@@ -483,6 +587,7 @@ pf_purge_timeout(void *arg)
s = splsoftnet();
pf_purge_expired_states();
pf_purge_expired_fragments();
+ pf_purge_expired_src_nodes();
splx(s);
timeout_add(to, pf_default_rule.timeout[PFTM_INTERVAL] * hz);
@@ -525,6 +630,52 @@ pf_state_expires(const struct pf_state *state)
}
void
+pf_purge_expired_src_nodes(void)
+{
+ struct pf_src_node *cur, *next;
+
+ for (cur = RB_MIN(pf_src_tree, &tree_src_tracking); cur; cur = next) {
+ next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur);
+
+ if (cur->states <= 0 && cur->expire <= time.tv_sec) {
+ if (cur->rule.ptr != NULL) {
+ cur->rule.ptr->src_nodes--;
+ if (cur->rule.ptr->states <= 0 &&
+ cur->rule.ptr->max_src_nodes <= 0)
+ pf_rm_rule(NULL, cur->rule.ptr);
+ }
+ RB_REMOVE(pf_src_tree, &tree_src_tracking, cur);
+ pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+ pf_status.src_nodes--;
+ pool_put(&pf_src_tree_pl, cur);
+ }
+ }
+}
+
+void
+pf_src_tree_remove_state(struct pf_state *s)
+{
+ u_int32_t timeout;
+
+ if (s->src_node != NULL) {
+ if (--s->src_node->states <= 0) {
+ timeout = s->rule.ptr->timeout[PFTM_SRC_NODE];
+ if (!timeout)
+ timeout = pf_default_rule.timeout[PFTM_SRC_NODE];
+ s->src_node->expire = time.tv_sec + timeout;
+ }
+ }
+ if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) {
+ if (--s->nat_src_node->states <= 0) {
+ timeout = s->rule.ptr->timeout[PFTM_SRC_NODE];
+ if (!timeout)
+ timeout = pf_default_rule.timeout[PFTM_SRC_NODE];
+ s->nat_src_node->expire = time.tv_sec + timeout;
+ }
+ }
+}
+
+void
pf_purge_expired_states(void)
{
struct pf_state *cur, *next;
@@ -545,10 +696,13 @@ pf_purge_expired_states(void)
#if NPFSYNC
pfsync_delete_state(cur);
#endif
- if (--cur->rule.ptr->states <= 0)
+ pf_src_tree_remove_state(cur);
+ if (--cur->rule.ptr->states <= 0 &&
+ cur->rule.ptr->src_nodes <= 0)
pf_rm_rule(NULL, cur->rule.ptr);
if (cur->nat_rule.ptr != NULL)
- if (--cur->nat_rule.ptr->states <= 0)
+ if (--cur->nat_rule.ptr->states <= 0 &&
+ cur->nat_rule.ptr->src_nodes <= 0)
pf_rm_rule(NULL, cur->nat_rule.ptr);
if (cur->anchor.ptr != NULL)
if (--cur->anchor.ptr->states <= 0)
@@ -1319,8 +1473,8 @@ pf_get_tag(struct mbuf *m)
}
int
-pf_match_tag(struct mbuf *m, struct pf_rule *r, struct pf_rule *nat,
- struct pf_rule *rdr, struct pf_tag *pftag, int *tag)
+pf_match_tag(struct mbuf *m, struct pf_rule *r, struct pf_rule *nat_rule,
+ struct pf_tag *pftag, int *tag)
{
if (*tag == -1) { /* find mbuf tag */
pftag = pf_get_tag(m);
@@ -1328,10 +1482,8 @@ pf_match_tag(struct mbuf *m, struct pf_rule *r, struct pf_rule *nat,
*tag = pftag->tag;
else
*tag = 0;
- if (nat != NULL && nat->tag)
- *tag = nat->tag;
- if (rdr != NULL && rdr->tag)
- *tag = rdr->tag;
+ if (nat_rule != NULL && nat_rule->tag)
+ *tag = nat_rule->tag;
}
return ((!r->match_tag_not && r->match_tag == *tag) ||
@@ -1415,7 +1567,7 @@ pf_poolmask(struct pf_addr *naddr, struct pf_addr *raddr,
}
void
-pf_addr_inc(struct pf_addr *addr, u_int8_t af)
+pf_addr_inc(struct pf_addr *addr, sa_family_t af)
{
switch (af) {
#ifdef INET
@@ -1504,13 +1656,39 @@ pf_hash(struct pf_addr *inaddr, struct pf_addr *hash,
}
int
-pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr,
- struct pf_addr *naddr, struct pf_addr *init_addr)
+pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
+ struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sn)
{
unsigned char hash[16];
- struct pf_addr *raddr;
- struct pf_addr *rmask;
- struct pf_pooladdr *acur = rpool->cur;
+ struct pf_pool *rpool = &r->rpool;
+ struct pf_addr *raddr = &rpool->cur->addr.v.a.addr;
+ struct pf_addr *rmask = &rpool->cur->addr.v.a.mask;
+ struct pf_pooladdr *acur = rpool->cur;
+ struct pf_src_node k;
+
+ if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR &&
+ (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
+ k.af = af;
+ PF_ACPY(&k.addr, saddr, af);
+ if (r->rule_flag & PFRULE_RULESRCTRACK ||
+ r->rpool.opts & PF_POOL_STICKYADDR)
+ k.rule.ptr = r;
+ else
+ k.rule.ptr = NULL;
+ pf_status.scounters[SCNT_SRC_NODE_SEARCH]++;
+ *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k);
+ if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) {
+ PF_ACPY(naddr, &(*sn)->raddr, af);
+ if (pf_status.debug >= PF_DEBUG_MISC) {
+ printf("pf_map_addr: src tracking maps ");
+ pf_print_host(&k.addr, 0, af);
+ printf(" to ");
+ pf_print_host(naddr, 0, af);
+ printf("\n");
+ }
+ return(0);
+ }
+ }
if (rpool->cur->addr.type == PF_ADDR_NOROUTE)
return (1);
@@ -1604,10 +1782,12 @@ pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr,
PF_AINC(&rpool->counter, af);
break;
}
+ if (*sn != NULL)
+ PF_ACPY(&(*sn)->raddr, naddr, af);
if (pf_status.debug >= PF_DEBUG_MISC &&
(rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
- printf("pf_map_addr: selected address: ");
+ printf("pf_map_addr: selected address ");
pf_print_host(naddr, 0, af);
printf("\n");
}
@@ -1616,16 +1796,17 @@ pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr,
}
int
-pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_pool *rpool,
+pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r,
struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport,
- struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high)
+ struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high,
+ struct pf_src_node **sn)
{
struct pf_state key;
struct pf_addr init_addr;
u_int16_t cut;
bzero(&init_addr, sizeof(init_addr));
- if (pf_map_addr(af, rpool, saddr, naddr, &init_addr))
+ if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn))
return (1);
do {
@@ -1683,10 +1864,10 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_pool *rpool,
}
}
- switch (rpool->opts & PF_POOL_TYPEMASK) {
+ switch (r->rpool.opts & PF_POOL_TYPEMASK) {
case PF_POOL_RANDOM:
case PF_POOL_ROUNDROBIN:
- if (pf_map_addr(af, rpool, saddr, naddr, &init_addr))
+ if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn))
return (1);
break;
case PF_POOL_NONE:
@@ -1771,7 +1952,7 @@ pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off,
struct pf_rule *
pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction,
- struct ifnet *ifp,
+ struct ifnet *ifp, struct pf_src_node **sn,
struct pf_addr *saddr, u_int16_t sport,
struct pf_addr *daddr, u_int16_t dport,
struct pf_addr *naddr, u_int16_t *nport)
@@ -1800,9 +1981,9 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction,
return (NULL);
break;
case PF_NAT:
- if (pf_get_sport(pd->af, pd->proto, &r->rpool, saddr,
+ if (pf_get_sport(pd->af, pd->proto, r, saddr,
daddr, dport, naddr, nport, r->rpool.proxy_port[0],
- r->rpool.proxy_port[1])) {
+ r->rpool.proxy_port[1], sn)) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: NAT proxy port allocation "
"(%u-%u) failed\n",
@@ -1837,7 +2018,7 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction,
}
break;
case PF_RDR: {
- if (pf_map_addr(r->af, &r->rpool, saddr, naddr, NULL))
+ if (pf_map_addr(r->af, r, saddr, naddr, NULL, sn))
return (NULL);
if (r->rpool.proxy_port[1]) {
@@ -2069,15 +2250,13 @@ pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr)
switch (s->af) {
#ifdef INET
case AF_INET:
- pf_map_addr(AF_INET, &r->rpool, saddr,
- &s->rt_addr, NULL);
+ pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, &s->nat_src_node);
s->rt_ifp = r->rpool.cur->ifp;
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
- pf_map_addr(AF_INET6, &r->rpool, saddr,
- &s->rt_addr, NULL);
+ pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, &s->nat_src_node);
s->rt_ifp = r->rpool.cur->ifp;
break;
#endif /* INET6 */
@@ -2089,7 +2268,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
struct ifnet *ifp, struct mbuf *m, int off, void *h,
struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm)
{
- struct pf_rule *nat = NULL, *rdr = NULL;
+ struct pf_rule *nr = NULL;
struct pf_addr *saddr = pd->src, *daddr = pd->dst;
struct tcphdr *th = pd->hdr.tcp;
u_int16_t bport, nport = 0;
@@ -2099,6 +2278,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
gid_t gid;
struct pf_rule *r, *a = NULL;
struct pf_ruleset *ruleset = NULL;
+ struct pf_src_node *nsn = NULL;
u_short reason;
int rewrite = 0;
struct pf_tag *pftag = NULL;
@@ -2110,30 +2290,30 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
if (direction == PF_OUT) {
bport = nport = th->th_sport;
/* check outgoing packet for BINAT/NAT */
- if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp,
+ if ((nr = pf_get_translation(pd, m, off, PF_OUT, ifp, &nsn,
saddr, th->th_sport, daddr, th->th_dport,
&pd->naddr, &nport)) != NULL) {
PF_ACPY(&pd->baddr, saddr, af);
pf_change_ap(saddr, &th->th_sport, pd->ip_sum,
&th->th_sum, &pd->naddr, nport, 0, af);
rewrite++;
- if (nat->natpass)
+ if (nr->natpass)
r = NULL;
- pd->nat_rule = nat;
+ pd->nat_rule = nr;
}
} else {
bport = nport = th->th_dport;
/* check incoming packet for BINAT/RDR */
- if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr,
- th->th_sport, daddr, th->th_dport,
+ if ((nr = pf_get_translation(pd, m, off, PF_IN, ifp, &nsn,
+ saddr, th->th_sport, daddr, th->th_dport,
&pd->naddr, &nport)) != NULL) {
PF_ACPY(&pd->baddr, daddr, af);
pf_change_ap(daddr, &th->th_dport, pd->ip_sum,
&th->th_sum, &pd->naddr, nport, 0, af);
rewrite++;
- if (rdr->natpass)
+ if (nr->natpass)
r = NULL;
- pd->nat_rule = rdr;
+ pd->nat_rule = nr;
}
}
@@ -2174,8 +2354,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
!pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1],
gid))
r = TAILQ_NEXT(r, entries);
- else if (r->match_tag &&
- !pf_match_tag(m, r, nat, rdr, pftag, &tag))
+ else if (r->match_tag && !pf_match_tag(m, r, nr, pftag, &tag))
r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
@@ -2217,14 +2396,16 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
(r->rule_flag & PFRULE_RETURNICMP) ||
(r->rule_flag & PFRULE_RETURN))) {
/* undo NAT changes, if they have taken place */
- if (nat != NULL) {
- pf_change_ap(saddr, &th->th_sport, pd->ip_sum,
- &th->th_sum, &pd->baddr, bport, 0, af);
- rewrite++;
- } else if (rdr != NULL) {
- pf_change_ap(daddr, &th->th_dport, pd->ip_sum,
- &th->th_sum, &pd->baddr, bport, 0, af);
- rewrite++;
+ if (nr != NULL) {
+ if (direction == PF_OUT) {
+ pf_change_ap(saddr, &th->th_sport, pd->ip_sum,
+ &th->th_sum, &pd->baddr, bport, 0, af);
+ rewrite++;
+ } else {
+ pf_change_ap(daddr, &th->th_dport, pd->ip_sum,
+ &th->th_sum, &pd->baddr, bport, 0, af);
+ rewrite++;
+ }
}
if (((r->rule_flag & PFRULE_RETURNRST) ||
(r->rule_flag & PFRULE_RETURN)) &&
@@ -2255,16 +2436,45 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
return (PF_DROP);
}
- if (r->keep_state || nat != NULL || rdr != NULL ||
+ if (r->keep_state || nr != NULL ||
(pd->flags & PFDESC_TCP_NORM)) {
/* create new state */
u_int16_t len;
struct pf_state *s = NULL;
+ struct pf_src_node *sn = NULL;
len = pd->tot_len - off - (th->th_off << 2);
- if (!r->max_states || r->states < r->max_states)
- s = pool_get(&pf_state_pl, PR_NOWAIT);
+
+ /* check maximums */
+ if (r->max_states && (r->states >= r->max_states))
+ goto cleanup;
+ /* src node for flter rule */
+ if ((r->rule_flag & PFRULE_SRCTRACK ||
+ r->rpool.opts & PF_POOL_STICKYADDR) &&
+ pf_insert_src_node(&sn, r, saddr, af) != 0)
+ goto cleanup;
+ /* src node for translation rule */
+ if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) &&
+ ((direction == PF_OUT &&
+ pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) ||
+ (pf_insert_src_node(&nsn, nr, saddr, af) != 0)))
+ goto cleanup;
+ s = pool_get(&pf_state_pl, PR_NOWAIT);
if (s == NULL) {
+cleanup:
+ if (sn != NULL && sn->states == 0 && sn->expire == 0) {
+ RB_REMOVE(pf_src_tree, &tree_src_tracking, sn);
+ pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+ pf_status.src_nodes--;
+ pool_put(&pf_src_tree_pl, sn);
+ }
+ if (nsn != sn && nsn != NULL && nsn->states == 0 &&
+ nsn->expire == 0) {
+ RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn);
+ pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+ pf_status.src_nodes--;
+ pool_put(&pf_src_tree_pl, nsn);
+ }
REASON_SET(&reason, PFRES_MEMORY);
return (PF_DROP);
}
@@ -2273,10 +2483,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
if (a != NULL)
a->states++;
s->rule.ptr = r;
- if (nat != NULL)
- s->nat_rule.ptr = nat;
- else
- s->nat_rule.ptr = rdr;
+ s->nat_rule.ptr = nr;
if (s->nat_rule.ptr != NULL)
s->nat_rule.ptr->states++;
s->anchor.ptr = a;
@@ -2290,7 +2497,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->gwy.port = th->th_sport; /* sport */
PF_ACPY(&s->ext.addr, daddr, af);
s->ext.port = th->th_dport;
- if (nat != NULL) {
+ if (nr != NULL) {
PF_ACPY(&s->lan.addr, &pd->baddr, af);
s->lan.port = bport;
} else {
@@ -2302,7 +2509,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->lan.port = th->th_dport;
PF_ACPY(&s->ext.addr, saddr, af);
s->ext.port = th->th_sport;
- if (rdr != NULL) {
+ if (nr != NULL) {
PF_ACPY(&s->gwy.addr, &pd->baddr, af);
s->gwy.port = bport;
} else {
@@ -2345,10 +2552,19 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->expire = time.tv_sec;
s->timeout = PFTM_TCP_FIRST_PACKET;
pf_set_rt_ifp(s, saddr);
-
+ if (sn != NULL) {
+ s->src_node = sn;
+ s->src_node->states++;
+ }
+ if (nsn != NULL) {
+ PF_ACPY(&nsn->raddr, &pd->naddr, af);
+ s->nat_src_node = nsn;
+ s->nat_src_node->states++;
+ }
if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m,
off, pd, th, &s->src, &s->dst)) {
REASON_SET(&reason, PFRES_MEMORY);
+ pf_src_tree_remove_state(s);
pool_put(&pf_state_pl, s);
return (PF_DROP);
}
@@ -2356,12 +2572,14 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
pf_normalize_tcp_stateful(m, off, pd, &reason, th, &s->src,
&s->dst, &rewrite)) {
pf_normalize_tcp_cleanup(s);
+ pf_src_tree_remove_state(s);
pool_put(&pf_state_pl, s);
return (PF_DROP);
}
if (pf_insert_state(s)) {
pf_normalize_tcp_cleanup(s);
REASON_SET(&reason, PFRES_MEMORY);
+ pf_src_tree_remove_state(s);
pool_put(&pf_state_pl, s);
return (PF_DROP);
} else
@@ -2369,14 +2587,17 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN &&
r->keep_state == PF_STATE_SYNPROXY) {
s->src.state = PF_TCPS_PROXY_SRC;
- if (nat != NULL)
- pf_change_ap(saddr, &th->th_sport,
- pd->ip_sum, &th->th_sum, &pd->baddr,
- bport, 0, af);
- else if (rdr != NULL)
- pf_change_ap(daddr, &th->th_dport,
- pd->ip_sum, &th->th_sum, &pd->baddr,
- bport, 0, af);
+ if (nr != NULL) {
+ if (direction == PF_OUT) {
+ pf_change_ap(saddr, &th->th_sport,
+ pd->ip_sum, &th->th_sum, &pd->baddr,
+ bport, 0, af);
+ } else {
+ pf_change_ap(daddr, &th->th_dport,
+ pd->ip_sum, &th->th_sum, &pd->baddr,
+ bport, 0, af);
+ }
+ }
s->src.seqhi = arc4random();
/* Find mss option */
mss = pf_get_mss(m, off, th->th_off, af);
@@ -2402,7 +2623,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
struct ifnet *ifp, struct mbuf *m, int off, void *h,
struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm)
{
- struct pf_rule *nat = NULL, *rdr = NULL;
+ struct pf_rule *nr = NULL;
struct pf_addr *saddr = pd->src, *daddr = pd->dst;
struct udphdr *uh = pd->hdr.udp;
u_int16_t bport, nport = 0;
@@ -2412,6 +2633,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
gid_t gid;
struct pf_rule *r, *a = NULL;
struct pf_ruleset *ruleset = NULL;
+ struct pf_src_node *nsn = NULL;
u_short reason;
int rewrite = 0;
struct pf_tag *pftag = NULL;
@@ -2422,30 +2644,30 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
if (direction == PF_OUT) {
bport = nport = uh->uh_sport;
/* check outgoing packet for BINAT/NAT */
- if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp,
+ if ((nr = pf_get_translation(pd, m, off, PF_OUT, ifp, &nsn,
saddr, uh->uh_sport, daddr, uh->uh_dport,
&pd->naddr, &nport)) != NULL) {
PF_ACPY(&pd->baddr, saddr, af);
pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum,
&uh->uh_sum, &pd->naddr, nport, 1, af);
rewrite++;
- if (nat->natpass)
+ if (nr->natpass)
r = NULL;
- pd->nat_rule = nat;
+ pd->nat_rule = nr;
}
} else {
bport = nport = uh->uh_dport;
/* check incoming packet for BINAT/RDR */
- if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr,
- uh->uh_sport, daddr, uh->uh_dport, &pd->naddr, &nport))
- != NULL) {
+ if ((nr = pf_get_translation(pd, m, off, PF_IN, ifp, &nsn,
+ saddr, uh->uh_sport, daddr, uh->uh_dport, &pd->naddr,
+ &nport)) != NULL) {
PF_ACPY(&pd->baddr, daddr, af);
pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum,
&uh->uh_sum, &pd->naddr, nport, 1, af);
rewrite++;
- if (rdr->natpass)
+ if (nr->natpass)
r = NULL;
- pd->nat_rule = rdr;
+ pd->nat_rule = nr;
}
}
@@ -2484,8 +2706,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
!pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1],
gid))
r = TAILQ_NEXT(r, entries);
- else if (r->match_tag &&
- !pf_match_tag(m, r, nat, rdr, pftag, &tag))
+ else if (r->match_tag && !pf_match_tag(m, r, nr, pftag, &tag))
r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
@@ -2525,14 +2746,16 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
((r->rule_flag & PFRULE_RETURNICMP) ||
(r->rule_flag & PFRULE_RETURN))) {
/* undo NAT changes, if they have taken place */
- if (nat != NULL) {
- pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum,
- &uh->uh_sum, &pd->baddr, bport, 1, af);
- rewrite++;
- } else if (rdr != NULL) {
- pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum,
- &uh->uh_sum, &pd->baddr, bport, 1, af);
- rewrite++;
+ if (nr != NULL) {
+ if (direction == PF_OUT) {
+ pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum,
+ &uh->uh_sum, &pd->baddr, bport, 1, af);
+ rewrite++;
+ } else {
+ pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum,
+ &uh->uh_sum, &pd->baddr, bport, 1, af);
+ rewrite++;
+ }
}
if ((af == AF_INET) && r->return_icmp)
pf_send_icmp(m, r->return_icmp >> 8,
@@ -2550,13 +2773,41 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
return (PF_DROP);
}
- if (r->keep_state || nat != NULL || rdr != NULL) {
+ if (r->keep_state || nr != NULL) {
/* create new state */
struct pf_state *s = NULL;
-
- if (!r->max_states || r->states < r->max_states)
- s = pool_get(&pf_state_pl, PR_NOWAIT);
+ struct pf_src_node *sn = NULL;
+
+ /* check maximums */
+ if (r->max_states && (r->states >= r->max_states))
+ goto cleanup;
+ /* src node for flter rule */
+ if ((r->rule_flag & PFRULE_SRCTRACK ||
+ r->rpool.opts & PF_POOL_STICKYADDR) &&
+ pf_insert_src_node(&sn, r, saddr, af) != 0)
+ goto cleanup;
+ /* src node for translation rule */
+ if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) &&
+ ((direction == PF_OUT &&
+ pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) ||
+ (pf_insert_src_node(&nsn, nr, saddr, af) != 0)))
+ goto cleanup;
+ s = pool_get(&pf_state_pl, PR_NOWAIT);
if (s == NULL) {
+cleanup:
+ if (sn != NULL && sn->states == 0 && sn->expire == 0) {
+ RB_REMOVE(pf_src_tree, &tree_src_tracking, sn);
+ pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+ pf_status.src_nodes--;
+ pool_put(&pf_src_tree_pl, sn);
+ }
+ if (nsn != sn && nsn != NULL && nsn->states == 0 &&
+ nsn->expire == 0) {
+ RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn);
+ pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+ pf_status.src_nodes--;
+ pool_put(&pf_src_tree_pl, nsn);
+ }
REASON_SET(&reason, PFRES_MEMORY);
return (PF_DROP);
}
@@ -2565,10 +2816,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
if (a != NULL)
a->states++;
s->rule.ptr = r;
- if (nat != NULL)
- s->nat_rule.ptr = nat;
- else
- s->nat_rule.ptr = rdr;
+ s->nat_rule.ptr = nr;
if (s->nat_rule.ptr != NULL)
s->nat_rule.ptr->states++;
s->anchor.ptr = a;
@@ -2582,7 +2830,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->gwy.port = uh->uh_sport;
PF_ACPY(&s->ext.addr, daddr, af);
s->ext.port = uh->uh_dport;
- if (nat != NULL) {
+ if (nr != NULL) {
PF_ACPY(&s->lan.addr, &pd->baddr, af);
s->lan.port = bport;
} else {
@@ -2594,7 +2842,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->lan.port = uh->uh_dport;
PF_ACPY(&s->ext.addr, saddr, af);
s->ext.port = uh->uh_sport;
- if (rdr != NULL) {
+ if (nr != NULL) {
PF_ACPY(&s->gwy.addr, &pd->baddr, af);
s->gwy.port = bport;
} else {
@@ -2608,8 +2856,18 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->expire = time.tv_sec;
s->timeout = PFTM_UDP_FIRST_PACKET;
pf_set_rt_ifp(s, saddr);
+ if (sn != NULL) {
+ s->src_node = sn;
+ s->src_node->states++;
+ }
+ if (nsn != NULL) {
+ PF_ACPY(&nsn->raddr, &pd->naddr, af);
+ s->nat_src_node = nsn;
+ s->nat_src_node->states++;
+ }
if (pf_insert_state(s)) {
REASON_SET(&reason, PFRES_MEMORY);
+ pf_src_tree_remove_state(s);
pool_put(&pf_state_pl, s);
return (PF_DROP);
} else
@@ -2628,10 +2886,11 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
struct ifnet *ifp, struct mbuf *m, int off, void *h,
struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm)
{
- struct pf_rule *nat = NULL, *rdr = NULL;
+ struct pf_rule *nr = NULL;
struct pf_addr *saddr = pd->src, *daddr = pd->dst;
struct pf_rule *r, *a = NULL;
struct pf_ruleset *ruleset = NULL;
+ struct pf_src_node *nsn = NULL;
u_short reason;
u_int16_t icmpid;
sa_family_t af = pd->af;
@@ -2677,8 +2936,8 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
if (direction == PF_OUT) {
/* check outgoing packet for BINAT/NAT */
- if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp, saddr, 0,
- daddr, 0, &pd->naddr, NULL)) != NULL) {
+ if ((nr = pf_get_translation(pd, m, off, PF_OUT, ifp, &nsn,
+ saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) {
PF_ACPY(&pd->baddr, saddr, af);
switch (af) {
#ifdef INET
@@ -2695,14 +2954,14 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
break;
#endif /* INET6 */
}
- if (nat->natpass)
+ if (nr->natpass)
r = NULL;
- pd->nat_rule = nat;
+ pd->nat_rule = nr;
}
} else {
/* check incoming packet for BINAT/RDR */
- if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr, 0,
- daddr, 0, &pd->naddr, NULL)) != NULL) {
+ if ((nr = pf_get_translation(pd, m, off, PF_IN, ifp, &nsn,
+ saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) {
PF_ACPY(&pd->baddr, daddr, af);
switch (af) {
#ifdef INET
@@ -2719,9 +2978,9 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
break;
#endif /* INET6 */
}
- if (rdr->natpass)
+ if (nr->natpass)
r = NULL;
- pd->nat_rule = rdr;
+ pd->nat_rule = nr;
}
}
@@ -2748,8 +3007,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
r = TAILQ_NEXT(r, entries);
else if (r->rule_flag & PFRULE_FRAGMENT)
r = TAILQ_NEXT(r, entries);
- else if (r->match_tag &&
- !pf_match_tag(m, r, nat, rdr, pftag, &tag))
+ else if (r->match_tag && !pf_match_tag(m, r, nr, pftag, &tag))
r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
@@ -2796,14 +3054,41 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
return (PF_DROP);
}
- if (!state_icmp && (r->keep_state ||
- nat != NULL || rdr != NULL)) {
+ if (!state_icmp && (r->keep_state || nr != NULL)) {
/* create new state */
struct pf_state *s = NULL;
-
- if (!r->max_states || r->states < r->max_states)
- s = pool_get(&pf_state_pl, PR_NOWAIT);
+ struct pf_src_node *sn = NULL;
+
+ /* check maximums */
+ if (r->max_states && (r->states >= r->max_states))
+ goto cleanup;
+ /* src node for flter rule */
+ if ((r->rule_flag & PFRULE_SRCTRACK ||
+ r->rpool.opts & PF_POOL_STICKYADDR) &&
+ pf_insert_src_node(&sn, r, saddr, af) != 0)
+ goto cleanup;
+ /* src node for translation rule */
+ if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) &&
+ ((direction == PF_OUT &&
+ pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) ||
+ (pf_insert_src_node(&nsn, nr, saddr, af) != 0)))
+ goto cleanup;
+ s = pool_get(&pf_state_pl, PR_NOWAIT);
if (s == NULL) {
+cleanup:
+ if (sn != NULL && sn->states == 0 && sn->expire == 0) {
+ RB_REMOVE(pf_src_tree, &tree_src_tracking, sn);
+ pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+ pf_status.src_nodes--;
+ pool_put(&pf_src_tree_pl, sn);
+ }
+ if (nsn != sn && nsn != NULL && nsn->states == 0 &&
+ nsn->expire == 0) {
+ RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn);
+ pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+ pf_status.src_nodes--;
+ pool_put(&pf_src_tree_pl, nsn);
+ }
REASON_SET(&reason, PFRES_MEMORY);
return (PF_DROP);
}
@@ -2812,10 +3097,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
if (a != NULL)
a->states++;
s->rule.ptr = r;
- if (nat != NULL)
- s->nat_rule.ptr = nat;
- else
- s->nat_rule.ptr = rdr;
+ s->nat_rule.ptr = nr;
if (s->nat_rule.ptr != NULL)
s->nat_rule.ptr->states++;
s->anchor.ptr = a;
@@ -2829,7 +3111,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->gwy.port = icmpid;
PF_ACPY(&s->ext.addr, daddr, af);
s->ext.port = icmpid;
- if (nat != NULL)
+ if (nr != NULL)
PF_ACPY(&s->lan.addr, &pd->baddr, af);
else
PF_ACPY(&s->lan.addr, &s->gwy.addr, af);
@@ -2839,7 +3121,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->lan.port = icmpid;
PF_ACPY(&s->ext.addr, saddr, af);
s->ext.port = icmpid;
- if (rdr != NULL)
+ if (nr != NULL)
PF_ACPY(&s->gwy.addr, &pd->baddr, af);
else
PF_ACPY(&s->gwy.addr, &s->lan.addr, af);
@@ -2849,8 +3131,18 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->expire = time.tv_sec;
s->timeout = PFTM_ICMP_FIRST_PACKET;
pf_set_rt_ifp(s, saddr);
+ if (sn != NULL) {
+ s->src_node = sn;
+ s->src_node->states++;
+ }
+ if (nsn != NULL) {
+ PF_ACPY(&nsn->raddr, &pd->naddr, af);
+ s->nat_src_node = nsn;
+ s->nat_src_node->states++;
+ }
if (pf_insert_state(s)) {
REASON_SET(&reason, PFRES_MEMORY);
+ pf_src_tree_remove_state(s);
pool_put(&pf_state_pl, s);
return (PF_DROP);
} else
@@ -2872,9 +3164,10 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
struct ifnet *ifp, struct mbuf *m, int off, void *h, struct pf_pdesc *pd,
struct pf_rule **am, struct pf_ruleset **rsm)
{
- struct pf_rule *nat = NULL, *rdr = NULL;
+ struct pf_rule *nr = NULL;
struct pf_rule *r, *a = NULL;
struct pf_ruleset *ruleset = NULL;
+ struct pf_src_node *nsn = NULL;
struct pf_addr *saddr = pd->src, *daddr = pd->dst;
sa_family_t af = pd->af;
u_short reason;
@@ -2885,8 +3178,8 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
if (direction == PF_OUT) {
/* check outgoing packet for BINAT/NAT */
- if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp, saddr, 0,
- daddr, 0, &pd->naddr, NULL)) != NULL) {
+ if ((nr = pf_get_translation(pd, m, off, PF_OUT, ifp, &nsn,
+ saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) {
PF_ACPY(&pd->baddr, saddr, af);
switch (af) {
#ifdef INET
@@ -2901,14 +3194,14 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
break;
#endif /* INET6 */
}
- if (nat->natpass)
+ if (nr->natpass)
r = NULL;
- pd->nat_rule = nat;
+ pd->nat_rule = nr;
}
} else {
/* check incoming packet for BINAT/RDR */
- if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr, 0,
- daddr, 0, &pd->naddr, NULL)) != NULL) {
+ if ((nr = pf_get_translation(pd, m, off, PF_IN, ifp, &nsn,
+ saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) {
PF_ACPY(&pd->baddr, daddr, af);
switch (af) {
#ifdef INET
@@ -2923,9 +3216,9 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
break;
#endif /* INET6 */
}
- if (rdr->natpass)
+ if (nr->natpass)
r = NULL;
- pd->nat_rule = rdr;
+ pd->nat_rule = nr;
}
}
@@ -2948,8 +3241,7 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
r = TAILQ_NEXT(r, entries);
else if (r->rule_flag & PFRULE_FRAGMENT)
r = TAILQ_NEXT(r, entries);
- else if (r->match_tag &&
- !pf_match_tag(m, r, nat, rdr, pftag, &tag))
+ else if (r->match_tag && !pf_match_tag(m, r, nr, pftag, &tag))
r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
@@ -2987,10 +3279,12 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
(r->rule_flag & PFRULE_RETURN))) {
struct pf_addr *a = NULL;
- if (nat != NULL)
- a = saddr;
- else if (rdr != NULL)
- a = daddr;
+ if (nr != NULL) {
+ if (direction == PF_OUT)
+ a = saddr;
+ else
+ a = daddr;
+ }
if (a != NULL) {
switch (af) {
#ifdef INET
@@ -3022,13 +3316,41 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
return (PF_DROP);
}
- if (r->keep_state || nat != NULL || rdr != NULL) {
+ if (r->keep_state || nr != NULL) {
/* create new state */
struct pf_state *s = NULL;
-
- if (!r->max_states || r->states < r->max_states)
- s = pool_get(&pf_state_pl, PR_NOWAIT);
+ struct pf_src_node *sn = NULL;
+
+ /* check maximums */
+ if (r->max_states && (r->states >= r->max_states))
+ goto cleanup;
+ /* src node for flter rule */
+ if ((r->rule_flag & PFRULE_SRCTRACK ||
+ r->rpool.opts & PF_POOL_STICKYADDR) &&
+ pf_insert_src_node(&sn, r, saddr, af) != 0)
+ goto cleanup;
+ /* src node for translation rule */
+ if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) &&
+ ((direction == PF_OUT &&
+ pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) ||
+ (pf_insert_src_node(&nsn, nr, saddr, af) != 0)))
+ goto cleanup;
+ s = pool_get(&pf_state_pl, PR_NOWAIT);
if (s == NULL) {
+cleanup:
+ if (sn != NULL && sn->states == 0 && sn->expire == 0) {
+ RB_REMOVE(pf_src_tree, &tree_src_tracking, sn);
+ pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+ pf_status.src_nodes--;
+ pool_put(&pf_src_tree_pl, sn);
+ }
+ if (nsn != sn && nsn != NULL && nsn->states == 0 &&
+ nsn->expire == 0) {
+ RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn);
+ pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+ pf_status.src_nodes--;
+ pool_put(&pf_src_tree_pl, nsn);
+ }
REASON_SET(&reason, PFRES_MEMORY);
return (PF_DROP);
}
@@ -3037,10 +3359,7 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
if (a != NULL)
a->states++;
s->rule.ptr = r;
- if (nat != NULL)
- s->nat_rule.ptr = nat;
- else
- s->nat_rule.ptr = rdr;
+ s->nat_rule.ptr = nr;
if (s->nat_rule.ptr != NULL)
s->nat_rule.ptr->states++;
s->anchor.ptr = a;
@@ -3052,14 +3371,14 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
if (direction == PF_OUT) {
PF_ACPY(&s->gwy.addr, saddr, af);
PF_ACPY(&s->ext.addr, daddr, af);
- if (nat != NULL)
+ if (nr != NULL)
PF_ACPY(&s->lan.addr, &pd->baddr, af);
else
PF_ACPY(&s->lan.addr, &s->gwy.addr, af);
} else {
PF_ACPY(&s->lan.addr, daddr, af);
PF_ACPY(&s->ext.addr, saddr, af);
- if (rdr != NULL)
+ if (nr != NULL)
PF_ACPY(&s->gwy.addr, &pd->baddr, af);
else
PF_ACPY(&s->gwy.addr, &s->lan.addr, af);
@@ -3070,11 +3389,21 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
s->expire = time.tv_sec;
s->timeout = PFTM_OTHER_FIRST_PACKET;
pf_set_rt_ifp(s, saddr);
+ if (sn != NULL) {
+ s->src_node = sn;
+ s->src_node->states++;
+ }
+ if (nsn != NULL) {
+ PF_ACPY(&nsn->raddr, &pd->naddr, af);
+ s->nat_src_node = nsn;
+ s->nat_src_node->states++;
+ }
if (pf_insert_state(s)) {
REASON_SET(&reason, PFRES_MEMORY);
if (r->log)
PFLOG_PACKET(ifp, h, m, af, direction, reason,
r, a, ruleset);
+ pf_src_tree_remove_state(s);
pool_put(&pf_state_pl, s);
return (PF_DROP);
} else
@@ -3118,8 +3447,7 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct ifnet *ifp,
r->flagset || r->type || r->code ||
r->os_fingerprint != PF_OSFP_ANY)
r = TAILQ_NEXT(r, entries);
- else if (r->match_tag &&
- !pf_match_tag(m, r, NULL, NULL, pftag, &tag))
+ else if (r->match_tag && !pf_match_tag(m, r, NULL, pftag, &tag))
r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
@@ -4351,6 +4679,7 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp,
struct ifnet *ifp = NULL;
struct m_tag *mtag;
struct pf_addr naddr;
+ struct pf_src_node *sn = NULL;
int error = 0;
if (m == NULL || *m == NULL || r == NULL ||
@@ -4402,9 +4731,8 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp,
if (TAILQ_EMPTY(&r->rpool.list))
panic("pf_route: TAILQ_EMPTY(&r->rpool.list)");
if (s == NULL) {
- pf_map_addr(AF_INET, &r->rpool,
- (struct pf_addr *)&ip->ip_src,
- &naddr, NULL);
+ pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src,
+ &naddr, NULL, &sn);
if (!PF_AZERO(&naddr, AF_INET))
dst->sin_addr.s_addr = naddr.v4.s_addr;
ifp = r->rpool.cur->ifp;
@@ -4544,6 +4872,7 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp,
struct ip6_hdr *ip6;
struct ifnet *ifp = NULL;
struct pf_addr naddr;
+ struct pf_src_node *sn = NULL;
int error = 0;
if (m == NULL || *m == NULL || r == NULL ||
@@ -4592,8 +4921,8 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp,
if (TAILQ_EMPTY(&r->rpool.list))
panic("pf_route6: TAILQ_EMPTY(&r->rpool.list)");
if (s == NULL) {
- pf_map_addr(AF_INET6, &r->rpool,
- (struct pf_addr *)&ip6->ip6_src, &naddr, NULL);
+ pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src,
+ &naddr, NULL, &sn);
if (!PF_AZERO(&naddr, AF_INET6))
PF_ACPY((struct pf_addr *)&dst->sin6_addr,
&naddr, AF_INET6);
@@ -4978,6 +5307,14 @@ done:
s->nat_rule.ptr->packets++;
s->nat_rule.ptr->bytes += pd.tot_len;
}
+ if (s->src_node != NULL) {
+ s->src_node->packets++;
+ s->src_node->bytes += pd.tot_len;
+ }
+ if (s->nat_src_node != NULL) {
+ s->nat_src_node->packets++;
+ s->nat_src_node->bytes += pd.tot_len;
+ }
}
tr = r;
nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule;
@@ -5265,6 +5602,14 @@ done:
s->nat_rule.ptr->packets++;
s->nat_rule.ptr->bytes += pd.tot_len;
}
+ if (s->src_node != NULL) {
+ s->src_node->packets++;
+ s->src_node->bytes += pd.tot_len;
+ }
+ if (s->nat_src_node != NULL) {
+ s->nat_src_node->packets++;
+ s->nat_src_node->bytes += pd.tot_len;
+ }
}
tr = r;
nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule;
diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c
index f0c38de9731..bd1e7db5921 100644
--- a/sys/net/pf_ioctl.c
+++ b/sys/net/pf_ioctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_ioctl.c,v 1.88 2003/12/12 20:05:45 cedric Exp $ */
+/* $OpenBSD: pf_ioctl.c,v 1.89 2003/12/15 00:02:04 mcbride Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -106,6 +106,8 @@ pfattach(int num)
&pool_allocator_nointr);
pool_init(&pf_addr_pl, sizeof(struct pf_addr_dyn), 0, 0, 0, "pfaddrpl",
&pool_allocator_nointr);
+ pool_init(&pf_src_tree_pl, sizeof(struct pf_src_node), 0, 0, 0,
+ "pfsrctrpl", NULL);
pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl",
NULL);
pool_init(&pf_altq_pl, sizeof(struct pf_altq), 0, 0, 0, "pfaltqpl",
@@ -120,6 +122,7 @@ pfattach(int num)
RB_INIT(&tree_lan_ext);
RB_INIT(&tree_ext_gwy);
+ RB_INIT(&tree_src_tracking);
TAILQ_INIT(&pf_anchors);
pf_init_ruleset(&pf_main_ruleset);
TAILQ_INIT(&pf_altqs[0]);
@@ -150,11 +153,13 @@ pfattach(int num)
timeout[PFTM_OTHER_MULTIPLE] = 60; /* Bidirectional */
timeout[PFTM_FRAG] = 30; /* Fragment expire */
timeout[PFTM_INTERVAL] = 10; /* Expire interval */
+ timeout[PFTM_SRC_NODE] = 0; /* Source tracking */
timeout_set(&pf_expire_to, pf_purge_timeout, &pf_expire_to);
timeout_add(&pf_expire_to, timeout[PFTM_INTERVAL] * hz);
pf_normalize_init();
+ bzero(&pf_status, sizeof(pf_status));
pf_status.debug = PF_DEBUG_URGENT;
}
@@ -414,7 +419,9 @@ pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule)
rule->entries.tqe_prev = NULL;
rule->nr = -1;
}
- if (rule->states > 0 || rule->entries.tqe_prev != NULL)
+
+ if (rule->states > 0 || rule->src_nodes > 0 ||
+ rule->entries.tqe_prev != NULL)
return;
pf_tag_unref(rule->tag);
pf_tag_unref(rule->match_tag);
@@ -732,6 +739,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
case DIOCRCLRASTATS:
case DIOCRTSTADDRS:
case DIOCOSFPGET:
+ case DIOCGETSRCNODES:
+ case DIOCCLRSRCNODES:
break;
default:
return (EPERM);
@@ -761,6 +770,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
case DIOCRGETASTATS:
case DIOCRTSTADDRS:
case DIOCOSFPGET:
+ case DIOCGETSRCNODES:
break;
default:
return (EACCES);
@@ -774,10 +784,12 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
else {
u_int32_t states = pf_status.states;
u_int32_t debug = pf_status.debug;
+ u_int32_t src_nodes = pf_status.src_nodes;
bzero(&pf_status, sizeof(struct pf_status));
pf_status.running = 1;
pf_status.states = states;
pf_status.debug = debug;
+ pf_status.states = src_nodes;
pf_status.since = time.tv_sec;
if (status_ifp != NULL)
strlcpy(pf_status.ifname,
@@ -847,6 +859,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
TAILQ_INIT(&rule->rpool.list);
/* initialize refcounting */
rule->states = 0;
+ rule->src_nodes = 0;
rule->entries.tqe_prev = NULL;
#ifndef INET
if (rule->af == AF_INET) {
@@ -1363,12 +1376,14 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
case DIOCCLRSTATUS: {
u_int32_t running = pf_status.running;
u_int32_t states = pf_status.states;
+ u_int32_t src_nodes = pf_status.src_nodes;
u_int32_t since = pf_status.since;
u_int32_t debug = pf_status.debug;
bzero(&pf_status, sizeof(struct pf_status));
pf_status.running = running;
pf_status.states = states;
+ pf_status.src_nodes = src_nodes;
pf_status.since = since;
pf_status.debug = debug;
if (status_ifp != NULL)
@@ -1488,6 +1503,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
}
old_limit = pf_pool_limits[pl->index].limit;
pf_pool_limits[pl->index].limit = pl->limit;
+ if (pl->index == PF_LIMIT_SRC_NODES)
+ pf_default_rule.max_src_nodes = pl->limit;
pl->limit = old_limit;
break;
}
@@ -2204,12 +2221,6 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
break;
}
- case DIOCOSFPFLUSH:
- s = splsoftnet();
- pf_osfp_flush();
- splx(s);
- break;
-
case DIOCOSFPADD: {
struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr;
s = splsoftnet();
@@ -2411,6 +2422,76 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
break;
}
+ case DIOCGETSRCNODES: {
+ struct pfioc_src_nodes *psn = (struct pfioc_src_nodes *)addr;
+ struct pf_src_node *n;
+ struct pf_src_node *p, pstore;
+ u_int32_t nr = 0;
+ int space = psn->psn_len;
+
+ if (space == 0) {
+ s = splsoftnet();
+ RB_FOREACH(n, pf_src_tree, &tree_src_tracking)
+ nr++;
+ splx(s);
+ psn->psn_len = sizeof(struct pf_src_node) * nr;
+ return (0);
+ }
+
+ s = splsoftnet();
+ p = psn->psn_src_nodes;
+ RB_FOREACH(n, pf_src_tree, &tree_src_tracking) {
+ int secs = time.tv_sec;
+
+ if ((nr + 1) * sizeof(*p) > (unsigned)psn->psn_len)
+ break;
+
+ bcopy(n, &pstore, sizeof(pstore));
+ if (n->rule.ptr != NULL)
+ pstore.rule.nr = n->rule.ptr->nr;
+ pstore.creation = secs - pstore.creation;
+ if (pstore.expire > secs)
+ pstore.expire -= secs;
+ else
+ pstore.expire = 0;
+ error = copyout(&pstore, p, sizeof(*p));
+ if (error) {
+ splx(s);
+ goto fail;
+ }
+ p++;
+ nr++;
+ }
+ psn->psn_len = sizeof(struct pf_src_node) * nr;
+ splx(s);
+ break;
+ }
+
+ case DIOCCLRSRCNODES: {
+ struct pf_src_node *n;
+ struct pf_state *state;
+
+ s = splsoftnet();
+ RB_FOREACH(state, pf_state_tree_lan_ext, &tree_lan_ext) {
+ state->src_node = NULL;
+ state->nat_src_node = NULL;
+ }
+ RB_FOREACH(n, pf_src_tree, &tree_src_tracking) {
+ n->expire = 1;
+ n->states = 0;
+ }
+ pf_purge_expired_src_nodes();
+ pf_status.src_nodes = 0;
+ splx(s);
+ break;
+ }
+
+ case DIOCOSFPFLUSH:
+ s = splsoftnet();
+ pf_osfp_flush();
+ splx(s);
+ break;
+
default:
error = ENODEV;
break;
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 71e31f90d79..4b4e4219a97 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfvar.h,v 1.176 2003/12/12 20:05:45 cedric Exp $ */
+/* $OpenBSD: pfvar.h,v 1.177 2003/12/15 00:02:04 mcbride Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -68,16 +68,17 @@ enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED,
PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY,
PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE,
PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL,
- PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_MAX,
- PFTM_PURGE, PFTM_UNTIL_PACKET };
+ PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_SRC_NODE,
+ PFTM_MAX, PFTM_PURGE, PFTM_UNTIL_PACKET };
enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO };
-enum { PF_LIMIT_STATES, PF_LIMIT_FRAGS, PF_LIMIT_MAX };
+enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, PF_LIMIT_MAX };
#define PF_POOL_IDMASK 0x0f
enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM,
PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN };
enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL,
PF_ADDR_TABLE };
#define PF_POOL_TYPEMASK 0x0f
+#define PF_POOL_STICKYADDR 0x20
#define PF_WSCALE_FLAG 0x80
#define PF_WSCALE_MASK 0x0f
@@ -447,7 +448,6 @@ struct pf_rule {
union pf_rule_ptr skip[PF_SKIP_COUNT];
#define PF_RULE_LABEL_SIZE 64
char label[PF_RULE_LABEL_SIZE];
- u_int32_t timeout[PFTM_MAX];
#define PF_QNAME_SIZE 16
char ifname[IFNAMSIZ];
char qname[PF_QNAME_SIZE];
@@ -469,8 +469,13 @@ struct pf_rule {
struct pf_anchor *anchor;
pf_osfp_t os_fingerprint;
+
+ u_int32_t timeout[PFTM_MAX];
u_int32_t states;
u_int32_t max_states;
+ u_int32_t src_nodes;
+ u_int32_t max_src_nodes;
+ u_int32_t max_src_states;
u_int32_t qid;
u_int32_t pqid;
u_int32_t rt_listid;
@@ -518,6 +523,8 @@ struct pf_rule {
#define PFRULE_RETURNICMP 0x0004
#define PFRULE_RETURN 0x0008
#define PFRULE_NOSYNC 0x0010
+#define PFRULE_SRCTRACK 0x0020 /* track source states */
+#define PFRULE_RULESRCTRACK 0x0040 /* per rule */
/* scrub flags */
#define PFRULE_NODF 0x0100
@@ -528,6 +535,20 @@ struct pf_rule {
#define PFSTATE_HIWAT 10000 /* default state table size */
+struct pf_src_node {
+ RB_ENTRY(pf_src_node) entry;
+ struct pf_addr addr;
+ struct pf_addr raddr;
+ union pf_rule_ptr rule;
+ struct ifnet *ifp;
+ u_int32_t bytes;
+ u_int32_t packets;
+ u_int32_t states;
+ u_int32_t creation;
+ u_int32_t expire;
+ sa_family_t af;
+ u_int8_t ruletype;
+};
struct pf_state_scrub {
u_int16_t pfss_flags;
@@ -567,6 +588,8 @@ struct pf_state {
union pf_rule_ptr nat_rule;
struct pf_addr rt_addr;
struct ifnet *rt_ifp;
+ struct pf_src_node *src_node;
+ struct pf_src_node *nat_src_node;
u_int32_t creation;
u_int32_t expire;
u_int32_t packets[2];
@@ -796,6 +819,10 @@ struct pf_pdesc {
#define FCNT_STATE_REMOVALS 2
#define FCNT_MAX 3
+#define SCNT_SRC_NODE_SEARCH 0
+#define SCNT_SRC_NODE_INSERT 1
+#define SCNT_SRC_NODE_REMOVALS 2
+#define SCNT_MAX 3
#define ACTION_SET(a, x) \
do { \
@@ -814,10 +841,12 @@ struct pf_pdesc {
struct pf_status {
u_int64_t counters[PFRES_MAX];
u_int64_t fcounters[FCNT_MAX];
+ u_int64_t scounters[SCNT_MAX];
u_int64_t pcounters[2][2][3];
u_int64_t bcounters[2][2];
u_int32_t running;
u_int32_t states;
+ u_int32_t src_nodes;
u_int32_t since;
u_int32_t debug;
char ifname[IFNAMSIZ];
@@ -963,6 +992,16 @@ struct pfioc_states {
#define ps_states ps_u.psu_states
};
+struct pfioc_src_nodes {
+ int psn_len;
+ union {
+ caddr_t psu_buf;
+ struct pf_src_node *psu_src_nodes;
+ } psn_u;
+#define psn_buf psn_u.psu_buf
+#define psn_src_nodes psn_u.psu_src_nodes
+};
+
struct pfioc_if {
char ifname[IFNAMSIZ];
};
@@ -1116,9 +1155,15 @@ struct pfioc_table {
#define DIOCXBEGIN _IOWR('D', 81, struct pfioc_trans)
#define DIOCXCOMMIT _IOWR('D', 82, struct pfioc_trans)
#define DIOCXROLLBACK _IOWR('D', 83, struct pfioc_trans)
+#define DIOCGETSRCNODES _IOWR('D', 84, struct pfioc_src_nodes)
+#define DIOCCLRSRCNODES _IO('D', 85)
#ifdef _KERNEL
+RB_HEAD(pf_src_tree, pf_src_node);
+RB_PROTOTYPE(pf_src_tree, pf_src_node, entry, pf_src_compare);
+extern struct pf_src_tree tree_src_tracking;
+
RB_HEAD(pf_state_tree_lan_ext, pf_state);
RB_PROTOTYPE(pf_state_tree_lan_ext, pf_state,
entry_lan_ext, pf_state_compare_lan_ext);
@@ -1154,12 +1199,17 @@ extern void pf_calc_skip_steps(struct pf_rulequeue *);
extern void pf_rule_set_qid(struct pf_rulequeue *);
extern u_int32_t pf_qname_to_qid(char *);
extern void pf_update_anchor_rules(void);
-extern struct pool pf_rule_pl, pf_addr_pl;
+extern struct pool pf_src_tree_pl, pf_rule_pl, pf_addr_pl;
extern struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl;
extern struct pool pf_state_scrub_pl;
extern void pf_purge_timeout(void *);
+extern void pf_purge_expired_src_nodes(void);
extern void pf_purge_expired_states(void);
extern int pf_insert_state(struct pf_state *);
+extern int pf_insert_src_node(struct pf_src_node **,
+ struct pf_rule *, struct pf_addr *,
+ sa_family_t);
+void pf_src_tree_remove_state(struct pf_state *);
extern struct pf_state *pf_find_state(struct pf_state *, u_int8_t);
extern struct pf_anchor *pf_find_anchor(const char *);
extern struct pf_ruleset *pf_find_ruleset(char *, char *);