1ed1f0be2SJan Lentfer /* $OpenBSD: pfctl_optimize.c,v 1.18 2008/05/07 06:23:30 markus Exp $ */
270224baaSJan Lentfer
370224baaSJan Lentfer /*
470224baaSJan Lentfer * Copyright (c) 2004 Mike Frantzen <frantzen@openbsd.org>
570224baaSJan Lentfer *
670224baaSJan Lentfer * Permission to use, copy, modify, and distribute this software for any
770224baaSJan Lentfer * purpose with or without fee is hereby granted, provided that the above
870224baaSJan Lentfer * copyright notice and this permission notice appear in all copies.
970224baaSJan Lentfer *
1070224baaSJan Lentfer * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1170224baaSJan Lentfer * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1270224baaSJan Lentfer * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1370224baaSJan Lentfer * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1470224baaSJan Lentfer * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1570224baaSJan Lentfer * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1670224baaSJan Lentfer * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1770224baaSJan Lentfer */
1870224baaSJan Lentfer
1970224baaSJan Lentfer #include <sys/types.h>
2070224baaSJan Lentfer #include <sys/ioctl.h>
2170224baaSJan Lentfer #include <sys/socket.h>
2270224baaSJan Lentfer
2370224baaSJan Lentfer #include <net/if.h>
2470224baaSJan Lentfer #include <net/pf/pfvar.h>
2570224baaSJan Lentfer
2670224baaSJan Lentfer #include <netinet/in.h>
2770224baaSJan Lentfer #include <arpa/inet.h>
2870224baaSJan Lentfer
2970224baaSJan Lentfer #include <assert.h>
3070224baaSJan Lentfer #include <ctype.h>
3170224baaSJan Lentfer #include <err.h>
3270224baaSJan Lentfer #include <errno.h>
3370224baaSJan Lentfer #include <stddef.h>
3470224baaSJan Lentfer #include <stdio.h>
3570224baaSJan Lentfer #include <stdlib.h>
3670224baaSJan Lentfer #include <string.h>
3770224baaSJan Lentfer
3870224baaSJan Lentfer #include "pfctl_parser.h"
3970224baaSJan Lentfer #include "pfctl.h"
4070224baaSJan Lentfer
4170224baaSJan Lentfer /* The size at which a table becomes faster than individual rules */
4270224baaSJan Lentfer #define TABLE_THRESHOLD 6
4370224baaSJan Lentfer
4470224baaSJan Lentfer
4570224baaSJan Lentfer /* #define OPT_DEBUG 1 */
4670224baaSJan Lentfer #ifdef OPT_DEBUG
4770224baaSJan Lentfer # define DEBUG(str, v...) \
487fd4e1a1SSascha Wildner printf("%s: " str "\n", __func__ , ## v)
4970224baaSJan Lentfer #else
5070224baaSJan Lentfer # define DEBUG(str, v...) ((void)0)
5170224baaSJan Lentfer #endif
5270224baaSJan Lentfer
5370224baaSJan Lentfer
5470224baaSJan Lentfer /*
5570224baaSJan Lentfer * A container that lets us sort a superblock to optimize the skip step jumps
5670224baaSJan Lentfer */
5770224baaSJan Lentfer struct pf_skip_step {
5870224baaSJan Lentfer int ps_count; /* number of items */
5970224baaSJan Lentfer TAILQ_HEAD( , pf_opt_rule) ps_rules;
6070224baaSJan Lentfer TAILQ_ENTRY(pf_skip_step) ps_entry;
6170224baaSJan Lentfer };
6270224baaSJan Lentfer
6370224baaSJan Lentfer
6470224baaSJan Lentfer /*
6570224baaSJan Lentfer * A superblock is a block of adjacent rules of similar action. If there
6670224baaSJan Lentfer * are five PASS rules in a row, they all become members of a superblock.
6770224baaSJan Lentfer * Once we have a superblock, we are free to re-order any rules within it
6870224baaSJan Lentfer * in order to improve performance; if a packet is passed, it doesn't matter
6970224baaSJan Lentfer * who passed it.
7070224baaSJan Lentfer */
7170224baaSJan Lentfer struct superblock {
7270224baaSJan Lentfer TAILQ_HEAD( , pf_opt_rule) sb_rules;
7370224baaSJan Lentfer TAILQ_ENTRY(superblock) sb_entry;
7470224baaSJan Lentfer struct superblock *sb_profiled_block;
7570224baaSJan Lentfer TAILQ_HEAD(skiplist, pf_skip_step) sb_skipsteps[PF_SKIP_COUNT];
7670224baaSJan Lentfer };
7770224baaSJan Lentfer TAILQ_HEAD(superblocks, superblock);
7870224baaSJan Lentfer
7970224baaSJan Lentfer
8070224baaSJan Lentfer /*
8170224baaSJan Lentfer * Description of the PF rule structure.
8270224baaSJan Lentfer */
8370224baaSJan Lentfer enum {
8470224baaSJan Lentfer BARRIER, /* the presence of the field puts the rule in it's own block */
8570224baaSJan Lentfer BREAK, /* the field may not differ between rules in a superblock */
8670224baaSJan Lentfer NOMERGE, /* the field may not differ between rules when combined */
8770224baaSJan Lentfer COMBINED, /* the field may itself be combined with other rules */
8870224baaSJan Lentfer DC, /* we just don't care about the field */
8970224baaSJan Lentfer NEVER}; /* we should never see this field set?!? */
9070224baaSJan Lentfer struct pf_rule_field {
9170224baaSJan Lentfer const char *prf_name;
9270224baaSJan Lentfer int prf_type;
9370224baaSJan Lentfer size_t prf_offset;
9470224baaSJan Lentfer size_t prf_size;
9570224baaSJan Lentfer } pf_rule_desc[] = {
9670224baaSJan Lentfer #define PF_RULE_FIELD(field, ty) \
9770224baaSJan Lentfer {#field, \
9870224baaSJan Lentfer ty, \
9970224baaSJan Lentfer offsetof(struct pf_rule, field), \
10070224baaSJan Lentfer sizeof(((struct pf_rule *)0)->field)}
10170224baaSJan Lentfer
10270224baaSJan Lentfer
10370224baaSJan Lentfer /*
10470224baaSJan Lentfer * The presence of these fields in a rule put the rule in it's own
10570224baaSJan Lentfer * superblock. Thus it will not be optimized. It also prevents the
10670224baaSJan Lentfer * rule from being re-ordered at all.
10770224baaSJan Lentfer */
10870224baaSJan Lentfer PF_RULE_FIELD(label, BARRIER),
10970224baaSJan Lentfer PF_RULE_FIELD(prob, BARRIER),
11070224baaSJan Lentfer PF_RULE_FIELD(max_states, BARRIER),
11170224baaSJan Lentfer PF_RULE_FIELD(max_src_nodes, BARRIER),
11270224baaSJan Lentfer PF_RULE_FIELD(max_src_states, BARRIER),
11370224baaSJan Lentfer PF_RULE_FIELD(max_src_conn, BARRIER),
11470224baaSJan Lentfer PF_RULE_FIELD(max_src_conn_rate, BARRIER),
11570224baaSJan Lentfer PF_RULE_FIELD(anchor, BARRIER), /* for now */
11670224baaSJan Lentfer
11770224baaSJan Lentfer /*
11870224baaSJan Lentfer * These fields must be the same between all rules in the same superblock.
11970224baaSJan Lentfer * These rules are allowed to be re-ordered but only among like rules.
12070224baaSJan Lentfer * For instance we can re-order all 'tag "foo"' rules because they have the
12170224baaSJan Lentfer * same tag. But we can not re-order between a 'tag "foo"' and a
12270224baaSJan Lentfer * 'tag "bar"' since that would change the meaning of the ruleset.
12370224baaSJan Lentfer */
12470224baaSJan Lentfer PF_RULE_FIELD(tagname, BREAK),
12570224baaSJan Lentfer PF_RULE_FIELD(keep_state, BREAK),
12670224baaSJan Lentfer PF_RULE_FIELD(qname, BREAK),
12770224baaSJan Lentfer PF_RULE_FIELD(pqname, BREAK),
12870224baaSJan Lentfer PF_RULE_FIELD(rt, BREAK),
12970224baaSJan Lentfer PF_RULE_FIELD(allow_opts, BREAK),
13070224baaSJan Lentfer PF_RULE_FIELD(rule_flag, BREAK),
13170224baaSJan Lentfer PF_RULE_FIELD(action, BREAK),
13270224baaSJan Lentfer PF_RULE_FIELD(log, BREAK),
13370224baaSJan Lentfer PF_RULE_FIELD(quick, BREAK),
13470224baaSJan Lentfer PF_RULE_FIELD(return_ttl, BREAK),
13570224baaSJan Lentfer PF_RULE_FIELD(overload_tblname, BREAK),
13670224baaSJan Lentfer PF_RULE_FIELD(flush, BREAK),
13770224baaSJan Lentfer PF_RULE_FIELD(rpool, BREAK),
13870224baaSJan Lentfer PF_RULE_FIELD(logif, BREAK),
13970224baaSJan Lentfer
14070224baaSJan Lentfer /*
14170224baaSJan Lentfer * Any fields not listed in this structure act as BREAK fields
14270224baaSJan Lentfer */
14370224baaSJan Lentfer
14470224baaSJan Lentfer
14570224baaSJan Lentfer /*
14670224baaSJan Lentfer * These fields must not differ when we merge two rules together but
14770224baaSJan Lentfer * their difference isn't enough to put the rules in different superblocks.
14870224baaSJan Lentfer * There are no problems re-ordering any rules with these fields.
14970224baaSJan Lentfer */
15070224baaSJan Lentfer PF_RULE_FIELD(af, NOMERGE),
15170224baaSJan Lentfer PF_RULE_FIELD(ifnot, NOMERGE),
15270224baaSJan Lentfer PF_RULE_FIELD(ifname, NOMERGE), /* hack for IF groups */
15370224baaSJan Lentfer PF_RULE_FIELD(match_tag_not, NOMERGE),
15470224baaSJan Lentfer PF_RULE_FIELD(match_tagname, NOMERGE),
15570224baaSJan Lentfer PF_RULE_FIELD(os_fingerprint, NOMERGE),
15670224baaSJan Lentfer PF_RULE_FIELD(timeout, NOMERGE),
15770224baaSJan Lentfer PF_RULE_FIELD(return_icmp, NOMERGE),
15870224baaSJan Lentfer PF_RULE_FIELD(return_icmp6, NOMERGE),
15970224baaSJan Lentfer PF_RULE_FIELD(uid, NOMERGE),
16070224baaSJan Lentfer PF_RULE_FIELD(gid, NOMERGE),
16170224baaSJan Lentfer PF_RULE_FIELD(direction, NOMERGE),
16270224baaSJan Lentfer PF_RULE_FIELD(proto, NOMERGE),
16370224baaSJan Lentfer PF_RULE_FIELD(type, NOMERGE),
16470224baaSJan Lentfer PF_RULE_FIELD(code, NOMERGE),
16570224baaSJan Lentfer PF_RULE_FIELD(flags, NOMERGE),
16670224baaSJan Lentfer PF_RULE_FIELD(flagset, NOMERGE),
16770224baaSJan Lentfer PF_RULE_FIELD(tos, NOMERGE),
16870224baaSJan Lentfer PF_RULE_FIELD(src.port, NOMERGE),
16970224baaSJan Lentfer PF_RULE_FIELD(dst.port, NOMERGE),
17070224baaSJan Lentfer PF_RULE_FIELD(src.port_op, NOMERGE),
17170224baaSJan Lentfer PF_RULE_FIELD(dst.port_op, NOMERGE),
17270224baaSJan Lentfer PF_RULE_FIELD(src.neg, NOMERGE),
17370224baaSJan Lentfer PF_RULE_FIELD(dst.neg, NOMERGE),
17470224baaSJan Lentfer
17570224baaSJan Lentfer /* These fields can be merged */
17670224baaSJan Lentfer PF_RULE_FIELD(src.addr, COMBINED),
17770224baaSJan Lentfer PF_RULE_FIELD(dst.addr, COMBINED),
17870224baaSJan Lentfer
17970224baaSJan Lentfer /* We just don't care about these fields. They're set by the kernel */
18070224baaSJan Lentfer PF_RULE_FIELD(skip, DC),
18170224baaSJan Lentfer PF_RULE_FIELD(evaluations, DC),
18270224baaSJan Lentfer PF_RULE_FIELD(packets, DC),
18370224baaSJan Lentfer PF_RULE_FIELD(bytes, DC),
18470224baaSJan Lentfer PF_RULE_FIELD(kif, DC),
185ed1f0be2SJan Lentfer PF_RULE_FIELD(states_cur, DC),
186ed1f0be2SJan Lentfer PF_RULE_FIELD(states_tot, DC),
18770224baaSJan Lentfer PF_RULE_FIELD(src_nodes, DC),
18870224baaSJan Lentfer PF_RULE_FIELD(nr, DC),
18970224baaSJan Lentfer PF_RULE_FIELD(entries, DC),
19070224baaSJan Lentfer PF_RULE_FIELD(qid, DC),
19170224baaSJan Lentfer PF_RULE_FIELD(pqid, DC),
19270224baaSJan Lentfer PF_RULE_FIELD(anchor_relative, DC),
19370224baaSJan Lentfer PF_RULE_FIELD(anchor_wildcard, DC),
19470224baaSJan Lentfer PF_RULE_FIELD(tag, DC),
19570224baaSJan Lentfer PF_RULE_FIELD(match_tag, DC),
19670224baaSJan Lentfer PF_RULE_FIELD(overload_tbl, DC),
19770224baaSJan Lentfer
19870224baaSJan Lentfer /* These fields should never be set in a PASS/BLOCK rule */
19970224baaSJan Lentfer PF_RULE_FIELD(natpass, NEVER),
20070224baaSJan Lentfer PF_RULE_FIELD(max_mss, NEVER),
20170224baaSJan Lentfer PF_RULE_FIELD(min_ttl, NEVER),
202ed1f0be2SJan Lentfer PF_RULE_FIELD(set_tos, NEVER),
20370224baaSJan Lentfer };
20470224baaSJan Lentfer
20570224baaSJan Lentfer
20670224baaSJan Lentfer
20770224baaSJan Lentfer int add_opt_table(struct pfctl *, struct pf_opt_tbl **, sa_family_t,
20870224baaSJan Lentfer struct pf_rule_addr *);
20970224baaSJan Lentfer int addrs_combineable(struct pf_rule_addr *, struct pf_rule_addr *);
21070224baaSJan Lentfer int addrs_equal(struct pf_rule_addr *, struct pf_rule_addr *);
21170224baaSJan Lentfer int block_feedback(struct pfctl *, struct superblock *);
21270224baaSJan Lentfer int combine_rules(struct pfctl *, struct superblock *);
21370224baaSJan Lentfer void comparable_rule(struct pf_rule *, const struct pf_rule *, int);
21470224baaSJan Lentfer int construct_superblocks(struct pfctl *, struct pf_opt_queue *,
21570224baaSJan Lentfer struct superblocks *);
21670224baaSJan Lentfer void exclude_supersets(struct pf_rule *, struct pf_rule *);
21770224baaSJan Lentfer int interface_group(const char *);
21870224baaSJan Lentfer int load_feedback_profile(struct pfctl *, struct superblocks *);
21970224baaSJan Lentfer int optimize_superblock(struct pfctl *, struct superblock *);
22070224baaSJan Lentfer int pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *);
22170224baaSJan Lentfer void remove_from_skipsteps(struct skiplist *, struct superblock *,
22270224baaSJan Lentfer struct pf_opt_rule *, struct pf_skip_step *);
22370224baaSJan Lentfer int remove_identical_rules(struct pfctl *, struct superblock *);
22470224baaSJan Lentfer int reorder_rules(struct pfctl *, struct superblock *, int);
22570224baaSJan Lentfer int rules_combineable(struct pf_rule *, struct pf_rule *);
22670224baaSJan Lentfer void skip_append(struct superblock *, int, struct pf_skip_step *,
22770224baaSJan Lentfer struct pf_opt_rule *);
22870224baaSJan Lentfer int skip_compare(int, struct pf_skip_step *, struct pf_opt_rule *);
22970224baaSJan Lentfer void skip_init(void);
23070224baaSJan Lentfer int skip_cmp_af(struct pf_rule *, struct pf_rule *);
23170224baaSJan Lentfer int skip_cmp_dir(struct pf_rule *, struct pf_rule *);
23270224baaSJan Lentfer int skip_cmp_dst_addr(struct pf_rule *, struct pf_rule *);
23370224baaSJan Lentfer int skip_cmp_dst_port(struct pf_rule *, struct pf_rule *);
23470224baaSJan Lentfer int skip_cmp_ifp(struct pf_rule *, struct pf_rule *);
23570224baaSJan Lentfer int skip_cmp_proto(struct pf_rule *, struct pf_rule *);
23670224baaSJan Lentfer int skip_cmp_src_addr(struct pf_rule *, struct pf_rule *);
23770224baaSJan Lentfer int skip_cmp_src_port(struct pf_rule *, struct pf_rule *);
23870224baaSJan Lentfer int superblock_inclusive(struct superblock *, struct pf_opt_rule *);
23970224baaSJan Lentfer void superblock_free(struct pfctl *, struct superblock *);
24070224baaSJan Lentfer
24170224baaSJan Lentfer
24270224baaSJan Lentfer int (*skip_comparitors[PF_SKIP_COUNT])(struct pf_rule *, struct pf_rule *);
24370224baaSJan Lentfer const char *skip_comparitors_names[PF_SKIP_COUNT];
24470224baaSJan Lentfer #define PF_SKIP_COMPARITORS { \
24570224baaSJan Lentfer { "ifp", PF_SKIP_IFP, skip_cmp_ifp }, \
24670224baaSJan Lentfer { "dir", PF_SKIP_DIR, skip_cmp_dir }, \
24770224baaSJan Lentfer { "af", PF_SKIP_AF, skip_cmp_af }, \
24870224baaSJan Lentfer { "proto", PF_SKIP_PROTO, skip_cmp_proto }, \
24970224baaSJan Lentfer { "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr }, \
25070224baaSJan Lentfer { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \
25170224baaSJan Lentfer { "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr }, \
25270224baaSJan Lentfer { "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port } \
25370224baaSJan Lentfer }
25470224baaSJan Lentfer
25570224baaSJan Lentfer struct pfr_buffer table_buffer;
25670224baaSJan Lentfer int table_identifier;
25770224baaSJan Lentfer
25870224baaSJan Lentfer
25970224baaSJan Lentfer int
pfctl_optimize_ruleset(struct pfctl * pf,struct pf_ruleset * rs)26070224baaSJan Lentfer pfctl_optimize_ruleset(struct pfctl *pf, struct pf_ruleset *rs)
26170224baaSJan Lentfer {
26270224baaSJan Lentfer struct superblocks superblocks;
26370224baaSJan Lentfer struct pf_opt_queue opt_queue;
26470224baaSJan Lentfer struct superblock *block;
26570224baaSJan Lentfer struct pf_opt_rule *por;
26670224baaSJan Lentfer struct pf_rule *r;
26770224baaSJan Lentfer struct pf_rulequeue *old_rules;
26870224baaSJan Lentfer
26970224baaSJan Lentfer DEBUG("optimizing ruleset");
27070224baaSJan Lentfer memset(&table_buffer, 0, sizeof(table_buffer));
27170224baaSJan Lentfer skip_init();
27270224baaSJan Lentfer TAILQ_INIT(&opt_queue);
27370224baaSJan Lentfer
27470224baaSJan Lentfer old_rules = rs->rules[PF_RULESET_FILTER].active.ptr;
27570224baaSJan Lentfer rs->rules[PF_RULESET_FILTER].active.ptr =
27670224baaSJan Lentfer rs->rules[PF_RULESET_FILTER].inactive.ptr;
27770224baaSJan Lentfer rs->rules[PF_RULESET_FILTER].inactive.ptr = old_rules;
27870224baaSJan Lentfer
27970224baaSJan Lentfer /*
28070224baaSJan Lentfer * XXX expanding the pf_opt_rule format throughout pfctl might allow
28170224baaSJan Lentfer * us to avoid all this copying.
28270224baaSJan Lentfer */
28370224baaSJan Lentfer while ((r = TAILQ_FIRST(rs->rules[PF_RULESET_FILTER].inactive.ptr))
28470224baaSJan Lentfer != NULL) {
28570224baaSJan Lentfer TAILQ_REMOVE(rs->rules[PF_RULESET_FILTER].inactive.ptr, r,
28670224baaSJan Lentfer entries);
28770224baaSJan Lentfer if ((por = calloc(1, sizeof(*por))) == NULL)
28870224baaSJan Lentfer err(1, "calloc");
28970224baaSJan Lentfer memcpy(&por->por_rule, r, sizeof(*r));
29070224baaSJan Lentfer if (TAILQ_FIRST(&r->rpool.list) != NULL) {
29170224baaSJan Lentfer TAILQ_INIT(&por->por_rule.rpool.list);
29270224baaSJan Lentfer pfctl_move_pool(&r->rpool, &por->por_rule.rpool);
29370224baaSJan Lentfer } else
29470224baaSJan Lentfer bzero(&por->por_rule.rpool,
29570224baaSJan Lentfer sizeof(por->por_rule.rpool));
29670224baaSJan Lentfer
29770224baaSJan Lentfer
29870224baaSJan Lentfer TAILQ_INSERT_TAIL(&opt_queue, por, por_entry);
29970224baaSJan Lentfer }
30070224baaSJan Lentfer
30170224baaSJan Lentfer TAILQ_INIT(&superblocks);
30270224baaSJan Lentfer if (construct_superblocks(pf, &opt_queue, &superblocks))
30370224baaSJan Lentfer goto error;
30470224baaSJan Lentfer
30570224baaSJan Lentfer if (pf->optimize & PF_OPTIMIZE_PROFILE) {
30670224baaSJan Lentfer if (load_feedback_profile(pf, &superblocks))
30770224baaSJan Lentfer goto error;
30870224baaSJan Lentfer }
30970224baaSJan Lentfer
31070224baaSJan Lentfer TAILQ_FOREACH(block, &superblocks, sb_entry) {
31170224baaSJan Lentfer if (optimize_superblock(pf, block))
31270224baaSJan Lentfer goto error;
31370224baaSJan Lentfer }
31470224baaSJan Lentfer
31570224baaSJan Lentfer rs->anchor->refcnt = 0;
31670224baaSJan Lentfer while ((block = TAILQ_FIRST(&superblocks))) {
31770224baaSJan Lentfer TAILQ_REMOVE(&superblocks, block, sb_entry);
31870224baaSJan Lentfer
31970224baaSJan Lentfer while ((por = TAILQ_FIRST(&block->sb_rules))) {
32070224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, por, por_entry);
32170224baaSJan Lentfer por->por_rule.nr = rs->anchor->refcnt++;
32270224baaSJan Lentfer if ((r = calloc(1, sizeof(*r))) == NULL)
32370224baaSJan Lentfer err(1, "calloc");
32470224baaSJan Lentfer memcpy(r, &por->por_rule, sizeof(*r));
32570224baaSJan Lentfer TAILQ_INIT(&r->rpool.list);
32670224baaSJan Lentfer pfctl_move_pool(&por->por_rule.rpool, &r->rpool);
32770224baaSJan Lentfer TAILQ_INSERT_TAIL(
32870224baaSJan Lentfer rs->rules[PF_RULESET_FILTER].active.ptr,
32970224baaSJan Lentfer r, entries);
33070224baaSJan Lentfer free(por);
33170224baaSJan Lentfer }
33270224baaSJan Lentfer free(block);
33370224baaSJan Lentfer }
33470224baaSJan Lentfer
33570224baaSJan Lentfer return (0);
33670224baaSJan Lentfer
33770224baaSJan Lentfer error:
33870224baaSJan Lentfer while ((por = TAILQ_FIRST(&opt_queue))) {
33970224baaSJan Lentfer TAILQ_REMOVE(&opt_queue, por, por_entry);
34070224baaSJan Lentfer if (por->por_src_tbl) {
34170224baaSJan Lentfer pfr_buf_clear(por->por_src_tbl->pt_buf);
34270224baaSJan Lentfer free(por->por_src_tbl->pt_buf);
34370224baaSJan Lentfer free(por->por_src_tbl);
34470224baaSJan Lentfer }
34570224baaSJan Lentfer if (por->por_dst_tbl) {
34670224baaSJan Lentfer pfr_buf_clear(por->por_dst_tbl->pt_buf);
34770224baaSJan Lentfer free(por->por_dst_tbl->pt_buf);
34870224baaSJan Lentfer free(por->por_dst_tbl);
34970224baaSJan Lentfer }
35070224baaSJan Lentfer free(por);
35170224baaSJan Lentfer }
35270224baaSJan Lentfer while ((block = TAILQ_FIRST(&superblocks))) {
35370224baaSJan Lentfer TAILQ_REMOVE(&superblocks, block, sb_entry);
35470224baaSJan Lentfer superblock_free(pf, block);
35570224baaSJan Lentfer }
35670224baaSJan Lentfer return (1);
35770224baaSJan Lentfer }
35870224baaSJan Lentfer
35970224baaSJan Lentfer
36070224baaSJan Lentfer /*
36170224baaSJan Lentfer * Go ahead and optimize a superblock
36270224baaSJan Lentfer */
36370224baaSJan Lentfer int
optimize_superblock(struct pfctl * pf,struct superblock * block)36470224baaSJan Lentfer optimize_superblock(struct pfctl *pf, struct superblock *block)
36570224baaSJan Lentfer {
36670224baaSJan Lentfer #ifdef OPT_DEBUG
36770224baaSJan Lentfer struct pf_opt_rule *por;
36870224baaSJan Lentfer #endif /* OPT_DEBUG */
36970224baaSJan Lentfer
37070224baaSJan Lentfer /* We have a few optimization passes:
37170224baaSJan Lentfer * 1) remove duplicate rules or rules that are a subset of other
37270224baaSJan Lentfer * rules
37370224baaSJan Lentfer * 2) combine otherwise identical rules with different IP addresses
37470224baaSJan Lentfer * into a single rule and put the addresses in a table.
37570224baaSJan Lentfer * 3) re-order the rules to improve kernel skip steps
37670224baaSJan Lentfer * 4) re-order the 'quick' rules based on feedback from the
37770224baaSJan Lentfer * active ruleset statistics
37870224baaSJan Lentfer *
37970224baaSJan Lentfer * XXX combine_rules() doesn't combine v4 and v6 rules. would just
38070224baaSJan Lentfer * have to keep af in the table container, make af 'COMBINE' and
38170224baaSJan Lentfer * twiddle the af on the merged rule
38270224baaSJan Lentfer * XXX maybe add a weighting to the metric on skipsteps when doing
38370224baaSJan Lentfer * reordering. sometimes two sequential tables will be better
38470224baaSJan Lentfer * that four consecutive interfaces.
38570224baaSJan Lentfer * XXX need to adjust the skipstep count of everything after PROTO,
38670224baaSJan Lentfer * since they aren't actually checked on a proto mismatch in
38770224baaSJan Lentfer * pf_test_{tcp, udp, icmp}()
38870224baaSJan Lentfer * XXX should i treat proto=0, af=0 or dir=0 special in skepstep
38970224baaSJan Lentfer * calculation since they are a DC?
39070224baaSJan Lentfer * XXX keep last skiplist of last superblock to influence this
39170224baaSJan Lentfer * superblock. '5 inet6 log' should make '3 inet6' come before '4
39270224baaSJan Lentfer * inet' in the next superblock.
39370224baaSJan Lentfer * XXX would be useful to add tables for ports
39470224baaSJan Lentfer * XXX we can also re-order some mutually exclusive superblocks to
39570224baaSJan Lentfer * try merging superblocks before any of these optimization passes.
39670224baaSJan Lentfer * for instance a single 'log in' rule in the middle of non-logging
39770224baaSJan Lentfer * out rules.
39870224baaSJan Lentfer */
39970224baaSJan Lentfer
40070224baaSJan Lentfer /* shortcut. there will be a lot of 1-rule superblocks */
40170224baaSJan Lentfer if (!TAILQ_NEXT(TAILQ_FIRST(&block->sb_rules), por_entry))
40270224baaSJan Lentfer return (0);
40370224baaSJan Lentfer
40470224baaSJan Lentfer #ifdef OPT_DEBUG
40570224baaSJan Lentfer printf("--- Superblock ---\n");
40670224baaSJan Lentfer TAILQ_FOREACH(por, &block->sb_rules, por_entry) {
40770224baaSJan Lentfer printf(" ");
40870224baaSJan Lentfer print_rule(&por->por_rule, por->por_rule.anchor ?
40970224baaSJan Lentfer por->por_rule.anchor->name : "", 1);
41070224baaSJan Lentfer }
41170224baaSJan Lentfer #endif /* OPT_DEBUG */
41270224baaSJan Lentfer
41370224baaSJan Lentfer
41470224baaSJan Lentfer if (remove_identical_rules(pf, block))
41570224baaSJan Lentfer return (1);
41670224baaSJan Lentfer if (combine_rules(pf, block))
41770224baaSJan Lentfer return (1);
41870224baaSJan Lentfer if ((pf->optimize & PF_OPTIMIZE_PROFILE) &&
41970224baaSJan Lentfer TAILQ_FIRST(&block->sb_rules)->por_rule.quick &&
42070224baaSJan Lentfer block->sb_profiled_block) {
42170224baaSJan Lentfer if (block_feedback(pf, block))
42270224baaSJan Lentfer return (1);
42370224baaSJan Lentfer } else if (reorder_rules(pf, block, 0)) {
42470224baaSJan Lentfer return (1);
42570224baaSJan Lentfer }
42670224baaSJan Lentfer
42770224baaSJan Lentfer /*
42870224baaSJan Lentfer * Don't add any optimization passes below reorder_rules(). It will
42970224baaSJan Lentfer * have divided superblocks into smaller blocks for further refinement
43070224baaSJan Lentfer * and doesn't put them back together again. What once was a true
43170224baaSJan Lentfer * superblock might have been split into multiple superblocks.
43270224baaSJan Lentfer */
43370224baaSJan Lentfer
43470224baaSJan Lentfer #ifdef OPT_DEBUG
43570224baaSJan Lentfer printf("--- END Superblock ---\n");
43670224baaSJan Lentfer #endif /* OPT_DEBUG */
43770224baaSJan Lentfer return (0);
43870224baaSJan Lentfer }
43970224baaSJan Lentfer
44070224baaSJan Lentfer
44170224baaSJan Lentfer /*
44270224baaSJan Lentfer * Optimization pass #1: remove identical rules
44370224baaSJan Lentfer */
44470224baaSJan Lentfer int
remove_identical_rules(struct pfctl * pf,struct superblock * block)44570224baaSJan Lentfer remove_identical_rules(struct pfctl *pf, struct superblock *block)
44670224baaSJan Lentfer {
44770224baaSJan Lentfer struct pf_opt_rule *por1, *por2, *por_next, *por2_next;
44870224baaSJan Lentfer struct pf_rule a, a2, b, b2;
44970224baaSJan Lentfer
45070224baaSJan Lentfer for (por1 = TAILQ_FIRST(&block->sb_rules); por1; por1 = por_next) {
45170224baaSJan Lentfer por_next = TAILQ_NEXT(por1, por_entry);
45270224baaSJan Lentfer for (por2 = por_next; por2; por2 = por2_next) {
45370224baaSJan Lentfer por2_next = TAILQ_NEXT(por2, por_entry);
45470224baaSJan Lentfer comparable_rule(&a, &por1->por_rule, DC);
45570224baaSJan Lentfer comparable_rule(&b, &por2->por_rule, DC);
45670224baaSJan Lentfer memcpy(&a2, &a, sizeof(a2));
45770224baaSJan Lentfer memcpy(&b2, &b, sizeof(b2));
45870224baaSJan Lentfer
45970224baaSJan Lentfer exclude_supersets(&a, &b);
46070224baaSJan Lentfer exclude_supersets(&b2, &a2);
46170224baaSJan Lentfer if (memcmp(&a, &b, sizeof(a)) == 0) {
46270224baaSJan Lentfer DEBUG("removing identical rule nr%d = *nr%d*",
46370224baaSJan Lentfer por1->por_rule.nr, por2->por_rule.nr);
46470224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, por2, por_entry);
46570224baaSJan Lentfer if (por_next == por2)
46670224baaSJan Lentfer por_next = TAILQ_NEXT(por1, por_entry);
46770224baaSJan Lentfer free(por2);
46870224baaSJan Lentfer } else if (memcmp(&a2, &b2, sizeof(a2)) == 0) {
46970224baaSJan Lentfer DEBUG("removing identical rule *nr%d* = nr%d",
47070224baaSJan Lentfer por1->por_rule.nr, por2->por_rule.nr);
47170224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, por1, por_entry);
47270224baaSJan Lentfer free(por1);
47370224baaSJan Lentfer break;
47470224baaSJan Lentfer }
47570224baaSJan Lentfer }
47670224baaSJan Lentfer }
47770224baaSJan Lentfer
47870224baaSJan Lentfer return (0);
47970224baaSJan Lentfer }
48070224baaSJan Lentfer
48170224baaSJan Lentfer
48270224baaSJan Lentfer /*
48370224baaSJan Lentfer * Optimization pass #2: combine similar rules with different addresses
48470224baaSJan Lentfer * into a single rule and a table
48570224baaSJan Lentfer */
48670224baaSJan Lentfer int
combine_rules(struct pfctl * pf,struct superblock * block)48770224baaSJan Lentfer combine_rules(struct pfctl *pf, struct superblock *block)
48870224baaSJan Lentfer {
48970224baaSJan Lentfer struct pf_opt_rule *p1, *p2, *por_next;
49070224baaSJan Lentfer int src_eq, dst_eq;
49170224baaSJan Lentfer
49270224baaSJan Lentfer if ((pf->loadopt & PFCTL_FLAG_TABLE) == 0) {
49370224baaSJan Lentfer warnx("Must enable table loading for optimizations");
49470224baaSJan Lentfer return (1);
49570224baaSJan Lentfer }
49670224baaSJan Lentfer
49770224baaSJan Lentfer /* First we make a pass to combine the rules. O(n log n) */
49870224baaSJan Lentfer TAILQ_FOREACH(p1, &block->sb_rules, por_entry) {
49970224baaSJan Lentfer for (p2 = TAILQ_NEXT(p1, por_entry); p2; p2 = por_next) {
50070224baaSJan Lentfer por_next = TAILQ_NEXT(p2, por_entry);
50170224baaSJan Lentfer
50270224baaSJan Lentfer src_eq = addrs_equal(&p1->por_rule.src,
50370224baaSJan Lentfer &p2->por_rule.src);
50470224baaSJan Lentfer dst_eq = addrs_equal(&p1->por_rule.dst,
50570224baaSJan Lentfer &p2->por_rule.dst);
50670224baaSJan Lentfer
50770224baaSJan Lentfer if (src_eq && !dst_eq && p1->por_src_tbl == NULL &&
50870224baaSJan Lentfer p2->por_dst_tbl == NULL &&
50970224baaSJan Lentfer p2->por_src_tbl == NULL &&
51070224baaSJan Lentfer rules_combineable(&p1->por_rule, &p2->por_rule) &&
51170224baaSJan Lentfer addrs_combineable(&p1->por_rule.dst,
51270224baaSJan Lentfer &p2->por_rule.dst)) {
51370224baaSJan Lentfer DEBUG("can combine rules nr%d = nr%d",
51470224baaSJan Lentfer p1->por_rule.nr, p2->por_rule.nr);
51570224baaSJan Lentfer if (p1->por_dst_tbl == NULL &&
51670224baaSJan Lentfer add_opt_table(pf, &p1->por_dst_tbl,
51770224baaSJan Lentfer p1->por_rule.af, &p1->por_rule.dst))
51870224baaSJan Lentfer return (1);
51970224baaSJan Lentfer if (add_opt_table(pf, &p1->por_dst_tbl,
52070224baaSJan Lentfer p1->por_rule.af, &p2->por_rule.dst))
52170224baaSJan Lentfer return (1);
52270224baaSJan Lentfer p2->por_dst_tbl = p1->por_dst_tbl;
52370224baaSJan Lentfer if (p1->por_dst_tbl->pt_rulecount >=
52470224baaSJan Lentfer TABLE_THRESHOLD) {
52570224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, p2,
52670224baaSJan Lentfer por_entry);
52770224baaSJan Lentfer free(p2);
52870224baaSJan Lentfer }
52970224baaSJan Lentfer } else if (!src_eq && dst_eq && p1->por_dst_tbl == NULL
53070224baaSJan Lentfer && p2->por_src_tbl == NULL &&
53170224baaSJan Lentfer p2->por_dst_tbl == NULL &&
53270224baaSJan Lentfer rules_combineable(&p1->por_rule, &p2->por_rule) &&
53370224baaSJan Lentfer addrs_combineable(&p1->por_rule.src,
53470224baaSJan Lentfer &p2->por_rule.src)) {
53570224baaSJan Lentfer DEBUG("can combine rules nr%d = nr%d",
53670224baaSJan Lentfer p1->por_rule.nr, p2->por_rule.nr);
53770224baaSJan Lentfer if (p1->por_src_tbl == NULL &&
53870224baaSJan Lentfer add_opt_table(pf, &p1->por_src_tbl,
53970224baaSJan Lentfer p1->por_rule.af, &p1->por_rule.src))
54070224baaSJan Lentfer return (1);
54170224baaSJan Lentfer if (add_opt_table(pf, &p1->por_src_tbl,
54270224baaSJan Lentfer p1->por_rule.af, &p2->por_rule.src))
54370224baaSJan Lentfer return (1);
54470224baaSJan Lentfer p2->por_src_tbl = p1->por_src_tbl;
54570224baaSJan Lentfer if (p1->por_src_tbl->pt_rulecount >=
54670224baaSJan Lentfer TABLE_THRESHOLD) {
54770224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, p2,
54870224baaSJan Lentfer por_entry);
54970224baaSJan Lentfer free(p2);
55070224baaSJan Lentfer }
55170224baaSJan Lentfer }
55270224baaSJan Lentfer }
55370224baaSJan Lentfer }
55470224baaSJan Lentfer
55570224baaSJan Lentfer
55670224baaSJan Lentfer /*
55770224baaSJan Lentfer * Then we make a final pass to create a valid table name and
55870224baaSJan Lentfer * insert the name into the rules.
55970224baaSJan Lentfer */
56070224baaSJan Lentfer for (p1 = TAILQ_FIRST(&block->sb_rules); p1; p1 = por_next) {
56170224baaSJan Lentfer por_next = TAILQ_NEXT(p1, por_entry);
56270224baaSJan Lentfer assert(p1->por_src_tbl == NULL || p1->por_dst_tbl == NULL);
56370224baaSJan Lentfer
56470224baaSJan Lentfer if (p1->por_src_tbl && p1->por_src_tbl->pt_rulecount >=
56570224baaSJan Lentfer TABLE_THRESHOLD) {
56670224baaSJan Lentfer if (p1->por_src_tbl->pt_generated) {
56770224baaSJan Lentfer /* This rule is included in a table */
56870224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, p1, por_entry);
56970224baaSJan Lentfer free(p1);
57070224baaSJan Lentfer continue;
57170224baaSJan Lentfer }
57270224baaSJan Lentfer p1->por_src_tbl->pt_generated = 1;
57370224baaSJan Lentfer
57470224baaSJan Lentfer if ((pf->opts & PF_OPT_NOACTION) == 0 &&
57570224baaSJan Lentfer pf_opt_create_table(pf, p1->por_src_tbl))
57670224baaSJan Lentfer return (1);
57770224baaSJan Lentfer
57870224baaSJan Lentfer pf->tdirty = 1;
57970224baaSJan Lentfer
58070224baaSJan Lentfer if (pf->opts & PF_OPT_VERBOSE)
58170224baaSJan Lentfer print_tabledef(p1->por_src_tbl->pt_name,
58270224baaSJan Lentfer PFR_TFLAG_CONST, 1,
58370224baaSJan Lentfer &p1->por_src_tbl->pt_nodes);
58470224baaSJan Lentfer
58570224baaSJan Lentfer memset(&p1->por_rule.src.addr, 0,
58670224baaSJan Lentfer sizeof(p1->por_rule.src.addr));
58770224baaSJan Lentfer p1->por_rule.src.addr.type = PF_ADDR_TABLE;
58870224baaSJan Lentfer strlcpy(p1->por_rule.src.addr.v.tblname,
58970224baaSJan Lentfer p1->por_src_tbl->pt_name,
59070224baaSJan Lentfer sizeof(p1->por_rule.src.addr.v.tblname));
59170224baaSJan Lentfer
59270224baaSJan Lentfer pfr_buf_clear(p1->por_src_tbl->pt_buf);
59370224baaSJan Lentfer free(p1->por_src_tbl->pt_buf);
59470224baaSJan Lentfer p1->por_src_tbl->pt_buf = NULL;
59570224baaSJan Lentfer }
59670224baaSJan Lentfer if (p1->por_dst_tbl && p1->por_dst_tbl->pt_rulecount >=
59770224baaSJan Lentfer TABLE_THRESHOLD) {
59870224baaSJan Lentfer if (p1->por_dst_tbl->pt_generated) {
59970224baaSJan Lentfer /* This rule is included in a table */
60070224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, p1, por_entry);
60170224baaSJan Lentfer free(p1);
60270224baaSJan Lentfer continue;
60370224baaSJan Lentfer }
60470224baaSJan Lentfer p1->por_dst_tbl->pt_generated = 1;
60570224baaSJan Lentfer
60670224baaSJan Lentfer if ((pf->opts & PF_OPT_NOACTION) == 0 &&
60770224baaSJan Lentfer pf_opt_create_table(pf, p1->por_dst_tbl))
60870224baaSJan Lentfer return (1);
60970224baaSJan Lentfer pf->tdirty = 1;
61070224baaSJan Lentfer
61170224baaSJan Lentfer if (pf->opts & PF_OPT_VERBOSE)
61270224baaSJan Lentfer print_tabledef(p1->por_dst_tbl->pt_name,
61370224baaSJan Lentfer PFR_TFLAG_CONST, 1,
61470224baaSJan Lentfer &p1->por_dst_tbl->pt_nodes);
61570224baaSJan Lentfer
61670224baaSJan Lentfer memset(&p1->por_rule.dst.addr, 0,
61770224baaSJan Lentfer sizeof(p1->por_rule.dst.addr));
61870224baaSJan Lentfer p1->por_rule.dst.addr.type = PF_ADDR_TABLE;
61970224baaSJan Lentfer strlcpy(p1->por_rule.dst.addr.v.tblname,
62070224baaSJan Lentfer p1->por_dst_tbl->pt_name,
62170224baaSJan Lentfer sizeof(p1->por_rule.dst.addr.v.tblname));
62270224baaSJan Lentfer
62370224baaSJan Lentfer pfr_buf_clear(p1->por_dst_tbl->pt_buf);
62470224baaSJan Lentfer free(p1->por_dst_tbl->pt_buf);
62570224baaSJan Lentfer p1->por_dst_tbl->pt_buf = NULL;
62670224baaSJan Lentfer }
62770224baaSJan Lentfer }
62870224baaSJan Lentfer
62970224baaSJan Lentfer return (0);
63070224baaSJan Lentfer }
63170224baaSJan Lentfer
63270224baaSJan Lentfer
63370224baaSJan Lentfer /*
63470224baaSJan Lentfer * Optimization pass #3: re-order rules to improve skip steps
63570224baaSJan Lentfer */
63670224baaSJan Lentfer int
reorder_rules(struct pfctl * pf,struct superblock * block,int depth)63770224baaSJan Lentfer reorder_rules(struct pfctl *pf, struct superblock *block, int depth)
63870224baaSJan Lentfer {
63970224baaSJan Lentfer struct superblock *newblock;
64070224baaSJan Lentfer struct pf_skip_step *skiplist;
64170224baaSJan Lentfer struct pf_opt_rule *por;
64270224baaSJan Lentfer int i, largest, largest_list, rule_count = 0;
64370224baaSJan Lentfer TAILQ_HEAD( , pf_opt_rule) head;
64470224baaSJan Lentfer
64570224baaSJan Lentfer /*
64670224baaSJan Lentfer * Calculate the best-case skip steps. We put each rule in a list
64770224baaSJan Lentfer * of other rules with common fields
64870224baaSJan Lentfer */
64970224baaSJan Lentfer for (i = 0; i < PF_SKIP_COUNT; i++) {
65070224baaSJan Lentfer TAILQ_FOREACH(por, &block->sb_rules, por_entry) {
65170224baaSJan Lentfer TAILQ_FOREACH(skiplist, &block->sb_skipsteps[i],
65270224baaSJan Lentfer ps_entry) {
65370224baaSJan Lentfer if (skip_compare(i, skiplist, por) == 0)
65470224baaSJan Lentfer break;
65570224baaSJan Lentfer }
65670224baaSJan Lentfer if (skiplist == NULL) {
65770224baaSJan Lentfer if ((skiplist = calloc(1, sizeof(*skiplist))) ==
65870224baaSJan Lentfer NULL)
65970224baaSJan Lentfer err(1, "calloc");
66070224baaSJan Lentfer TAILQ_INIT(&skiplist->ps_rules);
66170224baaSJan Lentfer TAILQ_INSERT_TAIL(&block->sb_skipsteps[i],
66270224baaSJan Lentfer skiplist, ps_entry);
66370224baaSJan Lentfer }
66470224baaSJan Lentfer skip_append(block, i, skiplist, por);
66570224baaSJan Lentfer }
66670224baaSJan Lentfer }
66770224baaSJan Lentfer
66870224baaSJan Lentfer TAILQ_FOREACH(por, &block->sb_rules, por_entry)
66970224baaSJan Lentfer rule_count++;
67070224baaSJan Lentfer
67170224baaSJan Lentfer /*
67270224baaSJan Lentfer * Now we're going to ignore any fields that are identical between
67370224baaSJan Lentfer * all of the rules in the superblock and those fields which differ
67470224baaSJan Lentfer * between every rule in the superblock.
67570224baaSJan Lentfer */
67670224baaSJan Lentfer largest = 0;
67770224baaSJan Lentfer for (i = 0; i < PF_SKIP_COUNT; i++) {
67870224baaSJan Lentfer skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]);
67970224baaSJan Lentfer if (skiplist->ps_count == rule_count) {
68070224baaSJan Lentfer DEBUG("(%d) original skipstep '%s' is all rules",
68170224baaSJan Lentfer depth, skip_comparitors_names[i]);
68270224baaSJan Lentfer skiplist->ps_count = 0;
68370224baaSJan Lentfer } else if (skiplist->ps_count == 1) {
68470224baaSJan Lentfer skiplist->ps_count = 0;
68570224baaSJan Lentfer } else {
68670224baaSJan Lentfer DEBUG("(%d) original skipstep '%s' largest jump is %d",
68770224baaSJan Lentfer depth, skip_comparitors_names[i],
68870224baaSJan Lentfer skiplist->ps_count);
68970224baaSJan Lentfer if (skiplist->ps_count > largest)
69070224baaSJan Lentfer largest = skiplist->ps_count;
69170224baaSJan Lentfer }
69270224baaSJan Lentfer }
69370224baaSJan Lentfer if (largest == 0) {
69470224baaSJan Lentfer /* Ugh. There is NO commonality in the superblock on which
69570224baaSJan Lentfer * optimize the skipsteps optimization.
69670224baaSJan Lentfer */
69770224baaSJan Lentfer goto done;
69870224baaSJan Lentfer }
69970224baaSJan Lentfer
70070224baaSJan Lentfer /*
70170224baaSJan Lentfer * Now we're going to empty the superblock rule list and re-create
70270224baaSJan Lentfer * it based on a more optimal skipstep order.
70370224baaSJan Lentfer */
70470224baaSJan Lentfer TAILQ_INIT(&head);
70570224baaSJan Lentfer while ((por = TAILQ_FIRST(&block->sb_rules))) {
70670224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, por, por_entry);
70770224baaSJan Lentfer TAILQ_INSERT_TAIL(&head, por, por_entry);
70870224baaSJan Lentfer }
70970224baaSJan Lentfer
71070224baaSJan Lentfer
71170224baaSJan Lentfer while (!TAILQ_EMPTY(&head)) {
71270224baaSJan Lentfer largest = 1;
71370224baaSJan Lentfer
71470224baaSJan Lentfer /*
71570224baaSJan Lentfer * Find the most useful skip steps remaining
71670224baaSJan Lentfer */
71770224baaSJan Lentfer for (i = 0; i < PF_SKIP_COUNT; i++) {
71870224baaSJan Lentfer skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]);
71970224baaSJan Lentfer if (skiplist->ps_count > largest) {
72070224baaSJan Lentfer largest = skiplist->ps_count;
72170224baaSJan Lentfer largest_list = i;
72270224baaSJan Lentfer }
72370224baaSJan Lentfer }
72470224baaSJan Lentfer
72570224baaSJan Lentfer if (largest <= 1) {
72670224baaSJan Lentfer /*
72770224baaSJan Lentfer * Nothing useful left. Leave remaining rules in order.
72870224baaSJan Lentfer */
72970224baaSJan Lentfer DEBUG("(%d) no more commonality for skip steps", depth);
73070224baaSJan Lentfer while ((por = TAILQ_FIRST(&head))) {
73170224baaSJan Lentfer TAILQ_REMOVE(&head, por, por_entry);
73270224baaSJan Lentfer TAILQ_INSERT_TAIL(&block->sb_rules, por,
73370224baaSJan Lentfer por_entry);
73470224baaSJan Lentfer }
73570224baaSJan Lentfer } else {
73670224baaSJan Lentfer /*
73770224baaSJan Lentfer * There is commonality. Extract those common rules
73870224baaSJan Lentfer * and place them in the ruleset adjacent to each
73970224baaSJan Lentfer * other.
74070224baaSJan Lentfer */
74170224baaSJan Lentfer skiplist = TAILQ_FIRST(&block->sb_skipsteps[
74270224baaSJan Lentfer largest_list]);
74370224baaSJan Lentfer DEBUG("(%d) skipstep '%s' largest jump is %d @ #%d",
74470224baaSJan Lentfer depth, skip_comparitors_names[largest_list],
74570224baaSJan Lentfer largest, TAILQ_FIRST(&TAILQ_FIRST(&block->
74670224baaSJan Lentfer sb_skipsteps [largest_list])->ps_rules)->
74770224baaSJan Lentfer por_rule.nr);
74870224baaSJan Lentfer TAILQ_REMOVE(&block->sb_skipsteps[largest_list],
74970224baaSJan Lentfer skiplist, ps_entry);
75070224baaSJan Lentfer
75170224baaSJan Lentfer
75270224baaSJan Lentfer /*
75370224baaSJan Lentfer * There may be further commonality inside these
75470224baaSJan Lentfer * rules. So we'll split them off into they're own
75570224baaSJan Lentfer * superblock and pass it back into the optimizer.
75670224baaSJan Lentfer */
75770224baaSJan Lentfer if (skiplist->ps_count > 2) {
75870224baaSJan Lentfer if ((newblock = calloc(1, sizeof(*newblock)))
75970224baaSJan Lentfer == NULL) {
76070224baaSJan Lentfer warn("calloc");
76170224baaSJan Lentfer return (1);
76270224baaSJan Lentfer }
76370224baaSJan Lentfer TAILQ_INIT(&newblock->sb_rules);
76470224baaSJan Lentfer for (i = 0; i < PF_SKIP_COUNT; i++)
76570224baaSJan Lentfer TAILQ_INIT(&newblock->sb_skipsteps[i]);
76670224baaSJan Lentfer TAILQ_INSERT_BEFORE(block, newblock, sb_entry);
76770224baaSJan Lentfer DEBUG("(%d) splitting off %d rules from superblock @ #%d",
76870224baaSJan Lentfer depth, skiplist->ps_count,
76970224baaSJan Lentfer TAILQ_FIRST(&skiplist->ps_rules)->
77070224baaSJan Lentfer por_rule.nr);
77170224baaSJan Lentfer } else {
77270224baaSJan Lentfer newblock = block;
77370224baaSJan Lentfer }
77470224baaSJan Lentfer
77570224baaSJan Lentfer while ((por = TAILQ_FIRST(&skiplist->ps_rules))) {
77670224baaSJan Lentfer TAILQ_REMOVE(&head, por, por_entry);
77770224baaSJan Lentfer TAILQ_REMOVE(&skiplist->ps_rules, por,
77870224baaSJan Lentfer por_skip_entry[largest_list]);
77970224baaSJan Lentfer TAILQ_INSERT_TAIL(&newblock->sb_rules, por,
78070224baaSJan Lentfer por_entry);
78170224baaSJan Lentfer
78270224baaSJan Lentfer /* Remove this rule from all other skiplists */
78370224baaSJan Lentfer remove_from_skipsteps(&block->sb_skipsteps[
78470224baaSJan Lentfer largest_list], block, por, skiplist);
78570224baaSJan Lentfer }
78670224baaSJan Lentfer free(skiplist);
78770224baaSJan Lentfer if (newblock != block)
78870224baaSJan Lentfer if (reorder_rules(pf, newblock, depth + 1))
78970224baaSJan Lentfer return (1);
79070224baaSJan Lentfer }
79170224baaSJan Lentfer }
79270224baaSJan Lentfer
79370224baaSJan Lentfer done:
79470224baaSJan Lentfer for (i = 0; i < PF_SKIP_COUNT; i++) {
79570224baaSJan Lentfer while ((skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]))) {
79670224baaSJan Lentfer TAILQ_REMOVE(&block->sb_skipsteps[i], skiplist,
79770224baaSJan Lentfer ps_entry);
79870224baaSJan Lentfer free(skiplist);
79970224baaSJan Lentfer }
80070224baaSJan Lentfer }
80170224baaSJan Lentfer
80270224baaSJan Lentfer return (0);
80370224baaSJan Lentfer }
80470224baaSJan Lentfer
80570224baaSJan Lentfer
80670224baaSJan Lentfer /*
80770224baaSJan Lentfer * Optimization pass #4: re-order 'quick' rules based on feedback from the
80870224baaSJan Lentfer * currently running ruleset
80970224baaSJan Lentfer */
81070224baaSJan Lentfer int
block_feedback(struct pfctl * pf,struct superblock * block)81170224baaSJan Lentfer block_feedback(struct pfctl *pf, struct superblock *block)
81270224baaSJan Lentfer {
81370224baaSJan Lentfer TAILQ_HEAD( , pf_opt_rule) queue;
81470224baaSJan Lentfer struct pf_opt_rule *por1, *por2;
81570224baaSJan Lentfer u_int64_t total_count = 0;
81670224baaSJan Lentfer struct pf_rule a, b;
81770224baaSJan Lentfer
81870224baaSJan Lentfer
81970224baaSJan Lentfer /*
82070224baaSJan Lentfer * Walk through all of the profiled superblock's rules and copy
82170224baaSJan Lentfer * the counters onto our rules.
82270224baaSJan Lentfer */
82370224baaSJan Lentfer TAILQ_FOREACH(por1, &block->sb_profiled_block->sb_rules, por_entry) {
82470224baaSJan Lentfer comparable_rule(&a, &por1->por_rule, DC);
82570224baaSJan Lentfer total_count += por1->por_rule.packets[0] +
82670224baaSJan Lentfer por1->por_rule.packets[1];
82770224baaSJan Lentfer TAILQ_FOREACH(por2, &block->sb_rules, por_entry) {
82870224baaSJan Lentfer if (por2->por_profile_count)
82970224baaSJan Lentfer continue;
83070224baaSJan Lentfer comparable_rule(&b, &por2->por_rule, DC);
83170224baaSJan Lentfer if (memcmp(&a, &b, sizeof(a)) == 0) {
83270224baaSJan Lentfer por2->por_profile_count =
83370224baaSJan Lentfer por1->por_rule.packets[0] +
83470224baaSJan Lentfer por1->por_rule.packets[1];
83570224baaSJan Lentfer break;
83670224baaSJan Lentfer }
83770224baaSJan Lentfer }
83870224baaSJan Lentfer }
83970224baaSJan Lentfer superblock_free(pf, block->sb_profiled_block);
84070224baaSJan Lentfer block->sb_profiled_block = NULL;
84170224baaSJan Lentfer
84270224baaSJan Lentfer /*
84370224baaSJan Lentfer * Now we pull all of the rules off the superblock and re-insert them
84470224baaSJan Lentfer * in sorted order.
84570224baaSJan Lentfer */
84670224baaSJan Lentfer
84770224baaSJan Lentfer TAILQ_INIT(&queue);
84870224baaSJan Lentfer while ((por1 = TAILQ_FIRST(&block->sb_rules)) != NULL) {
84970224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, por1, por_entry);
85070224baaSJan Lentfer TAILQ_INSERT_TAIL(&queue, por1, por_entry);
85170224baaSJan Lentfer }
85270224baaSJan Lentfer
85370224baaSJan Lentfer while ((por1 = TAILQ_FIRST(&queue)) != NULL) {
85470224baaSJan Lentfer TAILQ_REMOVE(&queue, por1, por_entry);
85570224baaSJan Lentfer /* XXX I should sort all of the unused rules based on skip steps */
85670224baaSJan Lentfer TAILQ_FOREACH(por2, &block->sb_rules, por_entry) {
85770224baaSJan Lentfer if (por1->por_profile_count > por2->por_profile_count) {
85870224baaSJan Lentfer TAILQ_INSERT_BEFORE(por2, por1, por_entry);
85970224baaSJan Lentfer break;
86070224baaSJan Lentfer }
86170224baaSJan Lentfer }
86270224baaSJan Lentfer if (por2 == NULL)
86370224baaSJan Lentfer TAILQ_INSERT_TAIL(&block->sb_rules, por1, por_entry);
86470224baaSJan Lentfer }
86570224baaSJan Lentfer
86670224baaSJan Lentfer return (0);
86770224baaSJan Lentfer }
86870224baaSJan Lentfer
86970224baaSJan Lentfer
87070224baaSJan Lentfer /*
87170224baaSJan Lentfer * Load the current ruleset from the kernel and try to associate them with
87270224baaSJan Lentfer * the ruleset we're optimizing.
87370224baaSJan Lentfer */
87470224baaSJan Lentfer int
load_feedback_profile(struct pfctl * pf,struct superblocks * superblocks)87570224baaSJan Lentfer load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks)
87670224baaSJan Lentfer {
87770224baaSJan Lentfer struct superblock *block, *blockcur;
87870224baaSJan Lentfer struct superblocks prof_superblocks;
87970224baaSJan Lentfer struct pf_opt_rule *por;
88070224baaSJan Lentfer struct pf_opt_queue queue;
88170224baaSJan Lentfer struct pfioc_rule pr;
88270224baaSJan Lentfer struct pf_rule a, b;
88370224baaSJan Lentfer int nr, mnr;
88470224baaSJan Lentfer
88570224baaSJan Lentfer TAILQ_INIT(&queue);
88670224baaSJan Lentfer TAILQ_INIT(&prof_superblocks);
88770224baaSJan Lentfer
88870224baaSJan Lentfer memset(&pr, 0, sizeof(pr));
88970224baaSJan Lentfer pr.rule.action = PF_PASS;
89070224baaSJan Lentfer if (ioctl(pf->dev, DIOCGETRULES, &pr)) {
89170224baaSJan Lentfer warn("DIOCGETRULES");
89270224baaSJan Lentfer return (1);
89370224baaSJan Lentfer }
89470224baaSJan Lentfer mnr = pr.nr;
89570224baaSJan Lentfer
89670224baaSJan Lentfer DEBUG("Loading %d active rules for a feedback profile", mnr);
89770224baaSJan Lentfer for (nr = 0; nr < mnr; ++nr) {
89870224baaSJan Lentfer struct pf_ruleset *rs;
89970224baaSJan Lentfer if ((por = calloc(1, sizeof(*por))) == NULL) {
90070224baaSJan Lentfer warn("calloc");
90170224baaSJan Lentfer return (1);
90270224baaSJan Lentfer }
90370224baaSJan Lentfer pr.nr = nr;
90470224baaSJan Lentfer if (ioctl(pf->dev, DIOCGETRULE, &pr)) {
90570224baaSJan Lentfer warn("DIOCGETRULES");
90670224baaSJan Lentfer return (1);
90770224baaSJan Lentfer }
90870224baaSJan Lentfer memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule));
90970224baaSJan Lentfer rs = pf_find_or_create_ruleset(pr.anchor_call);
91070224baaSJan Lentfer por->por_rule.anchor = rs->anchor;
91170224baaSJan Lentfer if (TAILQ_EMPTY(&por->por_rule.rpool.list))
91270224baaSJan Lentfer memset(&por->por_rule.rpool, 0,
91370224baaSJan Lentfer sizeof(por->por_rule.rpool));
91470224baaSJan Lentfer TAILQ_INSERT_TAIL(&queue, por, por_entry);
91570224baaSJan Lentfer
91670224baaSJan Lentfer /* XXX pfctl_get_pool(pf->dev, &pr.rule.rpool, nr, pr.ticket,
91770224baaSJan Lentfer * PF_PASS, pf->anchor) ???
91870224baaSJan Lentfer * ... pfctl_clear_pool(&pr.rule.rpool)
91970224baaSJan Lentfer */
92070224baaSJan Lentfer }
92170224baaSJan Lentfer
92270224baaSJan Lentfer if (construct_superblocks(pf, &queue, &prof_superblocks))
92370224baaSJan Lentfer return (1);
92470224baaSJan Lentfer
92570224baaSJan Lentfer
92670224baaSJan Lentfer /*
92770224baaSJan Lentfer * Now we try to associate the active ruleset's superblocks with
92870224baaSJan Lentfer * the superblocks we're compiling.
92970224baaSJan Lentfer */
93070224baaSJan Lentfer block = TAILQ_FIRST(superblocks);
93170224baaSJan Lentfer blockcur = TAILQ_FIRST(&prof_superblocks);
93270224baaSJan Lentfer while (block && blockcur) {
93370224baaSJan Lentfer comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule,
93470224baaSJan Lentfer BREAK);
93570224baaSJan Lentfer comparable_rule(&b, &TAILQ_FIRST(&blockcur->sb_rules)->por_rule,
93670224baaSJan Lentfer BREAK);
93770224baaSJan Lentfer if (memcmp(&a, &b, sizeof(a)) == 0) {
93870224baaSJan Lentfer /* The two superblocks lined up */
93970224baaSJan Lentfer block->sb_profiled_block = blockcur;
94070224baaSJan Lentfer } else {
94170224baaSJan Lentfer DEBUG("superblocks don't line up between #%d and #%d",
94270224baaSJan Lentfer TAILQ_FIRST(&block->sb_rules)->por_rule.nr,
94370224baaSJan Lentfer TAILQ_FIRST(&blockcur->sb_rules)->por_rule.nr);
94470224baaSJan Lentfer break;
94570224baaSJan Lentfer }
94670224baaSJan Lentfer block = TAILQ_NEXT(block, sb_entry);
94770224baaSJan Lentfer blockcur = TAILQ_NEXT(blockcur, sb_entry);
94870224baaSJan Lentfer }
94970224baaSJan Lentfer
95070224baaSJan Lentfer
95170224baaSJan Lentfer
95270224baaSJan Lentfer /* Free any superblocks we couldn't link */
95370224baaSJan Lentfer while (blockcur) {
95470224baaSJan Lentfer block = TAILQ_NEXT(blockcur, sb_entry);
95570224baaSJan Lentfer superblock_free(pf, blockcur);
95670224baaSJan Lentfer blockcur = block;
95770224baaSJan Lentfer }
95870224baaSJan Lentfer return (0);
95970224baaSJan Lentfer }
96070224baaSJan Lentfer
96170224baaSJan Lentfer
96270224baaSJan Lentfer /*
96370224baaSJan Lentfer * Compare a rule to a skiplist to see if the rule is a member
96470224baaSJan Lentfer */
96570224baaSJan Lentfer int
skip_compare(int skipnum,struct pf_skip_step * skiplist,struct pf_opt_rule * por)96670224baaSJan Lentfer skip_compare(int skipnum, struct pf_skip_step *skiplist,
96770224baaSJan Lentfer struct pf_opt_rule *por)
96870224baaSJan Lentfer {
96970224baaSJan Lentfer struct pf_rule *a, *b;
97070224baaSJan Lentfer if (skipnum >= PF_SKIP_COUNT || skipnum < 0)
97170224baaSJan Lentfer errx(1, "skip_compare() out of bounds");
97270224baaSJan Lentfer a = &por->por_rule;
97370224baaSJan Lentfer b = &TAILQ_FIRST(&skiplist->ps_rules)->por_rule;
97470224baaSJan Lentfer
97570224baaSJan Lentfer return ((skip_comparitors[skipnum])(a, b));
97670224baaSJan Lentfer }
97770224baaSJan Lentfer
97870224baaSJan Lentfer
97970224baaSJan Lentfer /*
98070224baaSJan Lentfer * Add a rule to a skiplist
98170224baaSJan Lentfer */
98270224baaSJan Lentfer void
skip_append(struct superblock * superblock,int skipnum,struct pf_skip_step * skiplist,struct pf_opt_rule * por)98370224baaSJan Lentfer skip_append(struct superblock *superblock, int skipnum,
98470224baaSJan Lentfer struct pf_skip_step *skiplist, struct pf_opt_rule *por)
98570224baaSJan Lentfer {
98670224baaSJan Lentfer struct pf_skip_step *prev;
98770224baaSJan Lentfer
98870224baaSJan Lentfer skiplist->ps_count++;
98970224baaSJan Lentfer TAILQ_INSERT_TAIL(&skiplist->ps_rules, por, por_skip_entry[skipnum]);
99070224baaSJan Lentfer
99170224baaSJan Lentfer /* Keep the list of skiplists sorted by whichever is larger */
99270224baaSJan Lentfer while ((prev = TAILQ_PREV(skiplist, skiplist, ps_entry)) &&
99370224baaSJan Lentfer prev->ps_count < skiplist->ps_count) {
99470224baaSJan Lentfer TAILQ_REMOVE(&superblock->sb_skipsteps[skipnum],
99570224baaSJan Lentfer skiplist, ps_entry);
99670224baaSJan Lentfer TAILQ_INSERT_BEFORE(prev, skiplist, ps_entry);
99770224baaSJan Lentfer }
99870224baaSJan Lentfer }
99970224baaSJan Lentfer
100070224baaSJan Lentfer
100170224baaSJan Lentfer /*
100270224baaSJan Lentfer * Remove a rule from the other skiplist calculations.
100370224baaSJan Lentfer */
100470224baaSJan Lentfer void
remove_from_skipsteps(struct skiplist * head,struct superblock * block,struct pf_opt_rule * por,struct pf_skip_step * active_list)100570224baaSJan Lentfer remove_from_skipsteps(struct skiplist *head, struct superblock *block,
100670224baaSJan Lentfer struct pf_opt_rule *por, struct pf_skip_step *active_list)
100770224baaSJan Lentfer {
100870224baaSJan Lentfer struct pf_skip_step *sk, *next;
100970224baaSJan Lentfer struct pf_opt_rule *p2;
101070224baaSJan Lentfer int i, found;
101170224baaSJan Lentfer
101270224baaSJan Lentfer for (i = 0; i < PF_SKIP_COUNT; i++) {
101370224baaSJan Lentfer sk = TAILQ_FIRST(&block->sb_skipsteps[i]);
101470224baaSJan Lentfer if (sk == NULL || sk == active_list || sk->ps_count <= 1)
101570224baaSJan Lentfer continue;
101670224baaSJan Lentfer found = 0;
101770224baaSJan Lentfer do {
101870224baaSJan Lentfer TAILQ_FOREACH(p2, &sk->ps_rules, por_skip_entry[i])
101970224baaSJan Lentfer if (p2 == por) {
102070224baaSJan Lentfer TAILQ_REMOVE(&sk->ps_rules, p2,
102170224baaSJan Lentfer por_skip_entry[i]);
102270224baaSJan Lentfer found = 1;
102370224baaSJan Lentfer sk->ps_count--;
102470224baaSJan Lentfer break;
102570224baaSJan Lentfer }
102670224baaSJan Lentfer } while (!found && (sk = TAILQ_NEXT(sk, ps_entry)));
102770224baaSJan Lentfer if (found && sk) {
102870224baaSJan Lentfer /* Does this change the sorting order? */
102970224baaSJan Lentfer while ((next = TAILQ_NEXT(sk, ps_entry)) &&
103070224baaSJan Lentfer next->ps_count > sk->ps_count) {
103170224baaSJan Lentfer TAILQ_REMOVE(head, sk, ps_entry);
103270224baaSJan Lentfer TAILQ_INSERT_AFTER(head, next, sk, ps_entry);
103370224baaSJan Lentfer }
103470224baaSJan Lentfer #ifdef OPT_DEBUG
103570224baaSJan Lentfer next = TAILQ_NEXT(sk, ps_entry);
103670224baaSJan Lentfer assert(next == NULL || next->ps_count <= sk->ps_count);
103770224baaSJan Lentfer #endif /* OPT_DEBUG */
103870224baaSJan Lentfer }
103970224baaSJan Lentfer }
104070224baaSJan Lentfer }
104170224baaSJan Lentfer
104270224baaSJan Lentfer
104370224baaSJan Lentfer /* Compare two rules AF field for skiplist construction */
104470224baaSJan Lentfer int
skip_cmp_af(struct pf_rule * a,struct pf_rule * b)104570224baaSJan Lentfer skip_cmp_af(struct pf_rule *a, struct pf_rule *b)
104670224baaSJan Lentfer {
104770224baaSJan Lentfer if (a->af != b->af || a->af == 0)
104870224baaSJan Lentfer return (1);
104970224baaSJan Lentfer return (0);
105070224baaSJan Lentfer }
105170224baaSJan Lentfer
105270224baaSJan Lentfer /* Compare two rules DIRECTION field for skiplist construction */
105370224baaSJan Lentfer int
skip_cmp_dir(struct pf_rule * a,struct pf_rule * b)105470224baaSJan Lentfer skip_cmp_dir(struct pf_rule *a, struct pf_rule *b)
105570224baaSJan Lentfer {
105670224baaSJan Lentfer if (a->direction == 0 || a->direction != b->direction)
105770224baaSJan Lentfer return (1);
105870224baaSJan Lentfer return (0);
105970224baaSJan Lentfer }
106070224baaSJan Lentfer
106170224baaSJan Lentfer /* Compare two rules DST Address field for skiplist construction */
106270224baaSJan Lentfer int
skip_cmp_dst_addr(struct pf_rule * a,struct pf_rule * b)106370224baaSJan Lentfer skip_cmp_dst_addr(struct pf_rule *a, struct pf_rule *b)
106470224baaSJan Lentfer {
106570224baaSJan Lentfer if (a->dst.neg != b->dst.neg ||
106670224baaSJan Lentfer a->dst.addr.type != b->dst.addr.type)
106770224baaSJan Lentfer return (1);
106870224baaSJan Lentfer /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
106970224baaSJan Lentfer * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
107070224baaSJan Lentfer * a->proto == IPPROTO_ICMP
107170224baaSJan Lentfer * return (1);
107270224baaSJan Lentfer */
107370224baaSJan Lentfer switch (a->dst.addr.type) {
107470224baaSJan Lentfer case PF_ADDR_ADDRMASK:
107570224baaSJan Lentfer if (memcmp(&a->dst.addr.v.a.addr, &b->dst.addr.v.a.addr,
107670224baaSJan Lentfer sizeof(a->dst.addr.v.a.addr)) ||
107770224baaSJan Lentfer memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask,
107870224baaSJan Lentfer sizeof(a->dst.addr.v.a.mask)) ||
107970224baaSJan Lentfer (a->dst.addr.v.a.addr.addr32[0] == 0 &&
108070224baaSJan Lentfer a->dst.addr.v.a.addr.addr32[1] == 0 &&
108170224baaSJan Lentfer a->dst.addr.v.a.addr.addr32[2] == 0 &&
108270224baaSJan Lentfer a->dst.addr.v.a.addr.addr32[3] == 0))
108370224baaSJan Lentfer return (1);
108470224baaSJan Lentfer return (0);
108570224baaSJan Lentfer case PF_ADDR_DYNIFTL:
108670224baaSJan Lentfer if (strcmp(a->dst.addr.v.ifname, b->dst.addr.v.ifname) != 0 ||
1087*e3cdbf6cSSascha Wildner a->dst.addr.iflags != b->dst.addr.iflags ||
108870224baaSJan Lentfer memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask,
108970224baaSJan Lentfer sizeof(a->dst.addr.v.a.mask)))
109070224baaSJan Lentfer return (1);
109170224baaSJan Lentfer return (0);
109270224baaSJan Lentfer case PF_ADDR_NOROUTE:
109370224baaSJan Lentfer case PF_ADDR_URPFFAILED:
109470224baaSJan Lentfer return (0);
109570224baaSJan Lentfer case PF_ADDR_TABLE:
109670224baaSJan Lentfer return (strcmp(a->dst.addr.v.tblname, b->dst.addr.v.tblname));
109770224baaSJan Lentfer }
109870224baaSJan Lentfer return (1);
109970224baaSJan Lentfer }
110070224baaSJan Lentfer
110170224baaSJan Lentfer /* Compare two rules DST port field for skiplist construction */
110270224baaSJan Lentfer int
skip_cmp_dst_port(struct pf_rule * a,struct pf_rule * b)110370224baaSJan Lentfer skip_cmp_dst_port(struct pf_rule *a, struct pf_rule *b)
110470224baaSJan Lentfer {
110570224baaSJan Lentfer /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
110670224baaSJan Lentfer * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
110770224baaSJan Lentfer * a->proto == IPPROTO_ICMP
110870224baaSJan Lentfer * return (1);
110970224baaSJan Lentfer */
111070224baaSJan Lentfer if (a->dst.port_op == PF_OP_NONE || a->dst.port_op != b->dst.port_op ||
111170224baaSJan Lentfer a->dst.port[0] != b->dst.port[0] ||
111270224baaSJan Lentfer a->dst.port[1] != b->dst.port[1])
111370224baaSJan Lentfer return (1);
111470224baaSJan Lentfer return (0);
111570224baaSJan Lentfer }
111670224baaSJan Lentfer
111770224baaSJan Lentfer /* Compare two rules IFP field for skiplist construction */
111870224baaSJan Lentfer int
skip_cmp_ifp(struct pf_rule * a,struct pf_rule * b)111970224baaSJan Lentfer skip_cmp_ifp(struct pf_rule *a, struct pf_rule *b)
112070224baaSJan Lentfer {
112170224baaSJan Lentfer if (strcmp(a->ifname, b->ifname) || a->ifname[0] == '\0')
112270224baaSJan Lentfer return (1);
112370224baaSJan Lentfer return (a->ifnot != b->ifnot);
112470224baaSJan Lentfer }
112570224baaSJan Lentfer
112670224baaSJan Lentfer /* Compare two rules PROTO field for skiplist construction */
112770224baaSJan Lentfer int
skip_cmp_proto(struct pf_rule * a,struct pf_rule * b)112870224baaSJan Lentfer skip_cmp_proto(struct pf_rule *a, struct pf_rule *b)
112970224baaSJan Lentfer {
113070224baaSJan Lentfer return (a->proto != b->proto || a->proto == 0);
113170224baaSJan Lentfer }
113270224baaSJan Lentfer
113370224baaSJan Lentfer /* Compare two rules SRC addr field for skiplist construction */
113470224baaSJan Lentfer int
skip_cmp_src_addr(struct pf_rule * a,struct pf_rule * b)113570224baaSJan Lentfer skip_cmp_src_addr(struct pf_rule *a, struct pf_rule *b)
113670224baaSJan Lentfer {
113770224baaSJan Lentfer if (a->src.neg != b->src.neg ||
113870224baaSJan Lentfer a->src.addr.type != b->src.addr.type)
113970224baaSJan Lentfer return (1);
114070224baaSJan Lentfer /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
114170224baaSJan Lentfer * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
114270224baaSJan Lentfer * a->proto == IPPROTO_ICMP
114370224baaSJan Lentfer * return (1);
114470224baaSJan Lentfer */
114570224baaSJan Lentfer switch (a->src.addr.type) {
114670224baaSJan Lentfer case PF_ADDR_ADDRMASK:
114770224baaSJan Lentfer if (memcmp(&a->src.addr.v.a.addr, &b->src.addr.v.a.addr,
114870224baaSJan Lentfer sizeof(a->src.addr.v.a.addr)) ||
114970224baaSJan Lentfer memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask,
115070224baaSJan Lentfer sizeof(a->src.addr.v.a.mask)) ||
115170224baaSJan Lentfer (a->src.addr.v.a.addr.addr32[0] == 0 &&
115270224baaSJan Lentfer a->src.addr.v.a.addr.addr32[1] == 0 &&
115370224baaSJan Lentfer a->src.addr.v.a.addr.addr32[2] == 0 &&
115470224baaSJan Lentfer a->src.addr.v.a.addr.addr32[3] == 0))
115570224baaSJan Lentfer return (1);
115670224baaSJan Lentfer return (0);
115770224baaSJan Lentfer case PF_ADDR_DYNIFTL:
115870224baaSJan Lentfer if (strcmp(a->src.addr.v.ifname, b->src.addr.v.ifname) != 0 ||
1159*e3cdbf6cSSascha Wildner a->src.addr.iflags != b->src.addr.iflags ||
116070224baaSJan Lentfer memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask,
116170224baaSJan Lentfer sizeof(a->src.addr.v.a.mask)))
116270224baaSJan Lentfer return (1);
116370224baaSJan Lentfer return (0);
116470224baaSJan Lentfer case PF_ADDR_NOROUTE:
116570224baaSJan Lentfer case PF_ADDR_URPFFAILED:
116670224baaSJan Lentfer return (0);
116770224baaSJan Lentfer case PF_ADDR_TABLE:
116870224baaSJan Lentfer return (strcmp(a->src.addr.v.tblname, b->src.addr.v.tblname));
116970224baaSJan Lentfer }
117070224baaSJan Lentfer return (1);
117170224baaSJan Lentfer }
117270224baaSJan Lentfer
117370224baaSJan Lentfer /* Compare two rules SRC port field for skiplist construction */
117470224baaSJan Lentfer int
skip_cmp_src_port(struct pf_rule * a,struct pf_rule * b)117570224baaSJan Lentfer skip_cmp_src_port(struct pf_rule *a, struct pf_rule *b)
117670224baaSJan Lentfer {
117770224baaSJan Lentfer if (a->src.port_op == PF_OP_NONE || a->src.port_op != b->src.port_op ||
117870224baaSJan Lentfer a->src.port[0] != b->src.port[0] ||
117970224baaSJan Lentfer a->src.port[1] != b->src.port[1])
118070224baaSJan Lentfer return (1);
118170224baaSJan Lentfer /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
118270224baaSJan Lentfer * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
118370224baaSJan Lentfer * a->proto == IPPROTO_ICMP
118470224baaSJan Lentfer * return (1);
118570224baaSJan Lentfer */
118670224baaSJan Lentfer return (0);
118770224baaSJan Lentfer }
118870224baaSJan Lentfer
118970224baaSJan Lentfer
119070224baaSJan Lentfer void
skip_init(void)119170224baaSJan Lentfer skip_init(void)
119270224baaSJan Lentfer {
119370224baaSJan Lentfer struct {
119470224baaSJan Lentfer char *name;
119570224baaSJan Lentfer int skipnum;
119670224baaSJan Lentfer int (*func)(struct pf_rule *, struct pf_rule *);
119770224baaSJan Lentfer } comps[] = PF_SKIP_COMPARITORS;
119870224baaSJan Lentfer int skipnum, i;
119970224baaSJan Lentfer
120070224baaSJan Lentfer for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) {
120170224baaSJan Lentfer for (i = 0; i < sizeof(comps)/sizeof(*comps); i++)
120270224baaSJan Lentfer if (comps[i].skipnum == skipnum) {
120370224baaSJan Lentfer skip_comparitors[skipnum] = comps[i].func;
120470224baaSJan Lentfer skip_comparitors_names[skipnum] = comps[i].name;
120570224baaSJan Lentfer }
120670224baaSJan Lentfer }
120770224baaSJan Lentfer for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++)
120870224baaSJan Lentfer if (skip_comparitors[skipnum] == NULL)
120970224baaSJan Lentfer errx(1, "Need to add skip step comparitor to pfctl?!");
121070224baaSJan Lentfer }
121170224baaSJan Lentfer
121270224baaSJan Lentfer /*
121370224baaSJan Lentfer * Add a host/netmask to a table
121470224baaSJan Lentfer */
121570224baaSJan Lentfer int
add_opt_table(struct pfctl * pf,struct pf_opt_tbl ** tbl,sa_family_t af,struct pf_rule_addr * addr)121670224baaSJan Lentfer add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af,
121770224baaSJan Lentfer struct pf_rule_addr *addr)
121870224baaSJan Lentfer {
121970224baaSJan Lentfer #ifdef OPT_DEBUG
122070224baaSJan Lentfer char buf[128];
122170224baaSJan Lentfer #endif /* OPT_DEBUG */
122270224baaSJan Lentfer static int tablenum = 0;
122370224baaSJan Lentfer struct node_host node_host;
122470224baaSJan Lentfer
122570224baaSJan Lentfer if (*tbl == NULL) {
122670224baaSJan Lentfer if ((*tbl = calloc(1, sizeof(**tbl))) == NULL ||
122770224baaSJan Lentfer ((*tbl)->pt_buf = calloc(1, sizeof(*(*tbl)->pt_buf))) ==
122870224baaSJan Lentfer NULL)
122970224baaSJan Lentfer err(1, "calloc");
123070224baaSJan Lentfer (*tbl)->pt_buf->pfrb_type = PFRB_ADDRS;
123170224baaSJan Lentfer SIMPLEQ_INIT(&(*tbl)->pt_nodes);
123270224baaSJan Lentfer
123370224baaSJan Lentfer /* This is just a temporary table name */
123470224baaSJan Lentfer snprintf((*tbl)->pt_name, sizeof((*tbl)->pt_name), "%s%d",
123570224baaSJan Lentfer PF_OPT_TABLE_PREFIX, tablenum++);
123670224baaSJan Lentfer DEBUG("creating table <%s>", (*tbl)->pt_name);
123770224baaSJan Lentfer }
123870224baaSJan Lentfer
123970224baaSJan Lentfer memset(&node_host, 0, sizeof(node_host));
124070224baaSJan Lentfer node_host.af = af;
124170224baaSJan Lentfer node_host.addr = addr->addr;
124270224baaSJan Lentfer
124370224baaSJan Lentfer #ifdef OPT_DEBUG
124470224baaSJan Lentfer DEBUG("<%s> adding %s/%d", (*tbl)->pt_name, inet_ntop(af,
124570224baaSJan Lentfer &node_host.addr.v.a.addr, buf, sizeof(buf)),
124670224baaSJan Lentfer unmask(&node_host.addr.v.a.mask, af));
124770224baaSJan Lentfer #endif /* OPT_DEBUG */
124870224baaSJan Lentfer
124970224baaSJan Lentfer if (append_addr_host((*tbl)->pt_buf, &node_host, 0, 0)) {
125070224baaSJan Lentfer warn("failed to add host");
125170224baaSJan Lentfer return (1);
125270224baaSJan Lentfer }
125370224baaSJan Lentfer if (pf->opts & PF_OPT_VERBOSE) {
125470224baaSJan Lentfer struct node_tinit *ti;
125570224baaSJan Lentfer
125670224baaSJan Lentfer if ((ti = calloc(1, sizeof(*ti))) == NULL)
125770224baaSJan Lentfer err(1, "malloc");
125870224baaSJan Lentfer if ((ti->host = malloc(sizeof(*ti->host))) == NULL)
125970224baaSJan Lentfer err(1, "malloc");
126070224baaSJan Lentfer memcpy(ti->host, &node_host, sizeof(*ti->host));
126170224baaSJan Lentfer SIMPLEQ_INSERT_TAIL(&(*tbl)->pt_nodes, ti, entries);
126270224baaSJan Lentfer }
126370224baaSJan Lentfer
126470224baaSJan Lentfer (*tbl)->pt_rulecount++;
126570224baaSJan Lentfer if ((*tbl)->pt_rulecount == TABLE_THRESHOLD)
126670224baaSJan Lentfer DEBUG("table <%s> now faster than skip steps", (*tbl)->pt_name);
126770224baaSJan Lentfer
126870224baaSJan Lentfer return (0);
126970224baaSJan Lentfer }
127070224baaSJan Lentfer
127170224baaSJan Lentfer
127270224baaSJan Lentfer /*
127370224baaSJan Lentfer * Do the dirty work of choosing an unused table name and creating it.
127470224baaSJan Lentfer * (be careful with the table name, it might already be used in another anchor)
127570224baaSJan Lentfer */
127670224baaSJan Lentfer int
pf_opt_create_table(struct pfctl * pf,struct pf_opt_tbl * tbl)127770224baaSJan Lentfer pf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl)
127870224baaSJan Lentfer {
127970224baaSJan Lentfer static int tablenum;
128070224baaSJan Lentfer const struct pfr_table *t;
128170224baaSJan Lentfer
128270224baaSJan Lentfer if (table_buffer.pfrb_type == 0) {
128370224baaSJan Lentfer /* Initialize the list of tables */
128470224baaSJan Lentfer table_buffer.pfrb_type = PFRB_TABLES;
128570224baaSJan Lentfer for (;;) {
128670224baaSJan Lentfer pfr_buf_grow(&table_buffer, table_buffer.pfrb_size);
128770224baaSJan Lentfer table_buffer.pfrb_size = table_buffer.pfrb_msize;
128870224baaSJan Lentfer if (pfr_get_tables(NULL, table_buffer.pfrb_caddr,
128970224baaSJan Lentfer &table_buffer.pfrb_size, PFR_FLAG_ALLRSETS))
129070224baaSJan Lentfer err(1, "pfr_get_tables");
129170224baaSJan Lentfer if (table_buffer.pfrb_size <= table_buffer.pfrb_msize)
129270224baaSJan Lentfer break;
129370224baaSJan Lentfer }
129470224baaSJan Lentfer table_identifier = arc4random();
129570224baaSJan Lentfer }
129670224baaSJan Lentfer
129770224baaSJan Lentfer /* XXX would be *really* nice to avoid duplicating identical tables */
129870224baaSJan Lentfer
129970224baaSJan Lentfer /* Now we have to pick a table name that isn't used */
130070224baaSJan Lentfer again:
130170224baaSJan Lentfer DEBUG("translating temporary table <%s> to <%s%x_%d>", tbl->pt_name,
130270224baaSJan Lentfer PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
130370224baaSJan Lentfer snprintf(tbl->pt_name, sizeof(tbl->pt_name), "%s%x_%d",
130470224baaSJan Lentfer PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
130570224baaSJan Lentfer PFRB_FOREACH(t, &table_buffer) {
130670224baaSJan Lentfer if (strcasecmp(t->pfrt_name, tbl->pt_name) == 0) {
130770224baaSJan Lentfer /* Collision. Try again */
130870224baaSJan Lentfer DEBUG("wow, table <%s> in use. trying again",
130970224baaSJan Lentfer tbl->pt_name);
131070224baaSJan Lentfer table_identifier = arc4random();
131170224baaSJan Lentfer goto again;
131270224baaSJan Lentfer }
131370224baaSJan Lentfer }
131470224baaSJan Lentfer tablenum++;
131570224baaSJan Lentfer
131670224baaSJan Lentfer
131770224baaSJan Lentfer if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1,
1318ed1f0be2SJan Lentfer pf->astack[0]->name, tbl->pt_buf, pf->astack[0]->ruleset.tticket)) {
1319ed1f0be2SJan Lentfer warn("failed to create table %s in %s",
1320ed1f0be2SJan Lentfer tbl->pt_name, pf->astack[0]->name);
132170224baaSJan Lentfer return (1);
132270224baaSJan Lentfer }
132370224baaSJan Lentfer return (0);
132470224baaSJan Lentfer }
132570224baaSJan Lentfer
132670224baaSJan Lentfer /*
132770224baaSJan Lentfer * Partition the flat ruleset into a list of distinct superblocks
132870224baaSJan Lentfer */
132970224baaSJan Lentfer int
construct_superblocks(struct pfctl * pf,struct pf_opt_queue * opt_queue,struct superblocks * superblocks)133070224baaSJan Lentfer construct_superblocks(struct pfctl *pf, struct pf_opt_queue *opt_queue,
133170224baaSJan Lentfer struct superblocks *superblocks)
133270224baaSJan Lentfer {
133370224baaSJan Lentfer struct superblock *block = NULL;
133470224baaSJan Lentfer struct pf_opt_rule *por;
133570224baaSJan Lentfer int i;
133670224baaSJan Lentfer
133770224baaSJan Lentfer while (!TAILQ_EMPTY(opt_queue)) {
133870224baaSJan Lentfer por = TAILQ_FIRST(opt_queue);
133970224baaSJan Lentfer TAILQ_REMOVE(opt_queue, por, por_entry);
134070224baaSJan Lentfer if (block == NULL || !superblock_inclusive(block, por)) {
134170224baaSJan Lentfer if ((block = calloc(1, sizeof(*block))) == NULL) {
134270224baaSJan Lentfer warn("calloc");
134370224baaSJan Lentfer return (1);
134470224baaSJan Lentfer }
134570224baaSJan Lentfer TAILQ_INIT(&block->sb_rules);
134670224baaSJan Lentfer for (i = 0; i < PF_SKIP_COUNT; i++)
134770224baaSJan Lentfer TAILQ_INIT(&block->sb_skipsteps[i]);
134870224baaSJan Lentfer TAILQ_INSERT_TAIL(superblocks, block, sb_entry);
134970224baaSJan Lentfer }
135070224baaSJan Lentfer TAILQ_INSERT_TAIL(&block->sb_rules, por, por_entry);
135170224baaSJan Lentfer }
135270224baaSJan Lentfer
135370224baaSJan Lentfer return (0);
135470224baaSJan Lentfer }
135570224baaSJan Lentfer
135670224baaSJan Lentfer
135770224baaSJan Lentfer /*
135870224baaSJan Lentfer * Compare two rule addresses
135970224baaSJan Lentfer */
136070224baaSJan Lentfer int
addrs_equal(struct pf_rule_addr * a,struct pf_rule_addr * b)136170224baaSJan Lentfer addrs_equal(struct pf_rule_addr *a, struct pf_rule_addr *b)
136270224baaSJan Lentfer {
136370224baaSJan Lentfer if (a->neg != b->neg)
136470224baaSJan Lentfer return (0);
136570224baaSJan Lentfer return (memcmp(&a->addr, &b->addr, sizeof(a->addr)) == 0);
136670224baaSJan Lentfer }
136770224baaSJan Lentfer
136870224baaSJan Lentfer
136970224baaSJan Lentfer /*
137070224baaSJan Lentfer * The addresses are not equal, but can we combine them into one table?
137170224baaSJan Lentfer */
137270224baaSJan Lentfer int
addrs_combineable(struct pf_rule_addr * a,struct pf_rule_addr * b)137370224baaSJan Lentfer addrs_combineable(struct pf_rule_addr *a, struct pf_rule_addr *b)
137470224baaSJan Lentfer {
137570224baaSJan Lentfer if (a->addr.type != PF_ADDR_ADDRMASK ||
137670224baaSJan Lentfer b->addr.type != PF_ADDR_ADDRMASK)
137770224baaSJan Lentfer return (0);
137870224baaSJan Lentfer if (a->neg != b->neg || a->port_op != b->port_op ||
137970224baaSJan Lentfer a->port[0] != b->port[0] || a->port[1] != b->port[1])
138070224baaSJan Lentfer return (0);
138170224baaSJan Lentfer return (1);
138270224baaSJan Lentfer }
138370224baaSJan Lentfer
138470224baaSJan Lentfer
138570224baaSJan Lentfer /*
138670224baaSJan Lentfer * Are we allowed to combine these two rules
138770224baaSJan Lentfer */
138870224baaSJan Lentfer int
rules_combineable(struct pf_rule * p1,struct pf_rule * p2)138970224baaSJan Lentfer rules_combineable(struct pf_rule *p1, struct pf_rule *p2)
139070224baaSJan Lentfer {
139170224baaSJan Lentfer struct pf_rule a, b;
139270224baaSJan Lentfer
139370224baaSJan Lentfer comparable_rule(&a, p1, COMBINED);
139470224baaSJan Lentfer comparable_rule(&b, p2, COMBINED);
139570224baaSJan Lentfer return (memcmp(&a, &b, sizeof(a)) == 0);
139670224baaSJan Lentfer }
139770224baaSJan Lentfer
139870224baaSJan Lentfer
139970224baaSJan Lentfer /*
140070224baaSJan Lentfer * Can a rule be included inside a superblock
140170224baaSJan Lentfer */
140270224baaSJan Lentfer int
superblock_inclusive(struct superblock * block,struct pf_opt_rule * por)140370224baaSJan Lentfer superblock_inclusive(struct superblock *block, struct pf_opt_rule *por)
140470224baaSJan Lentfer {
140570224baaSJan Lentfer struct pf_rule a, b;
140670224baaSJan Lentfer int i, j;
140770224baaSJan Lentfer
140870224baaSJan Lentfer /* First check for hard breaks */
140970224baaSJan Lentfer for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) {
141070224baaSJan Lentfer if (pf_rule_desc[i].prf_type == BARRIER) {
141170224baaSJan Lentfer for (j = 0; j < pf_rule_desc[i].prf_size; j++)
141270224baaSJan Lentfer if (((char *)&por->por_rule)[j +
141370224baaSJan Lentfer pf_rule_desc[i].prf_offset] != 0)
141470224baaSJan Lentfer return (0);
141570224baaSJan Lentfer }
141670224baaSJan Lentfer }
141770224baaSJan Lentfer
141870224baaSJan Lentfer /* per-rule src-track is also a hard break */
141970224baaSJan Lentfer if (por->por_rule.rule_flag & PFRULE_RULESRCTRACK)
142070224baaSJan Lentfer return (0);
142170224baaSJan Lentfer
142270224baaSJan Lentfer /*
1423ed1f0be2SJan Lentfer * Have to handle interface groups separately. Consider the following
142470224baaSJan Lentfer * rules:
142570224baaSJan Lentfer * block on EXTIFS to any port 22
142670224baaSJan Lentfer * pass on em0 to any port 22
142770224baaSJan Lentfer * (where EXTIFS is an arbitrary interface group)
142870224baaSJan Lentfer * The optimizer may decide to re-order the pass rule in front of the
142970224baaSJan Lentfer * block rule. But what if EXTIFS includes em0??? Such a reordering
143070224baaSJan Lentfer * would change the meaning of the ruleset.
143170224baaSJan Lentfer * We can't just lookup the EXTIFS group and check if em0 is a member
143270224baaSJan Lentfer * because the user is allowed to add interfaces to a group during
143370224baaSJan Lentfer * runtime.
143470224baaSJan Lentfer * Ergo interface groups become a defacto superblock break :-(
143570224baaSJan Lentfer */
143670224baaSJan Lentfer if (interface_group(por->por_rule.ifname) ||
143770224baaSJan Lentfer interface_group(TAILQ_FIRST(&block->sb_rules)->por_rule.ifname)) {
143870224baaSJan Lentfer if (strcasecmp(por->por_rule.ifname,
143970224baaSJan Lentfer TAILQ_FIRST(&block->sb_rules)->por_rule.ifname) != 0)
144070224baaSJan Lentfer return (0);
144170224baaSJan Lentfer }
144270224baaSJan Lentfer
144370224baaSJan Lentfer comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, NOMERGE);
144470224baaSJan Lentfer comparable_rule(&b, &por->por_rule, NOMERGE);
144570224baaSJan Lentfer if (memcmp(&a, &b, sizeof(a)) == 0)
144670224baaSJan Lentfer return (1);
144770224baaSJan Lentfer
144870224baaSJan Lentfer #ifdef OPT_DEBUG
144970224baaSJan Lentfer for (i = 0; i < sizeof(por->por_rule); i++) {
145070224baaSJan Lentfer int closest = -1;
145170224baaSJan Lentfer if (((u_int8_t *)&a)[i] != ((u_int8_t *)&b)[i]) {
145270224baaSJan Lentfer for (j = 0; j < sizeof(pf_rule_desc) /
145370224baaSJan Lentfer sizeof(*pf_rule_desc); j++) {
145470224baaSJan Lentfer if (i >= pf_rule_desc[j].prf_offset &&
145570224baaSJan Lentfer i < pf_rule_desc[j].prf_offset +
145670224baaSJan Lentfer pf_rule_desc[j].prf_size) {
145770224baaSJan Lentfer DEBUG("superblock break @ %d due to %s",
145870224baaSJan Lentfer por->por_rule.nr,
145970224baaSJan Lentfer pf_rule_desc[j].prf_name);
146070224baaSJan Lentfer return (0);
146170224baaSJan Lentfer }
146270224baaSJan Lentfer if (i > pf_rule_desc[j].prf_offset) {
146370224baaSJan Lentfer if (closest == -1 ||
146470224baaSJan Lentfer i-pf_rule_desc[j].prf_offset <
146570224baaSJan Lentfer i-pf_rule_desc[closest].prf_offset)
146670224baaSJan Lentfer closest = j;
146770224baaSJan Lentfer }
146870224baaSJan Lentfer }
146970224baaSJan Lentfer
147070224baaSJan Lentfer if (closest >= 0)
1471ed1f0be2SJan Lentfer DEBUG("superblock break @ %d on %s+%lxh",
147270224baaSJan Lentfer por->por_rule.nr,
147370224baaSJan Lentfer pf_rule_desc[closest].prf_name,
147470224baaSJan Lentfer i - pf_rule_desc[closest].prf_offset -
147570224baaSJan Lentfer pf_rule_desc[closest].prf_size);
147670224baaSJan Lentfer else
147770224baaSJan Lentfer DEBUG("superblock break @ %d on field @ %d",
147870224baaSJan Lentfer por->por_rule.nr, i);
147970224baaSJan Lentfer return (0);
148070224baaSJan Lentfer }
148170224baaSJan Lentfer }
148270224baaSJan Lentfer #endif /* OPT_DEBUG */
148370224baaSJan Lentfer
148470224baaSJan Lentfer return (0);
148570224baaSJan Lentfer }
148670224baaSJan Lentfer
148770224baaSJan Lentfer
148870224baaSJan Lentfer /*
148970224baaSJan Lentfer * Figure out if an interface name is an actual interface or actually a
149070224baaSJan Lentfer * group of interfaces.
149170224baaSJan Lentfer */
149270224baaSJan Lentfer int
interface_group(const char * ifname)149370224baaSJan Lentfer interface_group(const char *ifname)
149470224baaSJan Lentfer {
149570224baaSJan Lentfer if (ifname == NULL || !ifname[0])
149670224baaSJan Lentfer return (0);
149770224baaSJan Lentfer
149870224baaSJan Lentfer /* Real interfaces must end in a number, interface groups do not */
149970224baaSJan Lentfer if (isdigit(ifname[strlen(ifname) - 1]))
150070224baaSJan Lentfer return (0);
150170224baaSJan Lentfer else
150270224baaSJan Lentfer return (1);
150370224baaSJan Lentfer }
150470224baaSJan Lentfer
150570224baaSJan Lentfer
150670224baaSJan Lentfer /*
150770224baaSJan Lentfer * Make a rule that can directly compared by memcmp()
150870224baaSJan Lentfer */
150970224baaSJan Lentfer void
comparable_rule(struct pf_rule * dst,const struct pf_rule * src,int type)151070224baaSJan Lentfer comparable_rule(struct pf_rule *dst, const struct pf_rule *src, int type)
151170224baaSJan Lentfer {
151270224baaSJan Lentfer int i;
151370224baaSJan Lentfer /*
151470224baaSJan Lentfer * To simplify the comparison, we just zero out the fields that are
151570224baaSJan Lentfer * allowed to be different and then do a simple memcmp()
151670224baaSJan Lentfer */
151770224baaSJan Lentfer memcpy(dst, src, sizeof(*dst));
151870224baaSJan Lentfer for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++)
151970224baaSJan Lentfer if (pf_rule_desc[i].prf_type >= type) {
152070224baaSJan Lentfer #ifdef OPT_DEBUG
152170224baaSJan Lentfer assert(pf_rule_desc[i].prf_type != NEVER ||
152270224baaSJan Lentfer *(((char *)dst) + pf_rule_desc[i].prf_offset) == 0);
152370224baaSJan Lentfer #endif /* OPT_DEBUG */
152470224baaSJan Lentfer memset(((char *)dst) + pf_rule_desc[i].prf_offset, 0,
152570224baaSJan Lentfer pf_rule_desc[i].prf_size);
152670224baaSJan Lentfer }
152770224baaSJan Lentfer }
152870224baaSJan Lentfer
152970224baaSJan Lentfer
153070224baaSJan Lentfer /*
153170224baaSJan Lentfer * Remove superset information from two rules so we can directly compare them
153270224baaSJan Lentfer * with memcmp()
153370224baaSJan Lentfer */
153470224baaSJan Lentfer void
exclude_supersets(struct pf_rule * super,struct pf_rule * sub)153570224baaSJan Lentfer exclude_supersets(struct pf_rule *super, struct pf_rule *sub)
153670224baaSJan Lentfer {
153770224baaSJan Lentfer if (super->ifname[0] == '\0')
153870224baaSJan Lentfer memset(sub->ifname, 0, sizeof(sub->ifname));
153970224baaSJan Lentfer if (super->direction == PF_INOUT)
154070224baaSJan Lentfer sub->direction = PF_INOUT;
154170224baaSJan Lentfer if ((super->proto == 0 || super->proto == sub->proto) &&
154270224baaSJan Lentfer super->flags == 0 && super->flagset == 0 && (sub->flags ||
154370224baaSJan Lentfer sub->flagset)) {
154470224baaSJan Lentfer sub->flags = super->flags;
154570224baaSJan Lentfer sub->flagset = super->flagset;
154670224baaSJan Lentfer }
154770224baaSJan Lentfer if (super->proto == 0)
154870224baaSJan Lentfer sub->proto = 0;
154970224baaSJan Lentfer
155070224baaSJan Lentfer if (super->src.port_op == 0) {
155170224baaSJan Lentfer sub->src.port_op = 0;
155270224baaSJan Lentfer sub->src.port[0] = 0;
155370224baaSJan Lentfer sub->src.port[1] = 0;
155470224baaSJan Lentfer }
155570224baaSJan Lentfer if (super->dst.port_op == 0) {
155670224baaSJan Lentfer sub->dst.port_op = 0;
155770224baaSJan Lentfer sub->dst.port[0] = 0;
155870224baaSJan Lentfer sub->dst.port[1] = 0;
155970224baaSJan Lentfer }
156070224baaSJan Lentfer
156170224baaSJan Lentfer if (super->src.addr.type == PF_ADDR_ADDRMASK && !super->src.neg &&
156270224baaSJan Lentfer !sub->src.neg && super->src.addr.v.a.mask.addr32[0] == 0 &&
156370224baaSJan Lentfer super->src.addr.v.a.mask.addr32[1] == 0 &&
156470224baaSJan Lentfer super->src.addr.v.a.mask.addr32[2] == 0 &&
156570224baaSJan Lentfer super->src.addr.v.a.mask.addr32[3] == 0)
156670224baaSJan Lentfer memset(&sub->src.addr, 0, sizeof(sub->src.addr));
156770224baaSJan Lentfer else if (super->src.addr.type == PF_ADDR_ADDRMASK &&
156870224baaSJan Lentfer sub->src.addr.type == PF_ADDR_ADDRMASK &&
156970224baaSJan Lentfer super->src.neg == sub->src.neg &&
157070224baaSJan Lentfer super->af == sub->af &&
157170224baaSJan Lentfer unmask(&super->src.addr.v.a.mask, super->af) <
157270224baaSJan Lentfer unmask(&sub->src.addr.v.a.mask, sub->af) &&
157370224baaSJan Lentfer super->src.addr.v.a.addr.addr32[0] ==
157470224baaSJan Lentfer (sub->src.addr.v.a.addr.addr32[0] &
157570224baaSJan Lentfer super->src.addr.v.a.mask.addr32[0]) &&
157670224baaSJan Lentfer super->src.addr.v.a.addr.addr32[1] ==
157770224baaSJan Lentfer (sub->src.addr.v.a.addr.addr32[1] &
157870224baaSJan Lentfer super->src.addr.v.a.mask.addr32[1]) &&
157970224baaSJan Lentfer super->src.addr.v.a.addr.addr32[2] ==
158070224baaSJan Lentfer (sub->src.addr.v.a.addr.addr32[2] &
158170224baaSJan Lentfer super->src.addr.v.a.mask.addr32[2]) &&
158270224baaSJan Lentfer super->src.addr.v.a.addr.addr32[3] ==
158370224baaSJan Lentfer (sub->src.addr.v.a.addr.addr32[3] &
158470224baaSJan Lentfer super->src.addr.v.a.mask.addr32[3])) {
158570224baaSJan Lentfer /* sub->src.addr is a subset of super->src.addr/mask */
158670224baaSJan Lentfer memcpy(&sub->src.addr, &super->src.addr, sizeof(sub->src.addr));
158770224baaSJan Lentfer }
158870224baaSJan Lentfer
158970224baaSJan Lentfer if (super->dst.addr.type == PF_ADDR_ADDRMASK && !super->dst.neg &&
159070224baaSJan Lentfer !sub->dst.neg && super->dst.addr.v.a.mask.addr32[0] == 0 &&
159170224baaSJan Lentfer super->dst.addr.v.a.mask.addr32[1] == 0 &&
159270224baaSJan Lentfer super->dst.addr.v.a.mask.addr32[2] == 0 &&
159370224baaSJan Lentfer super->dst.addr.v.a.mask.addr32[3] == 0)
159470224baaSJan Lentfer memset(&sub->dst.addr, 0, sizeof(sub->dst.addr));
159570224baaSJan Lentfer else if (super->dst.addr.type == PF_ADDR_ADDRMASK &&
159670224baaSJan Lentfer sub->dst.addr.type == PF_ADDR_ADDRMASK &&
159770224baaSJan Lentfer super->dst.neg == sub->dst.neg &&
159870224baaSJan Lentfer super->af == sub->af &&
159970224baaSJan Lentfer unmask(&super->dst.addr.v.a.mask, super->af) <
160070224baaSJan Lentfer unmask(&sub->dst.addr.v.a.mask, sub->af) &&
160170224baaSJan Lentfer super->dst.addr.v.a.addr.addr32[0] ==
160270224baaSJan Lentfer (sub->dst.addr.v.a.addr.addr32[0] &
160370224baaSJan Lentfer super->dst.addr.v.a.mask.addr32[0]) &&
160470224baaSJan Lentfer super->dst.addr.v.a.addr.addr32[1] ==
160570224baaSJan Lentfer (sub->dst.addr.v.a.addr.addr32[1] &
160670224baaSJan Lentfer super->dst.addr.v.a.mask.addr32[1]) &&
160770224baaSJan Lentfer super->dst.addr.v.a.addr.addr32[2] ==
160870224baaSJan Lentfer (sub->dst.addr.v.a.addr.addr32[2] &
160970224baaSJan Lentfer super->dst.addr.v.a.mask.addr32[2]) &&
161070224baaSJan Lentfer super->dst.addr.v.a.addr.addr32[3] ==
161170224baaSJan Lentfer (sub->dst.addr.v.a.addr.addr32[3] &
161270224baaSJan Lentfer super->dst.addr.v.a.mask.addr32[3])) {
161370224baaSJan Lentfer /* sub->dst.addr is a subset of super->dst.addr/mask */
161470224baaSJan Lentfer memcpy(&sub->dst.addr, &super->dst.addr, sizeof(sub->dst.addr));
161570224baaSJan Lentfer }
161670224baaSJan Lentfer
161770224baaSJan Lentfer if (super->af == 0)
161870224baaSJan Lentfer sub->af = 0;
161970224baaSJan Lentfer }
162070224baaSJan Lentfer
162170224baaSJan Lentfer
162270224baaSJan Lentfer void
superblock_free(struct pfctl * pf,struct superblock * block)162370224baaSJan Lentfer superblock_free(struct pfctl *pf, struct superblock *block)
162470224baaSJan Lentfer {
162570224baaSJan Lentfer struct pf_opt_rule *por;
162670224baaSJan Lentfer while ((por = TAILQ_FIRST(&block->sb_rules))) {
162770224baaSJan Lentfer TAILQ_REMOVE(&block->sb_rules, por, por_entry);
162870224baaSJan Lentfer if (por->por_src_tbl) {
162970224baaSJan Lentfer if (por->por_src_tbl->pt_buf) {
163070224baaSJan Lentfer pfr_buf_clear(por->por_src_tbl->pt_buf);
163170224baaSJan Lentfer free(por->por_src_tbl->pt_buf);
163270224baaSJan Lentfer }
163370224baaSJan Lentfer free(por->por_src_tbl);
163470224baaSJan Lentfer }
163570224baaSJan Lentfer if (por->por_dst_tbl) {
163670224baaSJan Lentfer if (por->por_dst_tbl->pt_buf) {
163770224baaSJan Lentfer pfr_buf_clear(por->por_dst_tbl->pt_buf);
163870224baaSJan Lentfer free(por->por_dst_tbl->pt_buf);
163970224baaSJan Lentfer }
164070224baaSJan Lentfer free(por->por_dst_tbl);
164170224baaSJan Lentfer }
164270224baaSJan Lentfer free(por);
164370224baaSJan Lentfer }
164470224baaSJan Lentfer if (block->sb_profiled_block)
164570224baaSJan Lentfer superblock_free(pf, block->sb_profiled_block);
164670224baaSJan Lentfer free(block);
164770224baaSJan Lentfer }
164870224baaSJan Lentfer
1649