diff options
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/pfctl/Makefile | 7 | ||||
-rw-r--r-- | sbin/pfctl/parse.y | 138 | ||||
-rw-r--r-- | sbin/pfctl/pfctl.c | 346 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_optimize.c | 88 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.c | 18 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.h | 19 |
6 files changed, 429 insertions, 187 deletions
diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile index 52763fde678..ef097355a1d 100644 --- a/sbin/pfctl/Makefile +++ b/sbin/pfctl/Makefile @@ -1,14 +1,17 @@ -# $OpenBSD: Makefile,v 1.17 2004/07/16 23:44:24 frantzen Exp $ +# $OpenBSD: Makefile,v 1.18 2006/10/28 14:29:05 mcbride Exp $ PROG= pfctl SRCS= pfctl.c parse.y pfctl_parser.c pf_print_state.c pfctl_altq.c SRCS+= pfctl_osfp.c pfctl_radix.c pfctl_table.c pfctl_qstats.c -SRCS+= pfctl_optimize.c +SRCS+= pfctl_optimize.c pf_ruleset.c CFLAGS+= -Wall -Wmissing-prototypes -Wno-uninitialized CFLAGS+= -Wstrict-prototypes -I${.CURDIR} YFLAGS= MAN= pfctl.8 +# Ruleset and Anchor handling +.PATH: ${.CURDIR}/../../sys/net + LDADD+= -lm DPADD+= ${LIBM} diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index e7116136a7b..128e87a6fa9 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.512 2006/10/25 14:50:30 henning Exp $ */ +/* $OpenBSD: parse.y,v 1.513 2006/10/28 14:29:05 mcbride Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -310,6 +310,7 @@ struct sym { int symset(const char *, const char *, int); char *symget(const char *); +void mv_rules(struct pf_ruleset *, struct pf_ruleset *); void decide_address_family(struct node_host *, sa_family_t *); void remove_invalid_hosts(struct node_host **, sa_family_t *); int invalid_redirect(struct node_host *, sa_family_t); @@ -443,7 +444,7 @@ typedef struct { %type <v.gid> gids gid_list gid_item %type <v.route> route %type <v.redirection> redirection redirpool -%type <v.string> label string tag +%type <v.string> label string tag anchorname %type <v.keep_state> keep %type <v.state_opt> state_opt_spec state_opt_list state_opt_item %type <v.logquick> logquick quick log logopts logopt @@ -479,9 +480,22 @@ ruleset : /* empty */ | ruleset varset '\n' | ruleset antispoof '\n' | ruleset tabledef '\n' + | '{' fakeanchor '}' '\n'; | ruleset error '\n' { errors++; } ; +/* + * apply to previouslys specified rule: must be careful to note + * what that is: pf or nat or binat or rdr + */ +fakeanchor : fakeanchor '\n' + | fakeanchor anchorrule '\n' + | fakeanchor binatrule '\n' + | fakeanchor natrule '\n' + | fakeanchor pfrule '\n' + | fakeanchor error '\n' + ; + option : SET OPTIMIZATION STRING { if (check_rulestate(PFCTL_STATE_OPTION)) { free($3); @@ -547,7 +561,7 @@ option : SET OPTIMIZATION STRING { free($3); YYERROR; } - if (!pf->anchor[0]) { + if (!pf->anchor->name[0]) { if (pfctl_file_fingerprints(pf->dev, pf->opts, $3)) { yyerror("error loading " @@ -609,15 +623,84 @@ varset : STRING '=' string { } ; -anchorrule : ANCHOR string dir quick interface af proto fromto filter_opts { +anchorname : STRING { $$ = $1; } + | /* empty */ { $$ = NULL; } + ; + +optnl : optnl '\n' + | + ; + +pfa_anchorlist : pfrule optnl + | anchorrule optnl + | pfa_anchorlist pfrule optnl + | pfa_anchorlist anchorrule optnl + ; + +pfa_anchor : '{' + { + char tmp_anchorname[PF_ANCHOR_NAME_SIZE]; + struct pf_ruleset *rs; + + pf->asd++; + pf->bn++; + pf->brace = 1; + snprintf(tmp_anchorname, PF_ANCHOR_NAME_SIZE, + "_%d", pf->bn); + rs = pf_find_or_create_ruleset(tmp_anchorname); + if (rs == NULL) + err(1, "pfa_anchor: pf_find_or_create_ruleset"); + pf->astack[pf->asd] = rs->anchor; + pf->anchor = rs->anchor; + } '\n' pfa_anchorlist '}' + { + pf->alast = pf->anchor; + pf->asd--; + pf->anchor = pf->astack[pf->asd]; + } + | /* empty */ + ; + +anchorrule : ANCHOR anchorname dir quick interface af proto fromto + filter_opts pfa_anchor + { struct pf_rule r; if (check_rulestate(PFCTL_STATE_FILTER)) { - free($2); + if ($2) + free($2); YYERROR; } memset(&r, 0, sizeof(r)); + if (pf->astack[pf->asd + 1]) { + if ($2) { + struct pf_ruleset *src = + &pf->alast->ruleset; + struct pf_ruleset *dst = + pf_find_or_create_ruleset($2); + + if (!dst) + err(1, "anchorrule: unable to " + "create ruleset"); + if (dst->anchor->refcnt) { + yyerror("inline anchor " + "already exists\n"); + YYERROR; + } + + mv_rules(src, dst); + pf_remove_if_empty_ruleset(src); + pf->alast = dst->anchor; + } + r.anchor = pf->alast; + } else { + if (!$2) { + yyerror("anchors without explicit " + "rules must specify a name\n"); + YYERROR; + } + } r.direction = $3; r.quick = $4.quick; r.af = $6; @@ -638,8 +721,10 @@ anchorrule : ANCHOR string dir quick interface af proto fromto filter_opts { expand_rule(&r, $5, NULL, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, - 0, 0, 0, $2); + 0, 0, 0, pf->astack[pf->asd + 1] ? + pf->alast->name : $2); free($2); + pf->astack[pf->asd + 1] = NULL; } | NATANCHOR string interface af proto fromto rtable { struct pf_rule r; @@ -744,7 +829,8 @@ anchorrule : ANCHOR string dir quick interface af proto fromto filter_opts { loadrule : LOAD ANCHOR string FROM string { struct loadanchors *loadanchor; - if (strlen(pf->anchor) + 1 + strlen($3) >= MAXPATHLEN) { + if (strlen(pf->anchor->name) + 1 + + strlen($3) >= MAXPATHLEN) { yyerror("anchorname %s too long, max %u\n", $3, MAXPATHLEN - 1); free($3); @@ -756,9 +842,9 @@ loadrule : LOAD ANCHOR string FROM string { if ((loadanchor->anchorname = malloc(MAXPATHLEN)) == NULL) err(1, "loadrule: malloc"); - if (pf->anchor[0]) + if (pf->anchor->name[0]) snprintf(loadanchor->anchorname, MAXPATHLEN, - "%s/%s", pf->anchor, $3); + "%s/%s", pf->anchor->name, $3); else strlcpy(loadanchor->anchorname, $3, MAXPATHLEN); if ((loadanchor->filename = strdup($5)) == NULL) @@ -1630,7 +1716,7 @@ pfrule : action dir logquick interface route af proto fromto if (!r.keep_state && !r.action && !($9.marker & FOM_KEEP)) r.keep_state = PF_STATE_NORMAL; - + o = $9.keep.options; while (o) { struct node_state_opt *p = o; @@ -2962,7 +3048,7 @@ statelock : IFBOUND { keep : NO STATE { $$.action = 0; - $$.options = NULL; + $$.options = NULL; } | KEEP STATE state_opt_spec { $$.action = PF_STATE_NORMAL; @@ -4029,7 +4115,7 @@ process_tabledef(char *name, struct table_opts *opts) &opts->init_nodes); if (!(pf->opts & PF_OPT_NOACTION) && pfctl_define_table(name, opts->flags, opts->init_addr, - pf->anchor, &ab, pf->tticket)) { + pf->anchor->name, &ab, pf->anchor->ruleset.tticket)) { yyerror("cannot define table %s: %s", name, pfr_strerror(errno)); goto _error; @@ -4221,7 +4307,7 @@ expand_label_nr(const char *name, char *label, size_t len) char n[11]; if (strstr(label, name) != NULL) { - snprintf(n, sizeof(n), "%u", pf->rule_nr); + snprintf(n, sizeof(n), "%u", pf->anchor->refcnt); expand_label_str(label, len, name, n); } } @@ -4651,7 +4737,7 @@ expand_rule(struct pf_rule *r, if (rule_consistent(r, anchor_call[0]) < 0 || error) yyerror("skipping rule due to errors"); else { - r->nr = pf->rule_nr++; + r->nr = pf->anchor->refcnt++; pfctl_add_rule(pf, r, anchor_call); added++; } @@ -5181,6 +5267,29 @@ symget(const char *nam) } void +mv_rules(struct pf_ruleset *src, struct pf_ruleset *dst) +{ + int i; + struct pf_rule *r; + + for (i = 0; i < PF_RULESET_MAX; ++i) { + while ((r = TAILQ_FIRST(src->rules[i].active.ptr)) + != NULL) { + TAILQ_REMOVE(src->rules[i].active.ptr, r, entries); + TAILQ_INSERT_TAIL(dst->rules[i].active.ptr, r, entries); + } + while ((r = TAILQ_FIRST(src->rules[i].inactive.ptr)) + != NULL) { + TAILQ_REMOVE(src->rules[i].inactive.ptr, r, entries); + TAILQ_INSERT_TAIL(dst->rules[i].inactive.ptr, + r, entries); + } + } + dst->anchor->refcnt = src->anchor->refcnt; + src->anchor->refcnt = 0; +} + +void decide_address_family(struct node_host *n, sa_family_t *af) { sa_family_t target_af = 0; @@ -5356,4 +5465,3 @@ pfctl_load_anchors(int dev, int opts, struct pfr_buffer *trans) return (0); } - diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index 002e6ce4e3d..20b768965f8 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfctl.c,v 1.247 2006/06/30 16:52:27 deraadt Exp $ */ +/* $OpenBSD: pfctl.c,v 1.248 2006/10/28 14:29:05 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -77,7 +77,7 @@ int pfctl_load_hostid(struct pfctl *, unsigned int); int pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int, char *); void pfctl_print_rule_counters(struct pf_rule *, int); -int pfctl_show_rules(int, int, int, char *); +int pfctl_show_rules(int, char *, int, int, char *, int); int pfctl_show_nat(int, int, char *); int pfctl_show_src_nodes(int, int); int pfctl_show_states(int, const char *, int); @@ -88,8 +88,15 @@ void pfctl_debug(int, u_int32_t, int); int pfctl_clear_rule_counters(int, int); int pfctl_test_altqsupport(int, int); int pfctl_show_anchors(int, int, char *); +int pfctl_ruleset_trans(struct pfctl *, char *, struct pf_anchor *); +int pfctl_load_ruleset(struct pfctl *, char *, + struct pf_ruleset *, int, int); +int pfctl_load_rule(struct pfctl *, char *, struct pf_rule *, int); const char *pfctl_lookup_option(char *, const char **); +struct pf_anchor_global pf_anchors; +struct pf_anchor pf_main_anchor; + const char *clearopt; char *rulesopt; const char *showopt; @@ -110,6 +117,15 @@ int labels = 0; const char *infile; +#define INDENT(d, o) do { \ + if (o) { \ + int i; \ + for (i=0; i < d; i++) \ + printf(" "); \ + } \ + } while (0); \ + + static const struct { const char *name; int index; @@ -557,6 +573,17 @@ pfctl_get_pool(int dev, struct pf_pool *pool, u_int32_t nr, } void +pfctl_move_pool(struct pf_pool *src, struct pf_pool *dst) +{ + struct pf_pooladdr *pa; + + while ((pa = TAILQ_FIRST(&src->list)) != NULL) { + TAILQ_REMOVE(&src->list, pa, entries); + TAILQ_INSERT_TAIL(&dst->list, pa, entries); + } +} + +void pfctl_clear_pool(struct pf_pool *pool) { struct pf_pooladdr *pa; @@ -614,26 +641,33 @@ pfctl_print_title(char *title) } int -pfctl_show_rules(int dev, int opts, int format, char *anchorname) +pfctl_show_rules(int dev, char *path, int opts, int format, + char *anchorname, int depth) { struct pfioc_rule pr; u_int32_t nr, mnr, header = 0; int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG); + int len = strlen(path); + + if (path[0]) + snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname); + else + snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname); memset(&pr, 0, sizeof(pr)); - memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); + memcpy(pr.anchor, path, sizeof(pr.anchor)); if (opts & PF_OPT_SHOWALL) { pr.rule.action = PF_PASS; if (ioctl(dev, DIOCGETRULES, &pr)) { warn("DIOCGETRULES"); - return (-1); + goto error; } header++; } pr.rule.action = PF_SCRUB; if (ioctl(dev, DIOCGETRULES, &pr)) { warn("DIOCGETRULES"); - return (-1); + goto error; } if (opts & PF_OPT_SHOWALL) { if (format == 0 && (pr.nr > 0 || header)) @@ -646,12 +680,12 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname) pr.nr = nr; if (ioctl(dev, DIOCGETRULE, &pr)) { warn("DIOCGETRULE"); - return (-1); + goto error; } if (pfctl_get_pool(dev, &pr.rule.rpool, - nr, pr.ticket, PF_SCRUB, anchorname) != 0) - return (-1); + nr, pr.ticket, PF_SCRUB, path) != 0) + goto error; switch (format) { case 1: @@ -680,19 +714,19 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname) pr.rule.action = PF_PASS; if (ioctl(dev, DIOCGETRULES, &pr)) { warn("DIOCGETRULES"); - return (-1); + goto error; } mnr = pr.nr; for (nr = 0; nr < mnr; ++nr) { pr.nr = nr; if (ioctl(dev, DIOCGETRULE, &pr)) { warn("DIOCGETRULE"); - return (-1); + goto error; } if (pfctl_get_pool(dev, &pr.rule.rpool, - nr, pr.ticket, PF_PASS, anchorname) != 0) - return (-1); + nr, pr.ticket, PF_PASS, path) != 0) + goto error; switch (format) { case 1: @@ -713,12 +747,24 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname) default: if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL)) labels = 1; + INDENT(depth, !(opts & PF_OPT_VERBOSE)); print_rule(&pr.rule, pr.anchor_call, rule_numbers); pfctl_print_rule_counters(&pr.rule, opts); + if (pr.anchor_call[0] == '_') { + pfctl_show_rules(dev, path, opts, format, + pr.anchor_call, depth + 1); + INDENT(depth, !(opts & PF_OPT_VERBOSE)); + printf("}\n"); + } } pfctl_clear_pool(&pr.rule.rpool); } + path[len] = '\0'; return (0); + + error: + path[len] = '\0'; + return (-1); } int @@ -941,93 +987,134 @@ int pfctl_add_rule(struct pfctl *pf, struct pf_rule *r, const char *anchor_call) { u_int8_t rs_num; - struct pfioc_rule pr; + struct pf_rule *rule; + struct pf_ruleset *rs; - switch (r->action) { - case PF_SCRUB: - case PF_NOSCRUB: - if ((loadopt & PFCTL_FLAG_FILTER) == 0) - return (0); - rs_num = PF_RULESET_SCRUB; - break; - case PF_DROP: - case PF_PASS: - if ((loadopt & PFCTL_FLAG_FILTER) == 0) - return (0); - rs_num = PF_RULESET_FILTER; - break; - case PF_NAT: - case PF_NONAT: - if ((loadopt & PFCTL_FLAG_NAT) == 0) - return (0); - rs_num = PF_RULESET_NAT; - break; - case PF_RDR: - case PF_NORDR: - if ((loadopt & PFCTL_FLAG_NAT) == 0) - return (0); - rs_num = PF_RULESET_RDR; - break; - case PF_BINAT: - case PF_NOBINAT: - if ((loadopt & PFCTL_FLAG_NAT) == 0) - return (0); - rs_num = PF_RULESET_BINAT; - break; - default: + rs_num = pf_get_ruleset_number(r->action); + if (rs_num == PF_RULESET_MAX) errx(1, "Invalid rule type %d", r->action); - break; - } + rs = &pf->anchor->ruleset; - if ((pf->opts & PF_OPT_OPTIMIZE) && rs_num == PF_RULESET_FILTER) { - /* - * We'll do an optimization post-pass before finally adding the - * rules. Then we'll disable the optimization flag and feed - * the rules right back into this function. - */ - struct pf_opt_rule *pfr; - struct pf_pooladdr *pa; - if ((pfr = calloc(1, sizeof(*pfr))) == NULL) - err(1, "calloc"); - memcpy(&pfr->por_rule, r, sizeof(*r)); - if (strlcpy(pfr->por_anchor, anchor_call, - sizeof(pfr->por_anchor)) >= sizeof(pfr->por_anchor)) - errx(1, "pfctl_add_rule: strlcpy"); - TAILQ_INSERT_TAIL(&pf->opt_queue, pfr, por_entry); - - if (TAILQ_FIRST(&r->rpool.list) != NULL) { - TAILQ_INIT(&pfr->por_rule.rpool.list); - while ((pa = TAILQ_FIRST(&r->rpool.list)) != NULL) { - TAILQ_REMOVE(&r->rpool.list, pa, entries); - TAILQ_INSERT_TAIL(&pfr->por_rule.rpool.list, pa, - entries); - } - } else { - memset(&pfr->por_rule.rpool, 0, - sizeof(pfr->por_rule.rpool)); + if (anchor_call[0] && r->anchor == NULL) + r->anchor = pf_find_or_create_ruleset(anchor_call)->anchor; + + if ((rule = calloc(1, sizeof(*rule))) == NULL) + err(1, "calloc"); + bcopy(r, rule, sizeof(*rule)); + TAILQ_INIT(&rule->rpool.list); + pfctl_move_pool(&r->rpool, &rule->rpool); + + TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries); + return (0); +} +int +pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pf_anchor *a) +{ + int osize = pf->trans->pfrb_size; + + if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) { + if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) || + pfctl_add_trans(pf->trans, PF_RULESET_BINAT, path) || + pfctl_add_trans(pf->trans, PF_RULESET_RDR, path)) + return (1); + } + if (a == pf->astack[0] && ((altqsupport && + (pf->loadopt & PFCTL_FLAG_ALTQ) != 0))) { + if (pfctl_add_trans(pf->trans, PF_RULESET_ALTQ, path)) + return (2); + } + if ((pf->loadopt & PFCTL_FLAG_FILTER) != 0) { + if (pfctl_add_trans(pf->trans, PF_RULESET_SCRUB, path) || + pfctl_add_trans(pf->trans, PF_RULESET_FILTER, path)) + return (3); + } + if (pf->loadopt & PFCTL_FLAG_TABLE) + if (pfctl_add_trans(pf->trans, PF_RULESET_TABLE, path)) + return (4); + if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize)) + return (5); + + return (0); +} + +int +pfctl_load_ruleset(struct pfctl *pf, char *path, struct pf_ruleset *rs, + int rs_num, int depth) +{ + struct pf_rule *r; + int error, len = strlen(path); + + pf->anchor = rs->anchor; + + if (path[0]) + snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->anchor->name); + else + snprintf(&path[len], MAXPATHLEN - len, "%s", pf->anchor->name); + + if (rs != &pf->astack[0]->ruleset && + ((pf->opts & PF_OPT_NOACTION) == 0)) { + if ((error = pfctl_ruleset_trans(pf, path, rs->anchor))) { + printf("pfctl_load_rulesets: " + "pfctl_ruleset_trans %d\n", error); + goto error; } - return (0); } + if ((pf->opts & PF_OPT_OPTIMIZE) && rs_num == PF_RULESET_FILTER) + pfctl_optimize_ruleset(pf, rs); + + while ((r = TAILQ_FIRST(rs->rules[rs_num].active.ptr)) != NULL) { + TAILQ_REMOVE(rs->rules[rs_num].active.ptr, r, entries); + if ((error = pfctl_load_rule(pf, path, r, depth))) + goto error; + if (r->anchor && r->anchor->name[0] == '_') { + if ((error = pfctl_load_ruleset(pf, path, + &r->anchor->ruleset, rs_num, depth + 1))) + goto error; + INDENT(depth, (pf->opts & PF_OPT_VERBOSE)); + if (pf->opts & PF_OPT_VERBOSE) + printf("}\n"); + } + free(r); + } + path[len] = '\0'; + return (0); + + error: + path[len] = '\0'; + return (error); + +} + +int +pfctl_load_rule(struct pfctl *pf, char *path, struct pf_rule *r, int depth) +{ + u_int8_t rs_num = pf_get_ruleset_number(r->action); + struct pfioc_rule pr; + if ((pf->opts & PF_OPT_NOACTION) == 0) { bzero(&pr, sizeof(pr)); - if (strlcpy(pr.anchor, pf->anchor, sizeof(pr.anchor)) >= + if (strlcpy(pr.anchor, path, sizeof(pr.anchor)) >= sizeof(pr.anchor)) - errx(1, "pfctl_add_rule: strlcpy"); + errx(1, "pfctl_load_rule: strlcpy"); if (pfctl_add_pool(pf, &r->rpool, r->af)) return (1); - pr.ticket = pfctl_get_ticket(pf->trans, rs_num, pf->anchor); + pr.ticket = pfctl_get_ticket(pf->trans, rs_num, path); pr.pool_ticket = pf->paddr.ticket; memcpy(&pr.rule, r, sizeof(pr.rule)); - strlcpy(pr.anchor_call, anchor_call, sizeof(pr.anchor_call)); + if (r->anchor && strlcpy(pr.anchor_call, r->anchor->name, + sizeof(pr.anchor_call))); if (ioctl(pf->dev, DIOCADDRULE, &pr)) err(1, "DIOCADDRULE"); } - if (pf->opts & PF_OPT_VERBOSE) - print_rule(r, anchor_call, pf->opts & PF_OPT_VERBOSE2); + if (pf->opts & PF_OPT_VERBOSE) { + INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2)); + print_rule(r, r->anchor ? r->anchor->name : "", + pf->opts & PF_OPT_VERBOSE2); + } pfctl_clear_pool(&r->rpool); return (0); } @@ -1064,22 +1151,30 @@ pfctl_rules(int dev, char *filename, FILE *fin, int opts, char *anchorname, struct pfr_buffer *t, buf; struct pfioc_altq pa; struct pfctl pf; + struct pf_ruleset *rs; struct pfr_table trs; + char *path; int osize; + RB_INIT(&pf_anchors); + memset(&pf_main_anchor, 0, sizeof(pf_main_anchor)); + pf_init_ruleset(&pf_main_anchor.ruleset); + pf_main_anchor.ruleset.anchor = &pf_main_anchor; if (trans == NULL) { - bzero(&buf, sizeof(buf)); - buf.pfrb_type = PFRB_TRANS; - t = &buf; - osize = 0; + bzero(&buf, sizeof(buf)); + buf.pfrb_type = PFRB_TRANS; + t = &buf; + osize = 0; } else { - t = trans; - osize = t->pfrb_size; + t = trans; + osize = t->pfrb_size; } memset(&pa, 0, sizeof(pa)); memset(&pf, 0, sizeof(pf)); memset(&trs, 0, sizeof(trs)); + if ((path = calloc(1, MAXPATHLEN)) == NULL) + ERRX("pfctl_rules: calloc"); if (strlcpy(trs.pfrt_anchor, anchorname, sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor)) ERRX("pfctl_rules: strlcpy"); @@ -1087,44 +1182,32 @@ pfctl_rules(int dev, char *filename, FILE *fin, int opts, char *anchorname, pf.dev = dev; pf.opts = opts; pf.loadopt = loadopt; + pf.anchor = pf_find_or_create_ruleset(anchorname)->anchor; + rs = &pf.anchor->ruleset; + pf.astack[0] = pf.anchor; + pf.asd = 0; if (anchorname[0]) pf.loadopt &= ~PFCTL_FLAG_ALTQ; pf.paltq = &pa; pf.trans = t; - pf.rule_nr = 0; - pf.anchor = anchorname; - TAILQ_INIT(&pf.opt_queue); pfctl_init_options(&pf); if ((opts & PF_OPT_NOACTION) == 0) { - if ((pf.loadopt & PFCTL_FLAG_NAT) != 0) { - if (pfctl_add_trans(t, PF_RULESET_NAT, anchorname) || - pfctl_add_trans(t, PF_RULESET_BINAT, anchorname) || - pfctl_add_trans(t, PF_RULESET_RDR, anchorname)) - ERR("pfctl_rules"); - } - if (((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0))) { - if (pfctl_add_trans(t, PF_RULESET_ALTQ, anchorname)) - ERR("pfctl_rules"); - } - if ((pf.loadopt & PFCTL_FLAG_FILTER) != 0) { - if (pfctl_add_trans(t, PF_RULESET_SCRUB, anchorname) || - pfctl_add_trans(t, PF_RULESET_FILTER, anchorname)) - ERR("pfctl_rules"); - } - if (pf.loadopt & PFCTL_FLAG_TABLE) { - if (pfctl_add_trans(t, PF_RULESET_TABLE, anchorname)) - ERR("pfctl_rules"); - } - if (pfctl_trans(dev, t, DIOCXBEGIN, osize)) - ERR("DIOCXBEGIN"); + /* + * XXX For the time being we need to open transactions for + * the main ruleset before parsing, because tables are still + * loaded at parse time. + */ + if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor)) + ERRX("pfctl_rules"); if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ)) - pa.ticket = pfctl_get_ticket(t, PF_RULESET_ALTQ, - anchorname); + pa.ticket = + pfctl_get_ticket(t, PF_RULESET_ALTQ, anchorname); if (pf.loadopt & PFCTL_FLAG_TABLE) - pf.tticket = pfctl_get_ticket(t, PF_RULESET_TABLE, - anchorname); + pf.astack[0]->ruleset.tticket = + pfctl_get_ticket(t, PF_RULESET_TABLE, anchorname); } + if (parse_rules(fin, &pf) < 0) { if ((opts & PF_OPT_NOACTION) == 0) ERRX("Syntax error in config file: " @@ -1132,9 +1215,19 @@ pfctl_rules(int dev, char *filename, FILE *fin, int opts, char *anchorname, else goto _error; } - if (pf.opts & PF_OPT_OPTIMIZE) { - if (pfctl_optimize_rules(&pf)) - ERRX("Failed to optimize ruleset: pf rules not loaded"); + + if ((pf.loadopt & PFCTL_FLAG_FILTER && + (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) || + (pf.loadopt & PFCTL_FLAG_NAT && + (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) || + pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) || + pfctl_load_ruleset(&pf, path, rs, PF_RULESET_BINAT, 0))) || + (pf.loadopt & PFCTL_FLAG_FILTER && + pfctl_load_ruleset(&pf, path, rs, PF_RULESET_FILTER, 0))) { + if ((opts & PF_OPT_NOACTION) == 0) + ERRX("Unable to load rules into kernel"); + else + goto _error; } if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0)) @@ -1155,7 +1248,7 @@ pfctl_rules(int dev, char *filename, FILE *fin, int opts, char *anchorname, if (!anchorname[0]) if (pfctl_load_options(&pf)) goto _error; - if (pfctl_trans(dev, t, DIOCXCOMMIT, 0)) + if (pfctl_trans(dev, t, DIOCXCOMMIT, osize)) ERR("DIOCXCOMMIT"); } return (0); @@ -1163,7 +1256,7 @@ pfctl_rules(int dev, char *filename, FILE *fin, int opts, char *anchorname, _error: if (trans == NULL) { /* main ruleset */ if ((opts & PF_OPT_NOACTION) == 0) - if (pfctl_trans(dev, t, DIOCXROLLBACK, 0)) + if (pfctl_trans(dev, t, DIOCXROLLBACK, osize)) err(1, "DIOCXROLLBACK"); exit(1); } else { /* sub ruleset */ @@ -1824,17 +1917,22 @@ main(int argc, char *argv[]) error = 1; if (showopt != NULL) { + char *path; + + if ((path = calloc(1, MAXPATHLEN)) == NULL) + errx(1, "pfctl: calloc"); + switch (*showopt) { case 'A': pfctl_show_anchors(dev, opts, anchorname); break; case 'r': pfctl_load_fingerprints(dev, opts); - pfctl_show_rules(dev, opts, 0, anchorname); + pfctl_show_rules(dev, path, opts, 0, anchorname, 0); break; case 'l': pfctl_load_fingerprints(dev, opts); - pfctl_show_rules(dev, opts, 1, anchorname); + pfctl_show_rules(dev, path, opts, 1, anchorname, 0); break; case 'n': pfctl_load_fingerprints(dev, opts); @@ -1864,12 +1962,12 @@ main(int argc, char *argv[]) pfctl_load_fingerprints(dev, opts); pfctl_show_nat(dev, opts, anchorname); - pfctl_show_rules(dev, opts, 0, anchorname); + pfctl_show_rules(dev, path, opts, 0, anchorname, 0); pfctl_show_altq(dev, ifaceopt, opts, 0); pfctl_show_states(dev, ifaceopt, opts); pfctl_show_src_nodes(dev, opts); pfctl_show_status(dev, opts); - pfctl_show_rules(dev, opts, 1, anchorname); + pfctl_show_rules(dev, path, opts, 1, anchorname, 0); pfctl_show_timeouts(dev, opts); pfctl_show_limits(dev, opts); pfctl_show_tables(anchorname, opts); diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c index a63494d553f..e041073569d 100644 --- a/sbin/pfctl/pfctl_optimize.c +++ b/sbin/pfctl/pfctl_optimize.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfctl_optimize.c,v 1.11 2006/10/25 14:50:21 henning Exp $ */ +/* $OpenBSD: pfctl_optimize.c,v 1.12 2006/10/28 14:29:05 mcbride Exp $ */ /* * Copyright (c) 2004 Mike Frantzen <frantzen@openbsd.org> @@ -112,6 +112,7 @@ struct pf_rule_field { PF_RULE_FIELD(max_src_states, BARRIER), PF_RULE_FIELD(max_src_conn, BARRIER), PF_RULE_FIELD(max_src_conn_rate, BARRIER), + PF_RULE_FIELD(anchor, BARRIER), /* for now */ /* * These fields must be the same between all rules in the same superblock. @@ -181,7 +182,6 @@ struct pf_rule_field { PF_RULE_FIELD(packets, DC), PF_RULE_FIELD(bytes, DC), PF_RULE_FIELD(kif, DC), - PF_RULE_FIELD(anchor, DC), PF_RULE_FIELD(states, DC), PF_RULE_FIELD(src_nodes, DC), PF_RULE_FIELD(nr, DC), @@ -255,22 +255,49 @@ int table_identifier; int -pfctl_optimize_rules(struct pfctl *pf) +pfctl_optimize_ruleset(struct pfctl *pf, struct pf_ruleset *rs) { struct superblocks superblocks; + struct pf_opt_queue opt_queue; struct superblock *block; struct pf_opt_rule *por; - int nr; + struct pf_rule *r; + struct pf_rulequeue *old_rules; DEBUG("optimizing ruleset"); memset(&table_buffer, 0, sizeof(table_buffer)); skip_init(); + TAILQ_INIT(&opt_queue); - if (TAILQ_FIRST(&pf->opt_queue)) - nr = TAILQ_FIRST(&pf->opt_queue)->por_rule.nr; + old_rules = rs->rules[PF_RULESET_FILTER].active.ptr; + rs->rules[PF_RULESET_FILTER].active.ptr = + rs->rules[PF_RULESET_FILTER].inactive.ptr; + rs->rules[PF_RULESET_FILTER].inactive.ptr = old_rules; + + /* + * XXX expanding the pf_opt_rule format throughout pfctl might allow + * us to avoid all this copying. + */ + while ((r = TAILQ_FIRST(rs->rules[PF_RULESET_FILTER].inactive.ptr)) + != NULL) { + TAILQ_REMOVE(rs->rules[PF_RULESET_FILTER].inactive.ptr, r, + entries); + if ((por = calloc(1, sizeof(*por))) == NULL) + err(1, "calloc"); + memcpy(&por->por_rule, r, sizeof(*r)); + if (TAILQ_FIRST(&r->rpool.list) != NULL) { + TAILQ_INIT(&por->por_rule.rpool.list); + pfctl_move_pool(&r->rpool, &por->por_rule.rpool); + } else + bzero(&por->por_rule.rpool, + sizeof(por->por_rule.rpool)); + + + TAILQ_INSERT_TAIL(&opt_queue, por, por_entry); + } TAILQ_INIT(&superblocks); - if (construct_superblocks(pf, &pf->opt_queue, &superblocks)) + if (construct_superblocks(pf, &opt_queue, &superblocks)) goto error; if (pf->opts & PF_OPT_OPTIMIZE_PROFILE) { @@ -283,24 +310,21 @@ pfctl_optimize_rules(struct pfctl *pf) goto error; } - - /* - * Optimizations are done so we turn off the optimization flag and - * put the rules right back into the regular codepath. - */ - pf->opts &= ~PF_OPT_OPTIMIZE; - + rs->anchor->refcnt = 0; while ((block = TAILQ_FIRST(&superblocks))) { TAILQ_REMOVE(&superblocks, block, sb_entry); while ((por = TAILQ_FIRST(&block->sb_rules))) { TAILQ_REMOVE(&block->sb_rules, por, por_entry); - por->por_rule.nr = nr++; - if (pfctl_add_rule(pf, &por->por_rule, - por->por_anchor)) { - free(por); - goto error; - } + por->por_rule.nr = rs->anchor->refcnt++; + if ((r = calloc(1, sizeof(*r))) == NULL) + err(1, "calloc"); + memcpy(r, &por->por_rule, sizeof(*r)); + TAILQ_INIT(&r->rpool.list); + pfctl_move_pool(&por->por_rule.rpool, &r->rpool); + TAILQ_INSERT_TAIL( + rs->rules[PF_RULESET_FILTER].active.ptr, + r, entries); free(por); } free(block); @@ -309,8 +333,8 @@ pfctl_optimize_rules(struct pfctl *pf) return (0); error: - while ((por = TAILQ_FIRST(&pf->opt_queue))) { - TAILQ_REMOVE(&pf->opt_queue, por, por_entry); + while ((por = TAILQ_FIRST(&opt_queue))) { + TAILQ_REMOVE(&opt_queue, por, por_entry); if (por->por_src_tbl) { pfr_buf_clear(por->por_src_tbl->pt_buf); free(por->por_src_tbl->pt_buf); @@ -379,7 +403,8 @@ optimize_superblock(struct pfctl *pf, struct superblock *block) printf("--- Superblock ---\n"); TAILQ_FOREACH(por, &block->sb_rules, por_entry) { printf(" "); - print_rule(&por->por_rule, por->por_anchor, 1); + print_rule(&por->por_rule, por->por_rule.anchor ? + por->por_rule.anchor->name : "", 1); } #endif /* OPT_DEBUG */ @@ -868,6 +893,7 @@ load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks) DEBUG("Loading %d active rules for a feedback profile", mnr); for (nr = 0; nr < mnr; ++nr) { + struct pf_ruleset *rs; if ((por = calloc(1, sizeof(*por))) == NULL) { warn("calloc"); return (1); @@ -878,8 +904,8 @@ load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks) return (1); } memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule)); - strlcpy(por->por_anchor, pr.anchor_call, - sizeof(por->por_anchor)); + rs = pf_find_or_create_ruleset(pr.anchor_call); + por->por_rule.anchor = rs->anchor; if (TAILQ_EMPTY(&por->por_rule.rpool.list)) memset(&por->por_rule.rpool, 0, sizeof(por->por_rule.rpool)); @@ -1286,8 +1312,8 @@ again: tablenum++; - if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1, pf->anchor, - tbl->pt_buf, pf->tticket)) { + if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1, + pf->anchor->name, tbl->pt_buf, pf->anchor->ruleset.tticket)) { warn("failed to create table %s", tbl->pt_name); return (1); } @@ -1386,9 +1412,8 @@ superblock_inclusive(struct superblock *block, struct pf_opt_rule *por) } } - /* 'anchor' heads and per-rule src-track are also hard breaks */ - if (por->por_anchor[0] != '\0' || - (por->por_rule.rule_flag & PFRULE_RULESRCTRACK)) + /* per-rule src-track is also a hard break */ + if (por->por_rule.rule_flag & PFRULE_RULESRCTRACK) return (0); /* @@ -1414,8 +1439,7 @@ superblock_inclusive(struct superblock *block, struct pf_opt_rule *por) comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, NOMERGE); comparable_rule(&b, &por->por_rule, NOMERGE); - if (strcmp(TAILQ_FIRST(&block->sb_rules)->por_anchor, - por->por_anchor) == 0 && memcmp(&a, &b, sizeof(a)) == 0) + if (memcmp(&a, &b, sizeof(a)) == 0) return (1); #ifdef OPT_DEBUG diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index 20e67130427..4503048e069 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfctl_parser.c,v 1.232 2006/10/25 11:28:36 henning Exp $ */ +/* $OpenBSD: pfctl_parser.c,v 1.233 2006/10/28 14:29:05 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -668,15 +668,21 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose) "anchor", "nat-anchor", "nat-anchor", "binat-anchor", "binat-anchor", "rdr-anchor", "rdr-anchor" }; int i, opts; + int brace = 0; if (verbose) printf("@%d ", r->nr); if (r->action > PF_NORDR) printf("action(%d)", r->action); - else if (anchor_call[0]) - printf("%s \"%s\"", anchortypes[r->action], - anchor_call); - else { + else if (anchor_call[0]) { + if (anchor_call[0] == '_') { + brace++; + printf("%s", anchortypes[r->action]); + } else + printf("%s \"%s\"", anchortypes[r->action], + anchor_call); + + } else { printf("%s", actiontypes[r->action]); if (r->natpass) printf(" pass"); @@ -984,6 +990,8 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose) print_pool(&r->rpool, r->rpool.proxy_port[0], r->rpool.proxy_port[1], r->af, r->action); } + if (brace) + printf(" {"); printf("\n"); } diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index c22a76e8389..a43ad9e3629 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfctl_parser.h,v 1.83 2005/11/17 20:52:39 dhartmei Exp $ */ +/* $OpenBSD: pfctl_parser.h,v 1.84 2006/10/28 14:29:05 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -63,24 +63,24 @@ } struct pfr_buffer; /* forward definition */ -struct pf_opt_rule; -TAILQ_HEAD(pf_opt_queue, pf_opt_rule); struct pfctl { int dev; int opts; int loadopt; - u_int32_t tticket; /* table ticket */ + int asd; /* anchor stack depth */ + int bn; /* brace number */ + int brace; int tdirty; /* kernel dirty */ - u_int32_t rule_nr; +#define PFCTL_ANCHOR_STACK_DEPTH 64 + struct pf_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH]; struct pfioc_pooladdr paddr; struct pfioc_altq *paltq; struct pfioc_queue *pqueue; struct pfr_buffer *trans; - const char *anchor; + struct pf_anchor *anchor, *alast; const char *ruleset; - struct pf_opt_queue opt_queue; /* 'set foo' options */ u_int32_t timeout[PFTM_MAX]; @@ -176,19 +176,20 @@ struct pf_opt_rule { struct pf_rule por_rule; struct pf_opt_tbl *por_src_tbl; struct pf_opt_tbl *por_dst_tbl; - char por_anchor[MAXPATHLEN]; u_int64_t por_profile_count; TAILQ_ENTRY(pf_opt_rule) por_entry; TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT]; }; +TAILQ_HEAD(pf_opt_queue, pf_opt_rule); int pfctl_rules(int, char *, FILE *, int, char *, struct pfr_buffer *); -int pfctl_optimize_rules(struct pfctl *); +int pfctl_optimize_ruleset(struct pfctl *, struct pf_ruleset *); int pfctl_add_rule(struct pfctl *, struct pf_rule *, const char *); int pfctl_add_altq(struct pfctl *, struct pf_altq *); int pfctl_add_pool(struct pfctl *, struct pf_pool *, sa_family_t); +void pfctl_move_pool(struct pf_pool *, struct pf_pool *); void pfctl_clear_pool(struct pf_pool *); int pfctl_set_timeout(struct pfctl *, const char *, int, int); |