diff options
author | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2003-12-15 00:02:05 +0000 |
---|---|---|
committer | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2003-12-15 00:02:05 +0000 |
commit | bcbefdbeb6961a98675a03e10371e908592d2742 (patch) | |
tree | 44fc5938d025a365526a21723a1004d25f125611 | |
parent | 7177de71616eff6b6f4d44f5b1c99fe17c82545e (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.y | 253 | ||||
-rw-r--r-- | sbin/pfctl/pf_print_state.c | 7 | ||||
-rw-r--r-- | sbin/pfctl/pfctl.8 | 9 | ||||
-rw-r--r-- | sbin/pfctl/pfctl.c | 79 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.c | 99 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.h | 5 | ||||
-rw-r--r-- | share/man/man4/pf.4 | 58 | ||||
-rw-r--r-- | share/man/man5/pf.conf.5 | 51 | ||||
-rw-r--r-- | sys/net/pf.c | 661 | ||||
-rw-r--r-- | sys/net/pf_ioctl.c | 97 | ||||
-rw-r--r-- | sys/net/pfvar.h | 62 |
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 *); |