pfctl_optimize.c revision 145840
1145837Smlaier/*	$OpenBSD: pfctl_optimize.c,v 1.5 2005/01/03 15:18:10 frantzen Exp $ */
2145837Smlaier
3145837Smlaier/*
4145837Smlaier * Copyright (c) 2004 Mike Frantzen <frantzen@openbsd.org>
5145837Smlaier *
6145837Smlaier * Permission to use, copy, modify, and distribute this software for any
7145837Smlaier * purpose with or without fee is hereby granted, provided that the above
8145837Smlaier * copyright notice and this permission notice appear in all copies.
9145837Smlaier *
10145837Smlaier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11145837Smlaier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12145837Smlaier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13145837Smlaier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14145837Smlaier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15145837Smlaier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16145837Smlaier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17145837Smlaier */
18145837Smlaier
19145840Smlaier#include <sys/cdefs.h>
20145840Smlaier__FBSDID("$FreeBSD: head/contrib/pf/pfctl/pfctl_optimize.c 145840 2005-05-03 16:55:20Z mlaier $");
21145840Smlaier
22145837Smlaier#include <sys/types.h>
23145837Smlaier#include <sys/ioctl.h>
24145837Smlaier#include <sys/socket.h>
25145837Smlaier
26145837Smlaier#include <net/if.h>
27145837Smlaier#include <net/pfvar.h>
28145837Smlaier
29145837Smlaier#include <netinet/in.h>
30145837Smlaier#include <arpa/inet.h>
31145837Smlaier
32145837Smlaier#include <assert.h>
33145837Smlaier#include <ctype.h>
34145837Smlaier#include <err.h>
35145837Smlaier#include <errno.h>
36145837Smlaier#include <stddef.h>
37145837Smlaier#include <stdio.h>
38145837Smlaier#include <stdlib.h>
39145837Smlaier#include <string.h>
40145837Smlaier
41145837Smlaier#include "pfctl_parser.h"
42145837Smlaier#include "pfctl.h"
43145837Smlaier
44145837Smlaier/* The size at which a table becomes faster than individual rules */
45145837Smlaier#define TABLE_THRESHOLD		6
46145837Smlaier
47145837Smlaier
48145837Smlaier/* #define OPT_DEBUG	1 */
49145837Smlaier#ifdef OPT_DEBUG
50145837Smlaier# define DEBUG(str, v...) \
51145837Smlaier	printf("%s: " str "\n", __FUNCTION__ , ## v)
52145837Smlaier#else
53145837Smlaier# define DEBUG(str, v...) ((void)0)
54145837Smlaier#endif
55145837Smlaier
56145837Smlaier
57145837Smlaier/*
58145837Smlaier * A container that lets us sort a superblock to optimize the skip step jumps
59145837Smlaier */
60145837Smlaierstruct pf_skip_step {
61145837Smlaier	int				ps_count;	/* number of items */
62145837Smlaier	TAILQ_HEAD( , pf_opt_rule)	ps_rules;
63145837Smlaier	TAILQ_ENTRY(pf_skip_step)	ps_entry;
64145837Smlaier};
65145837Smlaier
66145837Smlaier
67145837Smlaier/*
68145837Smlaier * A superblock is a block of adjacent rules of similar action.  If there
69145837Smlaier * are five PASS rules in a row, they all become members of a superblock.
70145837Smlaier * Once we have a superblock, we are free to re-order any rules within it
71145837Smlaier * in order to improve performance; if a packet is passed, it doesn't matter
72145837Smlaier * who passed it.
73145837Smlaier */
74145837Smlaierstruct superblock {
75145837Smlaier	TAILQ_HEAD( , pf_opt_rule)		 sb_rules;
76145837Smlaier	TAILQ_ENTRY(superblock)			 sb_entry;
77145837Smlaier	struct superblock			*sb_profiled_block;
78145837Smlaier	TAILQ_HEAD(skiplist, pf_skip_step)	 sb_skipsteps[PF_SKIP_COUNT];
79145837Smlaier};
80145837SmlaierTAILQ_HEAD(superblocks, superblock);
81145837Smlaier
82145837Smlaier
83145837Smlaier/*
84145837Smlaier * Description of the PF rule structure.
85145837Smlaier */
86145837Smlaierenum {
87145837Smlaier    BARRIER,	/* the presence of the field puts the rule in it's own block */
88145837Smlaier    BREAK,	/* the field may not differ between rules in a superblock */
89145837Smlaier    NOMERGE,	/* the field may not differ between rules when combined */
90145837Smlaier    COMBINED,	/* the field may itself be combined with other rules */
91145837Smlaier    DC,		/* we just don't care about the field */
92145837Smlaier    NEVER};	/* we should never see this field set?!? */
93145837Smlaierstruct pf_rule_field {
94145837Smlaier	const char	*prf_name;
95145837Smlaier	int		 prf_type;
96145837Smlaier	size_t		 prf_offset;
97145837Smlaier	size_t		 prf_size;
98145837Smlaier} pf_rule_desc[] = {
99145837Smlaier#define PF_RULE_FIELD(field, ty)	\
100145837Smlaier    {#field,				\
101145837Smlaier    ty,					\
102145837Smlaier    offsetof(struct pf_rule, field),	\
103145837Smlaier    sizeof(((struct pf_rule *)0)->field)}
104145837Smlaier
105145837Smlaier
106145837Smlaier    /*
107145837Smlaier     * The presence of these fields in a rule put the rule in it's own
108145837Smlaier     * superblock.  Thus it will not be optimized.  It also prevents the
109145837Smlaier     * rule from being re-ordered at all.
110145837Smlaier     */
111145837Smlaier    PF_RULE_FIELD(label,		BARRIER),
112145837Smlaier    PF_RULE_FIELD(prob,			BARRIER),
113145837Smlaier    PF_RULE_FIELD(max_states,		BARRIER),
114145837Smlaier    PF_RULE_FIELD(max_src_nodes,	BARRIER),
115145837Smlaier
116145837Smlaier    /*
117145837Smlaier     * These fields must be the same between all rules in the same superblock.
118145837Smlaier     * These rules are allowed to be re-ordered but only among like rules.
119145837Smlaier     * For instance we can re-order all 'tag "foo"' rules because they have the
120145837Smlaier     * same tag.  But we can not re-order between a 'tag "foo"' and a
121145837Smlaier     * 'tag "bar"' since that would change the meaning of the ruleset.
122145837Smlaier     */
123145837Smlaier    PF_RULE_FIELD(tagname,		BREAK),
124145837Smlaier    PF_RULE_FIELD(keep_state,		BREAK),
125145837Smlaier    PF_RULE_FIELD(qname,		BREAK),
126145837Smlaier    PF_RULE_FIELD(rt,			BREAK),
127145837Smlaier    PF_RULE_FIELD(allow_opts,		BREAK),
128145837Smlaier    PF_RULE_FIELD(rule_flag,		BREAK),
129145837Smlaier    PF_RULE_FIELD(action,		BREAK),
130145837Smlaier
131145837Smlaier    /*
132145837Smlaier     * Any fields not listed in this structure act as BREAK fields
133145837Smlaier     */
134145837Smlaier
135145837Smlaier
136145837Smlaier    /*
137145837Smlaier     * These fields must not differ when we merge two rules together but
138145837Smlaier     * their difference isn't enough to put the rules in different superblocks.
139145837Smlaier     * There are no problems re-ordering any rules with these fields.
140145837Smlaier     */
141145837Smlaier    PF_RULE_FIELD(af,			NOMERGE),
142145837Smlaier    PF_RULE_FIELD(ifnot,		NOMERGE),
143145837Smlaier    PF_RULE_FIELD(ifname,		NOMERGE),
144145837Smlaier    PF_RULE_FIELD(match_tag_not,	NOMERGE),
145145837Smlaier    PF_RULE_FIELD(match_tagname,	NOMERGE),
146145837Smlaier    PF_RULE_FIELD(os_fingerprint,	NOMERGE),
147145837Smlaier    PF_RULE_FIELD(timeout,		NOMERGE),
148145837Smlaier    PF_RULE_FIELD(return_icmp,		NOMERGE),
149145837Smlaier    PF_RULE_FIELD(return_icmp6,		NOMERGE),
150145837Smlaier    PF_RULE_FIELD(uid,			NOMERGE),
151145837Smlaier    PF_RULE_FIELD(gid,			NOMERGE),
152145837Smlaier    PF_RULE_FIELD(direction,		NOMERGE),
153145837Smlaier    PF_RULE_FIELD(proto,		NOMERGE),
154145837Smlaier    PF_RULE_FIELD(type,			NOMERGE),
155145837Smlaier    PF_RULE_FIELD(code,			NOMERGE),
156145837Smlaier    PF_RULE_FIELD(flags,		NOMERGE),
157145837Smlaier    PF_RULE_FIELD(flagset,		NOMERGE),
158145837Smlaier    PF_RULE_FIELD(tos,			NOMERGE),
159145837Smlaier    PF_RULE_FIELD(src.port,		NOMERGE),
160145837Smlaier    PF_RULE_FIELD(dst.port,		NOMERGE),
161145837Smlaier    PF_RULE_FIELD(src.port_op,		NOMERGE),
162145837Smlaier    PF_RULE_FIELD(dst.port_op,		NOMERGE),
163145837Smlaier    PF_RULE_FIELD(src.neg,		NOMERGE),
164145837Smlaier    PF_RULE_FIELD(dst.neg,		NOMERGE),
165145837Smlaier
166145837Smlaier    /* These fields can be merged */
167145837Smlaier    PF_RULE_FIELD(src.addr,		COMBINED),
168145837Smlaier    PF_RULE_FIELD(dst.addr,		COMBINED),
169145837Smlaier
170145837Smlaier    /* We just don't care about these fields.  They're set by the kernel */
171145837Smlaier    PF_RULE_FIELD(skip,			DC),
172145837Smlaier    PF_RULE_FIELD(evaluations,		DC),
173145837Smlaier    PF_RULE_FIELD(packets,		DC),
174145837Smlaier    PF_RULE_FIELD(bytes,		DC),
175145837Smlaier    PF_RULE_FIELD(kif,			DC),
176145837Smlaier    PF_RULE_FIELD(anchor,		DC),
177145837Smlaier    PF_RULE_FIELD(states,		DC),
178145837Smlaier    PF_RULE_FIELD(src_nodes,		DC),
179145837Smlaier    PF_RULE_FIELD(nr,			DC),
180145837Smlaier    PF_RULE_FIELD(entries,		DC),
181145837Smlaier    PF_RULE_FIELD(qid,			DC),
182145837Smlaier    PF_RULE_FIELD(pqid,			DC),
183145837Smlaier    PF_RULE_FIELD(anchor_relative,	DC),
184145837Smlaier    PF_RULE_FIELD(anchor_wildcard,	DC),
185145837Smlaier
186145837Smlaier    /* These fields should never be set in a PASS/BLOCK rule */
187145837Smlaier    PF_RULE_FIELD(natpass,		NEVER),
188145837Smlaier    PF_RULE_FIELD(max_mss,		NEVER),
189145837Smlaier    PF_RULE_FIELD(min_ttl,		NEVER),
190145837Smlaier};
191145837Smlaier
192145837Smlaier
193145837Smlaier
194145837Smlaierint	add_opt_table(struct pfctl *, struct pf_opt_tbl **, sa_family_t,
195145837Smlaier	    struct pf_rule_addr *);
196145837Smlaierint	addrs_combineable(struct pf_rule_addr *, struct pf_rule_addr *);
197145837Smlaierint	addrs_equal(struct pf_rule_addr *, struct pf_rule_addr *);
198145837Smlaierint	block_feedback(struct pfctl *, struct superblock *);
199145837Smlaierint	combine_rules(struct pfctl *, struct superblock *);
200145837Smlaiervoid	comparable_rule(struct pf_rule *, const struct pf_rule *, int);
201145837Smlaierint	construct_superblocks(struct pfctl *, struct pf_opt_queue *,
202145837Smlaier	    struct superblocks *);
203145837Smlaiervoid	exclude_supersets(struct pf_rule *, struct pf_rule *);
204145837Smlaierint	load_feedback_profile(struct pfctl *, struct superblocks *);
205145837Smlaierint	optimize_superblock(struct pfctl *, struct superblock *);
206145837Smlaierint	pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *);
207145837Smlaiervoid	remove_from_skipsteps(struct skiplist *, struct superblock *,
208145837Smlaier	    struct pf_opt_rule *, struct pf_skip_step *);
209145837Smlaierint	remove_identical_rules(struct pfctl *, struct superblock *);
210145837Smlaierint	reorder_rules(struct pfctl *, struct superblock *, int);
211145837Smlaierint	rules_combineable(struct pf_rule *, struct pf_rule *);
212145837Smlaiervoid	skip_append(struct superblock *, int, struct pf_skip_step *,
213145837Smlaier	    struct pf_opt_rule *);
214145837Smlaierint	skip_compare(int, struct pf_skip_step *, struct pf_opt_rule *);
215145837Smlaiervoid	skip_init(void);
216145837Smlaierint	skip_cmp_af(struct pf_rule *, struct pf_rule *);
217145837Smlaierint	skip_cmp_dir(struct pf_rule *, struct pf_rule *);
218145837Smlaierint	skip_cmp_dst_addr(struct pf_rule *, struct pf_rule *);
219145837Smlaierint	skip_cmp_dst_port(struct pf_rule *, struct pf_rule *);
220145837Smlaierint	skip_cmp_ifp(struct pf_rule *, struct pf_rule *);
221145837Smlaierint	skip_cmp_proto(struct pf_rule *, struct pf_rule *);
222145837Smlaierint	skip_cmp_src_addr(struct pf_rule *, struct pf_rule *);
223145837Smlaierint	skip_cmp_src_port(struct pf_rule *, struct pf_rule *);
224145837Smlaierint	superblock_inclusive(struct superblock *, struct pf_opt_rule *);
225145837Smlaiervoid	superblock_free(struct pfctl *, struct superblock *);
226145837Smlaier
227145837Smlaier
228145837Smlaierint (*skip_comparitors[PF_SKIP_COUNT])(struct pf_rule *, struct pf_rule *);
229145837Smlaierconst char *skip_comparitors_names[PF_SKIP_COUNT];
230145837Smlaier#define PF_SKIP_COMPARITORS {				\
231145837Smlaier    { "ifp", PF_SKIP_IFP, skip_cmp_ifp },		\
232145837Smlaier    { "dir", PF_SKIP_DIR, skip_cmp_dir },		\
233145837Smlaier    { "af", PF_SKIP_AF, skip_cmp_af },			\
234145837Smlaier    { "proto", PF_SKIP_PROTO, skip_cmp_proto },		\
235145837Smlaier    { "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr },	\
236145837Smlaier    { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port },	\
237145837Smlaier    { "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr },	\
238145837Smlaier    { "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port }	\
239145837Smlaier}
240145837Smlaier
241145837Smlaierstruct pfr_buffer table_buffer;
242145837Smlaierint table_identifier;
243145837Smlaier
244145837Smlaier
245145837Smlaierint
246145837Smlaierpfctl_optimize_rules(struct pfctl *pf)
247145837Smlaier{
248145837Smlaier	struct superblocks superblocks;
249145837Smlaier	struct superblock *block;
250145837Smlaier	struct pf_opt_rule *por;
251145837Smlaier	int nr;
252145837Smlaier
253145837Smlaier	DEBUG("optimizing ruleset");
254145837Smlaier	memset(&table_buffer, 0, sizeof(table_buffer));
255145837Smlaier	skip_init();
256145837Smlaier
257145837Smlaier	if (TAILQ_FIRST(&pf->opt_queue))
258145837Smlaier		nr = TAILQ_FIRST(&pf->opt_queue)->por_rule.nr;
259145837Smlaier
260145837Smlaier	TAILQ_INIT(&superblocks);
261145837Smlaier	if (construct_superblocks(pf, &pf->opt_queue, &superblocks))
262145837Smlaier		goto error;
263145837Smlaier
264145837Smlaier	if (pf->opts & PF_OPT_OPTIMIZE_PROFILE) {
265145837Smlaier		if (load_feedback_profile(pf, &superblocks))
266145837Smlaier			goto error;
267145837Smlaier	}
268145837Smlaier
269145837Smlaier	TAILQ_FOREACH(block, &superblocks, sb_entry) {
270145837Smlaier		if (optimize_superblock(pf, block))
271145837Smlaier			goto error;
272145837Smlaier	}
273145837Smlaier
274145837Smlaier
275145837Smlaier	/*
276145837Smlaier	 * Optimizations are done so we turn off the optimization flag and
277145837Smlaier	 * put the rules right back into the regular codepath.
278145837Smlaier	 */
279145837Smlaier	pf->opts &= ~PF_OPT_OPTIMIZE;
280145837Smlaier
281145837Smlaier	while ((block = TAILQ_FIRST(&superblocks))) {
282145837Smlaier		TAILQ_REMOVE(&superblocks, block, sb_entry);
283145837Smlaier
284145837Smlaier		while ((por = TAILQ_FIRST(&block->sb_rules))) {
285145837Smlaier			TAILQ_REMOVE(&block->sb_rules, por, por_entry);
286145837Smlaier			por->por_rule.nr = nr++;
287145837Smlaier			if (pfctl_add_rule(pf, &por->por_rule,
288145837Smlaier			    por->por_anchor)) {
289145837Smlaier				free(por);
290145837Smlaier				goto error;
291145837Smlaier			}
292145837Smlaier			free(por);
293145837Smlaier		}
294145837Smlaier		free(block);
295145837Smlaier	}
296145837Smlaier
297145837Smlaier	return (0);
298145837Smlaier
299145837Smlaiererror:
300145837Smlaier	while ((por = TAILQ_FIRST(&pf->opt_queue))) {
301145837Smlaier		TAILQ_REMOVE(&pf->opt_queue, por, por_entry);
302145837Smlaier		if (por->por_src_tbl) {
303145837Smlaier			pfr_buf_clear(por->por_src_tbl->pt_buf);
304145837Smlaier			free(por->por_src_tbl->pt_buf);
305145837Smlaier			free(por->por_src_tbl);
306145837Smlaier		}
307145837Smlaier		if (por->por_dst_tbl) {
308145837Smlaier			pfr_buf_clear(por->por_dst_tbl->pt_buf);
309145837Smlaier			free(por->por_dst_tbl->pt_buf);
310145837Smlaier			free(por->por_dst_tbl);
311145837Smlaier		}
312145837Smlaier		free(por);
313145837Smlaier	}
314145837Smlaier	while ((block = TAILQ_FIRST(&superblocks))) {
315145837Smlaier		TAILQ_REMOVE(&superblocks, block, sb_entry);
316145837Smlaier		superblock_free(pf, block);
317145837Smlaier	}
318145837Smlaier	return (1);
319145837Smlaier}
320145837Smlaier
321145837Smlaier
322145837Smlaier/*
323145837Smlaier * Go ahead and optimize a superblock
324145837Smlaier */
325145837Smlaierint
326145837Smlaieroptimize_superblock(struct pfctl *pf, struct superblock *block)
327145837Smlaier{
328145837Smlaier#ifdef OPT_DEBUG
329145837Smlaier	struct pf_opt_rule *por;
330145837Smlaier#endif /* OPT_DEBUG */
331145837Smlaier
332145837Smlaier	/* We have a few optimization passes:
333145837Smlaier	 *   1) remove duplicate rules or rules that are a subset of other
334145837Smlaier	 *      rules
335145837Smlaier	 *   2) combine otherwise identical rules with different IP addresses
336145837Smlaier	 *      into a single rule and put the addresses in a table.
337145837Smlaier	 *   3) re-order the rules to improve kernel skip steps
338145837Smlaier	 *   4) re-order the 'quick' rules based on feedback from the
339145837Smlaier	 *      active ruleset statistics
340145837Smlaier	 *
341145837Smlaier	 * XXX combine_rules() doesn't combine v4 and v6 rules.  would just
342145837Smlaier	 *     have to keep af in the table container, make af 'COMBINE' and
343145837Smlaier	 *     twiddle the af on the merged rule
344145837Smlaier	 * XXX maybe add a weighting to the metric on skipsteps when doing
345145837Smlaier	 *     reordering.  sometimes two sequential tables will be better
346145837Smlaier	 *     that four consecutive interfaces.
347145837Smlaier	 * XXX need to adjust the skipstep count of everything after PROTO,
348145837Smlaier	 *     since they aren't actually checked on a proto mismatch in
349145837Smlaier	 *     pf_test_{tcp, udp, icmp}()
350145837Smlaier	 * XXX should i treat proto=0, af=0 or dir=0 special in skepstep
351145837Smlaier	 *     calculation since they are a DC?
352145837Smlaier	 * XXX keep last skiplist of last superblock to influence this
353145837Smlaier	 *     superblock.  '5 inet6 log' should make '3 inet6' come before '4
354145837Smlaier	 *     inet' in the next superblock.
355145837Smlaier	 * XXX would be useful to add tables for ports
356145837Smlaier	 * XXX we can also re-order some mutually exclusive superblocks to
357145837Smlaier	 *     try merging superblocks before any of these optimization passes.
358145837Smlaier	 *     for instance a single 'log in' rule in the middle of non-logging
359145837Smlaier	 *     out rules.
360145837Smlaier	 */
361145837Smlaier
362145837Smlaier	/* shortcut.  there will be alot of 1-rule superblocks */
363145837Smlaier	if (!TAILQ_NEXT(TAILQ_FIRST(&block->sb_rules), por_entry))
364145837Smlaier		return (0);
365145837Smlaier
366145837Smlaier#ifdef OPT_DEBUG
367145837Smlaier	printf("--- Superblock ---\n");
368145837Smlaier	TAILQ_FOREACH(por, &block->sb_rules, por_entry) {
369145837Smlaier		printf("  ");
370145837Smlaier		print_rule(&por->por_rule, por->por_anchor, 1);
371145837Smlaier	}
372145837Smlaier#endif /* OPT_DEBUG */
373145837Smlaier
374145837Smlaier
375145837Smlaier	if (remove_identical_rules(pf, block))
376145837Smlaier		return (1);
377145837Smlaier	if (combine_rules(pf, block))
378145837Smlaier		return (1);
379145837Smlaier	if ((pf->opts & PF_OPT_OPTIMIZE_PROFILE) &&
380145837Smlaier	    TAILQ_FIRST(&block->sb_rules)->por_rule.quick &&
381145837Smlaier	    block->sb_profiled_block) {
382145837Smlaier		if (block_feedback(pf, block))
383145837Smlaier			return (1);
384145837Smlaier	} else if (reorder_rules(pf, block, 0)) {
385145837Smlaier		return (1);
386145837Smlaier	}
387145837Smlaier
388145837Smlaier	/*
389145837Smlaier	 * Don't add any optimization passes below reorder_rules().  It will
390145837Smlaier	 * have divided superblocks into smaller blocks for further refinement
391145837Smlaier	 * and doesn't put them back together again.  What once was a true
392145837Smlaier	 * superblock might have been split into multiple superblocks.
393145837Smlaier	 */
394145837Smlaier
395145837Smlaier#ifdef OPT_DEBUG
396145837Smlaier	printf("--- END Superblock ---\n");
397145837Smlaier#endif /* OPT_DEBUG */
398145837Smlaier	return (0);
399145837Smlaier}
400145837Smlaier
401145837Smlaier
402145837Smlaier/*
403145837Smlaier * Optimization pass #1: remove identical rules
404145837Smlaier */
405145837Smlaierint
406145837Smlaierremove_identical_rules(struct pfctl *pf, struct superblock *block)
407145837Smlaier{
408145837Smlaier	struct pf_opt_rule *por1, *por2, *por_next, *por2_next;
409145837Smlaier	struct pf_rule a, a2, b, b2;
410145837Smlaier
411145837Smlaier	for (por1 = TAILQ_FIRST(&block->sb_rules); por1; por1 = por_next) {
412145837Smlaier		por_next = TAILQ_NEXT(por1, por_entry);
413145837Smlaier		for (por2 = por_next; por2; por2 = por2_next) {
414145837Smlaier			por2_next = TAILQ_NEXT(por2, por_entry);
415145837Smlaier			comparable_rule(&a, &por1->por_rule, DC);
416145837Smlaier			comparable_rule(&b, &por2->por_rule, DC);
417145837Smlaier			memcpy(&a2, &a, sizeof(a2));
418145837Smlaier			memcpy(&b2, &b, sizeof(b2));
419145837Smlaier
420145837Smlaier			exclude_supersets(&a, &b);
421145837Smlaier			exclude_supersets(&b2, &a2);
422145837Smlaier			if (memcmp(&a, &b, sizeof(a)) == 0) {
423145837Smlaier				DEBUG("removing identical rule  nr%d = *nr%d*",
424145837Smlaier				    por1->por_rule.nr, por2->por_rule.nr);
425145837Smlaier				TAILQ_REMOVE(&block->sb_rules, por2, por_entry);
426145837Smlaier				if (por_next == por2)
427145837Smlaier					por_next = TAILQ_NEXT(por1, por_entry);
428145837Smlaier				free(por2);
429145837Smlaier			} else if (memcmp(&a2, &b2, sizeof(a2)) == 0) {
430145837Smlaier				DEBUG("removing identical rule  *nr%d* = nr%d",
431145837Smlaier				    por1->por_rule.nr, por2->por_rule.nr);
432145837Smlaier				TAILQ_REMOVE(&block->sb_rules, por1, por_entry);
433145837Smlaier				free(por1);
434145837Smlaier				break;
435145837Smlaier			}
436145837Smlaier		}
437145837Smlaier	}
438145837Smlaier
439145837Smlaier	return (0);
440145837Smlaier}
441145837Smlaier
442145837Smlaier
443145837Smlaier/*
444145837Smlaier * Optimization pass #2: combine similar rules with different addresses
445145837Smlaier * into a single rule and a table
446145837Smlaier */
447145837Smlaierint
448145837Smlaiercombine_rules(struct pfctl *pf, struct superblock *block)
449145837Smlaier{
450145837Smlaier	struct pf_opt_rule *p1, *p2, *por_next;
451145837Smlaier	int src_eq, dst_eq;
452145837Smlaier
453145837Smlaier	if ((pf->loadopt & PFCTL_FLAG_TABLE) == 0) {
454145837Smlaier		warnx("Must enable table loading for optimizations");
455145837Smlaier		return (1);
456145837Smlaier	}
457145837Smlaier
458145837Smlaier	/* First we make a pass to combine the rules.  O(n log n) */
459145837Smlaier	TAILQ_FOREACH(p1, &block->sb_rules, por_entry) {
460145837Smlaier		for (p2 = TAILQ_NEXT(p1, por_entry); p2; p2 = por_next) {
461145837Smlaier			por_next = TAILQ_NEXT(p2, por_entry);
462145837Smlaier
463145837Smlaier			src_eq = addrs_equal(&p1->por_rule.src,
464145837Smlaier			    &p2->por_rule.src);
465145837Smlaier			dst_eq = addrs_equal(&p1->por_rule.dst,
466145837Smlaier			    &p2->por_rule.dst);
467145837Smlaier
468145837Smlaier			if (src_eq && !dst_eq && p1->por_src_tbl == NULL &&
469145837Smlaier			    p2->por_dst_tbl == NULL &&
470145837Smlaier			    p2->por_src_tbl == NULL &&
471145837Smlaier			    rules_combineable(&p1->por_rule, &p2->por_rule) &&
472145837Smlaier			    addrs_combineable(&p1->por_rule.dst,
473145837Smlaier			    &p2->por_rule.dst)) {
474145837Smlaier				DEBUG("can combine rules  nr%d = nr%d",
475145837Smlaier				    p1->por_rule.nr, p2->por_rule.nr);
476145837Smlaier				if (p1->por_dst_tbl == NULL &&
477145837Smlaier				    add_opt_table(pf, &p1->por_dst_tbl,
478145837Smlaier				    p1->por_rule.af, &p1->por_rule.dst))
479145837Smlaier					return (1);
480145837Smlaier				if (add_opt_table(pf, &p1->por_dst_tbl,
481145837Smlaier				    p1->por_rule.af, &p2->por_rule.dst))
482145837Smlaier					return (1);
483145837Smlaier				p2->por_dst_tbl = p1->por_dst_tbl;
484145837Smlaier				if (p1->por_dst_tbl->pt_rulecount >=
485145837Smlaier				    TABLE_THRESHOLD) {
486145837Smlaier					TAILQ_REMOVE(&block->sb_rules, p2,
487145837Smlaier					    por_entry);
488145837Smlaier					free(p2);
489145837Smlaier				}
490145837Smlaier			} else if (!src_eq && dst_eq && p1->por_dst_tbl == NULL
491145837Smlaier			    && p2->por_src_tbl == NULL &&
492145837Smlaier			    p2->por_dst_tbl == NULL &&
493145837Smlaier			    rules_combineable(&p1->por_rule, &p2->por_rule) &&
494145837Smlaier			    addrs_combineable(&p1->por_rule.src,
495145837Smlaier			    &p2->por_rule.src)) {
496145837Smlaier				DEBUG("can combine rules  nr%d = nr%d",
497145837Smlaier				    p1->por_rule.nr, p2->por_rule.nr);
498145837Smlaier				if (p1->por_src_tbl == NULL &&
499145837Smlaier				    add_opt_table(pf, &p1->por_src_tbl,
500145837Smlaier				    p1->por_rule.af, &p1->por_rule.src))
501145837Smlaier					return (1);
502145837Smlaier				if (add_opt_table(pf, &p1->por_src_tbl,
503145837Smlaier				    p1->por_rule.af, &p2->por_rule.src))
504145837Smlaier					return (1);
505145837Smlaier				p2->por_src_tbl = p1->por_src_tbl;
506145837Smlaier				if (p1->por_src_tbl->pt_rulecount >=
507145837Smlaier				    TABLE_THRESHOLD) {
508145837Smlaier					TAILQ_REMOVE(&block->sb_rules, p2,
509145837Smlaier					    por_entry);
510145837Smlaier					free(p2);
511145837Smlaier				}
512145837Smlaier			}
513145837Smlaier		}
514145837Smlaier	}
515145837Smlaier
516145837Smlaier
517145837Smlaier	/*
518145837Smlaier	 * Then we make a final pass to create a valid table name and
519145837Smlaier	 * insert the name into the rules.
520145837Smlaier	 */
521145837Smlaier	for (p1 = TAILQ_FIRST(&block->sb_rules); p1; p1 = por_next) {
522145837Smlaier		por_next = TAILQ_NEXT(p1, por_entry);
523145837Smlaier		assert(p1->por_src_tbl == NULL || p1->por_dst_tbl == NULL);
524145837Smlaier
525145837Smlaier		if (p1->por_src_tbl && p1->por_src_tbl->pt_rulecount >=
526145837Smlaier		    TABLE_THRESHOLD) {
527145837Smlaier			if (p1->por_src_tbl->pt_generated) {
528145837Smlaier				/* This rule is included in a table */
529145837Smlaier				TAILQ_REMOVE(&block->sb_rules, p1, por_entry);
530145837Smlaier				free(p1);
531145837Smlaier				continue;
532145837Smlaier			}
533145837Smlaier			p1->por_src_tbl->pt_generated = 1;
534145837Smlaier
535145837Smlaier			if ((pf->opts & PF_OPT_NOACTION) == 0 &&
536145837Smlaier			    pf_opt_create_table(pf, p1->por_src_tbl))
537145837Smlaier				return (1);
538145837Smlaier
539145837Smlaier			pf->tdirty = 1;
540145837Smlaier
541145837Smlaier			if (pf->opts & PF_OPT_VERBOSE)
542145837Smlaier				print_tabledef(p1->por_src_tbl->pt_name,
543145837Smlaier				    PFR_TFLAG_CONST, 1,
544145837Smlaier				    &p1->por_src_tbl->pt_nodes);
545145837Smlaier
546145837Smlaier			memset(&p1->por_rule.src.addr, 0,
547145837Smlaier			    sizeof(p1->por_rule.src.addr));
548145837Smlaier			p1->por_rule.src.addr.type = PF_ADDR_TABLE;
549145837Smlaier			strlcpy(p1->por_rule.src.addr.v.tblname,
550145837Smlaier			    p1->por_src_tbl->pt_name,
551145837Smlaier			    sizeof(p1->por_rule.src.addr.v.tblname));
552145837Smlaier
553145837Smlaier			pfr_buf_clear(p1->por_src_tbl->pt_buf);
554145837Smlaier			free(p1->por_src_tbl->pt_buf);
555145837Smlaier			p1->por_src_tbl->pt_buf = NULL;
556145837Smlaier		}
557145837Smlaier		if (p1->por_dst_tbl && p1->por_dst_tbl->pt_rulecount >=
558145837Smlaier		    TABLE_THRESHOLD) {
559145837Smlaier			if (p1->por_dst_tbl->pt_generated) {
560145837Smlaier				/* This rule is included in a table */
561145837Smlaier				TAILQ_REMOVE(&block->sb_rules, p1, por_entry);
562145837Smlaier				free(p1);
563145837Smlaier				continue;
564145837Smlaier			}
565145837Smlaier			p1->por_dst_tbl->pt_generated = 1;
566145837Smlaier
567145837Smlaier			if ((pf->opts & PF_OPT_NOACTION) == 0 &&
568145837Smlaier			    pf_opt_create_table(pf, p1->por_dst_tbl))
569145837Smlaier				return (1);
570145837Smlaier			pf->tdirty = 1;
571145837Smlaier
572145837Smlaier			if (pf->opts & PF_OPT_VERBOSE)
573145837Smlaier				print_tabledef(p1->por_dst_tbl->pt_name,
574145837Smlaier				    PFR_TFLAG_CONST, 1,
575145837Smlaier				    &p1->por_dst_tbl->pt_nodes);
576145837Smlaier
577145837Smlaier			memset(&p1->por_rule.dst.addr, 0,
578145837Smlaier			    sizeof(p1->por_rule.dst.addr));
579145837Smlaier			p1->por_rule.dst.addr.type = PF_ADDR_TABLE;
580145837Smlaier			strlcpy(p1->por_rule.dst.addr.v.tblname,
581145837Smlaier			    p1->por_dst_tbl->pt_name,
582145837Smlaier			    sizeof(p1->por_rule.dst.addr.v.tblname));
583145837Smlaier
584145837Smlaier			pfr_buf_clear(p1->por_dst_tbl->pt_buf);
585145837Smlaier			free(p1->por_dst_tbl->pt_buf);
586145837Smlaier			p1->por_dst_tbl->pt_buf = NULL;
587145837Smlaier		}
588145837Smlaier	}
589145837Smlaier
590145837Smlaier	return (0);
591145837Smlaier}
592145837Smlaier
593145837Smlaier
594145837Smlaier/*
595145837Smlaier * Optimization pass #3: re-order rules to improve skip steps
596145837Smlaier */
597145837Smlaierint
598145837Smlaierreorder_rules(struct pfctl *pf, struct superblock *block, int depth)
599145837Smlaier{
600145837Smlaier	struct superblock *newblock;
601145837Smlaier	struct pf_skip_step *skiplist;
602145837Smlaier	struct pf_opt_rule *por;
603145837Smlaier	int i, largest, largest_list, rule_count = 0;
604145837Smlaier	TAILQ_HEAD( , pf_opt_rule) head;
605145837Smlaier
606145837Smlaier	/*
607145837Smlaier	 * Calculate the best-case skip steps.  We put each rule in a list
608145837Smlaier	 * of other rules with common fields
609145837Smlaier	 */
610145837Smlaier	for (i = 0; i < PF_SKIP_COUNT; i++) {
611145837Smlaier		TAILQ_FOREACH(por, &block->sb_rules, por_entry) {
612145837Smlaier			TAILQ_FOREACH(skiplist, &block->sb_skipsteps[i],
613145837Smlaier			    ps_entry) {
614145837Smlaier				if (skip_compare(i, skiplist, por) == 0)
615145837Smlaier					break;
616145837Smlaier			}
617145837Smlaier			if (skiplist == NULL) {
618145837Smlaier				if ((skiplist = calloc(1, sizeof(*skiplist))) ==
619145837Smlaier				    NULL)
620145837Smlaier					err(1, "calloc");
621145837Smlaier				TAILQ_INIT(&skiplist->ps_rules);
622145837Smlaier				TAILQ_INSERT_TAIL(&block->sb_skipsteps[i],
623145837Smlaier				    skiplist, ps_entry);
624145837Smlaier			}
625145837Smlaier			skip_append(block, i, skiplist, por);
626145837Smlaier		}
627145837Smlaier	}
628145837Smlaier
629145837Smlaier	TAILQ_FOREACH(por, &block->sb_rules, por_entry)
630145837Smlaier		rule_count++;
631145837Smlaier
632145837Smlaier	/*
633145837Smlaier	 * Now we're going to ignore any fields that are identical between
634145837Smlaier	 * all of the rules in the superblock and those fields which differ
635145837Smlaier	 * between every rule in the superblock.
636145837Smlaier	 */
637145837Smlaier	largest = 0;
638145837Smlaier	for (i = 0; i < PF_SKIP_COUNT; i++) {
639145837Smlaier		skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]);
640145837Smlaier		if (skiplist->ps_count == rule_count) {
641145837Smlaier			DEBUG("(%d) original skipstep '%s' is all rules",
642145837Smlaier			    depth, skip_comparitors_names[i]);
643145837Smlaier			skiplist->ps_count = 0;
644145837Smlaier		} else if (skiplist->ps_count == 1) {
645145837Smlaier			skiplist->ps_count = 0;
646145837Smlaier		} else {
647145837Smlaier			DEBUG("(%d) original skipstep '%s' largest jump is %d",
648145837Smlaier			    depth, skip_comparitors_names[i],
649145837Smlaier			    skiplist->ps_count);
650145837Smlaier			if (skiplist->ps_count > largest)
651145837Smlaier				largest = skiplist->ps_count;
652145837Smlaier		}
653145837Smlaier	}
654145837Smlaier	if (largest == 0) {
655145837Smlaier		/* Ugh.  There is NO commonality in the superblock on which
656145837Smlaier		 * optimize the skipsteps optimization.
657145837Smlaier		 */
658145837Smlaier		goto done;
659145837Smlaier	}
660145837Smlaier
661145837Smlaier	/*
662145837Smlaier	 * Now we're going to empty the superblock rule list and re-create
663145837Smlaier	 * it based on a more optimal skipstep order.
664145837Smlaier	 */
665145837Smlaier	TAILQ_INIT(&head);
666145837Smlaier	while ((por = TAILQ_FIRST(&block->sb_rules))) {
667145837Smlaier		TAILQ_REMOVE(&block->sb_rules, por, por_entry);
668145837Smlaier		TAILQ_INSERT_TAIL(&head, por, por_entry);
669145837Smlaier	}
670145837Smlaier
671145837Smlaier
672145837Smlaier	while (!TAILQ_EMPTY(&head)) {
673145837Smlaier		largest = 1;
674145837Smlaier
675145837Smlaier		/*
676145837Smlaier		 * Find the most useful skip steps remaining
677145837Smlaier		 */
678145837Smlaier		for (i = 0; i < PF_SKIP_COUNT; i++) {
679145837Smlaier			skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]);
680145837Smlaier			if (skiplist->ps_count > largest) {
681145837Smlaier				largest = skiplist->ps_count;
682145837Smlaier				largest_list = i;
683145837Smlaier			}
684145837Smlaier		}
685145837Smlaier
686145837Smlaier		if (largest <= 1) {
687145837Smlaier			/*
688145837Smlaier			 * Nothing useful left.  Leave remaining rules in order.
689145837Smlaier			 */
690145837Smlaier			DEBUG("(%d) no more commonality for skip steps", depth);
691145837Smlaier			while ((por = TAILQ_FIRST(&head))) {
692145837Smlaier				TAILQ_REMOVE(&head, por, por_entry);
693145837Smlaier				TAILQ_INSERT_TAIL(&block->sb_rules, por,
694145837Smlaier				    por_entry);
695145837Smlaier			}
696145837Smlaier		} else {
697145837Smlaier			/*
698145837Smlaier			 * There is commonality.  Extract those common rules
699145837Smlaier			 * and place them in the ruleset adjacent to each
700145837Smlaier			 * other.
701145837Smlaier			 */
702145837Smlaier			skiplist = TAILQ_FIRST(&block->sb_skipsteps[
703145837Smlaier			    largest_list]);
704145837Smlaier			DEBUG("(%d) skipstep '%s' largest jump is %d @ #%d",
705145837Smlaier			    depth, skip_comparitors_names[largest_list],
706145837Smlaier			    largest, TAILQ_FIRST(&TAILQ_FIRST(&block->
707145837Smlaier			    sb_skipsteps [largest_list])->ps_rules)->
708145837Smlaier			    por_rule.nr);
709145837Smlaier			TAILQ_REMOVE(&block->sb_skipsteps[largest_list],
710145837Smlaier			    skiplist, ps_entry);
711145837Smlaier
712145837Smlaier
713145837Smlaier			/*
714145837Smlaier			 * There may be further commonality inside these
715145837Smlaier			 * rules.  So we'll split them off into they're own
716145837Smlaier			 * superblock and pass it back into the optimizer.
717145837Smlaier			 */
718145837Smlaier			if (skiplist->ps_count > 2) {
719145837Smlaier				if ((newblock = calloc(1, sizeof(*newblock)))
720145837Smlaier				    == NULL) {
721145837Smlaier					warn("calloc");
722145837Smlaier					return (1);
723145837Smlaier				}
724145837Smlaier				TAILQ_INIT(&newblock->sb_rules);
725145837Smlaier				for (i = 0; i < PF_SKIP_COUNT; i++)
726145837Smlaier					TAILQ_INIT(&newblock->sb_skipsteps[i]);
727145837Smlaier				TAILQ_INSERT_BEFORE(block, newblock, sb_entry);
728145837Smlaier				DEBUG("(%d) splitting off %d rules from superblock @ #%d",
729145837Smlaier				    depth, skiplist->ps_count,
730145837Smlaier				    TAILQ_FIRST(&skiplist->ps_rules)->
731145837Smlaier				    por_rule.nr);
732145837Smlaier			} else {
733145837Smlaier				newblock = block;
734145837Smlaier			}
735145837Smlaier
736145837Smlaier			while ((por = TAILQ_FIRST(&skiplist->ps_rules))) {
737145837Smlaier				TAILQ_REMOVE(&head, por, por_entry);
738145837Smlaier				TAILQ_REMOVE(&skiplist->ps_rules, por,
739145837Smlaier				    por_skip_entry[largest_list]);
740145837Smlaier				TAILQ_INSERT_TAIL(&newblock->sb_rules, por,
741145837Smlaier				    por_entry);
742145837Smlaier
743145837Smlaier				/* Remove this rule from all other skiplists */
744145837Smlaier				remove_from_skipsteps(&block->sb_skipsteps[
745145837Smlaier				    largest_list], block, por, skiplist);
746145837Smlaier			}
747145837Smlaier			free(skiplist);
748145837Smlaier			if (newblock != block)
749145837Smlaier				if (reorder_rules(pf, newblock, depth + 1))
750145837Smlaier					return (1);
751145837Smlaier		}
752145837Smlaier	}
753145837Smlaier
754145837Smlaierdone:
755145837Smlaier	for (i = 0; i < PF_SKIP_COUNT; i++) {
756145837Smlaier		while ((skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]))) {
757145837Smlaier			TAILQ_REMOVE(&block->sb_skipsteps[i], skiplist,
758145837Smlaier			    ps_entry);
759145837Smlaier			free(skiplist);
760145837Smlaier		}
761145837Smlaier	}
762145837Smlaier
763145837Smlaier	return (0);
764145837Smlaier}
765145837Smlaier
766145837Smlaier
767145837Smlaier/*
768145837Smlaier * Optimization pass #4: re-order 'quick' rules based on feedback from the
769145837Smlaier * currently running ruleset
770145837Smlaier */
771145837Smlaierint
772145837Smlaierblock_feedback(struct pfctl *pf, struct superblock *block)
773145837Smlaier{
774145837Smlaier	TAILQ_HEAD( , pf_opt_rule) queue;
775145837Smlaier	struct pf_opt_rule *por1, *por2;
776145837Smlaier	u_int64_t total_count = 0;
777145837Smlaier	struct pf_rule a, b;
778145837Smlaier
779145837Smlaier
780145837Smlaier	/*
781145837Smlaier	 * Walk through all of the profiled superblock's rules and copy
782145837Smlaier	 * the counters onto our rules.
783145837Smlaier	 */
784145837Smlaier	TAILQ_FOREACH(por1, &block->sb_profiled_block->sb_rules, por_entry) {
785145837Smlaier		comparable_rule(&a, &por1->por_rule, DC);
786145837Smlaier		total_count += por1->por_rule.packets;
787145837Smlaier		TAILQ_FOREACH(por2, &block->sb_rules, por_entry) {
788145837Smlaier			if (por2->por_profile_count)
789145837Smlaier				continue;
790145837Smlaier			comparable_rule(&b, &por2->por_rule, DC);
791145837Smlaier			if (memcmp(&a, &b, sizeof(a)) == 0) {
792145837Smlaier				por2->por_profile_count =
793145837Smlaier				    por1->por_rule.packets;
794145837Smlaier				break;
795145837Smlaier			}
796145837Smlaier		}
797145837Smlaier	}
798145837Smlaier	superblock_free(pf, block->sb_profiled_block);
799145837Smlaier	block->sb_profiled_block = NULL;
800145837Smlaier
801145837Smlaier	/*
802145837Smlaier	 * Now we pull all of the rules off the superblock and re-insert them
803145837Smlaier	 * in sorted order.
804145837Smlaier	 */
805145837Smlaier
806145837Smlaier	TAILQ_INIT(&queue);
807145837Smlaier	while ((por1 = TAILQ_FIRST(&block->sb_rules)) != NULL) {
808145837Smlaier		TAILQ_REMOVE(&block->sb_rules, por1, por_entry);
809145837Smlaier		TAILQ_INSERT_TAIL(&queue, por1, por_entry);
810145837Smlaier	}
811145837Smlaier
812145837Smlaier	while ((por1 = TAILQ_FIRST(&queue)) != NULL) {
813145837Smlaier		TAILQ_REMOVE(&queue, por1, por_entry);
814145837Smlaier/* XXX I should sort all of the unused rules based on skip steps */
815145837Smlaier		TAILQ_FOREACH(por2, &block->sb_rules, por_entry) {
816145837Smlaier			if (por1->por_profile_count > por2->por_profile_count) {
817145837Smlaier				TAILQ_INSERT_BEFORE(por2, por1, por_entry);
818145837Smlaier				break;
819145837Smlaier			}
820145837Smlaier		}
821145840Smlaier#ifdef __FreeBSD__
822145840Smlaier		if (por2 == NULL)
823145840Smlaier#else
824145837Smlaier		if (por2 == TAILQ_END(&block->sb_rules))
825145840Smlaier#endif
826145837Smlaier			TAILQ_INSERT_TAIL(&block->sb_rules, por1, por_entry);
827145837Smlaier	}
828145837Smlaier
829145837Smlaier	return (0);
830145837Smlaier}
831145837Smlaier
832145837Smlaier
833145837Smlaier/*
834145837Smlaier * Load the current ruleset from the kernel and try to associate them with
835145837Smlaier * the ruleset we're optimizing.
836145837Smlaier */
837145837Smlaierint
838145837Smlaierload_feedback_profile(struct pfctl *pf, struct superblocks *superblocks)
839145837Smlaier{
840145837Smlaier	struct superblock *block, *blockcur;
841145837Smlaier	struct superblocks prof_superblocks;
842145837Smlaier	struct pf_opt_rule *por;
843145837Smlaier	struct pf_opt_queue queue;
844145837Smlaier	struct pfioc_rule pr;
845145837Smlaier	struct pf_rule a, b;
846145837Smlaier	int nr, mnr;
847145837Smlaier
848145837Smlaier	TAILQ_INIT(&queue);
849145837Smlaier	TAILQ_INIT(&prof_superblocks);
850145837Smlaier
851145837Smlaier	memset(&pr, 0, sizeof(pr));
852145837Smlaier	pr.rule.action = PF_PASS;
853145837Smlaier	if (ioctl(pf->dev, DIOCGETRULES, &pr)) {
854145837Smlaier		warn("DIOCGETRULES");
855145837Smlaier		return (1);
856145837Smlaier	}
857145837Smlaier	mnr = pr.nr;
858145837Smlaier
859145837Smlaier	DEBUG("Loading %d active rules for a feedback profile", mnr);
860145837Smlaier	for (nr = 0; nr < mnr; ++nr) {
861145837Smlaier		if ((por = calloc(1, sizeof(*por))) == NULL) {
862145837Smlaier			warn("calloc");
863145837Smlaier			return (1);
864145837Smlaier		}
865145837Smlaier		pr.nr = nr;
866145837Smlaier		if (ioctl(pf->dev, DIOCGETRULE, &pr)) {
867145837Smlaier			warn("DIOCGETRULES");
868145837Smlaier			return (1);
869145837Smlaier		}
870145837Smlaier		memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule));
871145837Smlaier		strlcpy(por->por_anchor, pr.anchor_call,
872145837Smlaier		    sizeof(por->por_anchor));
873145837Smlaier		if (TAILQ_EMPTY(&por->por_rule.rpool.list))
874145837Smlaier			memset(&por->por_rule.rpool, 0,
875145837Smlaier			    sizeof(por->por_rule.rpool));
876145837Smlaier		TAILQ_INSERT_TAIL(&queue, por, por_entry);
877145837Smlaier
878145837Smlaier		/* XXX pfctl_get_pool(pf->dev, &pr.rule.rpool, nr, pr.ticket,
879145837Smlaier		 *         PF_PASS, pf->anchor) ???
880145837Smlaier		 * ... pfctl_clear_pool(&pr.rule.rpool)
881145837Smlaier		 */
882145837Smlaier	}
883145837Smlaier
884145837Smlaier	if (construct_superblocks(pf, &queue, &prof_superblocks))
885145837Smlaier		return (1);
886145837Smlaier
887145837Smlaier
888145837Smlaier	/*
889145837Smlaier	 * Now we try to associate the active ruleset's superblocks with
890145837Smlaier	 * the superblocks we're compiling.
891145837Smlaier	 */
892145837Smlaier	block = TAILQ_FIRST(superblocks);
893145837Smlaier	blockcur = TAILQ_FIRST(&prof_superblocks);
894145837Smlaier	while (block && blockcur) {
895145837Smlaier		comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule,
896145837Smlaier		    BREAK);
897145837Smlaier		comparable_rule(&b, &TAILQ_FIRST(&blockcur->sb_rules)->por_rule,
898145837Smlaier		    BREAK);
899145837Smlaier		if (memcmp(&a, &b, sizeof(a)) == 0) {
900145837Smlaier			/* The two superblocks lined up */
901145837Smlaier			block->sb_profiled_block = blockcur;
902145837Smlaier		} else {
903145837Smlaier			DEBUG("superblocks don't line up between #%d and #%d",
904145837Smlaier			    TAILQ_FIRST(&block->sb_rules)->por_rule.nr,
905145837Smlaier			    TAILQ_FIRST(&blockcur->sb_rules)->por_rule.nr);
906145837Smlaier			break;
907145837Smlaier		}
908145837Smlaier		block = TAILQ_NEXT(block, sb_entry);
909145837Smlaier		blockcur = TAILQ_NEXT(blockcur, sb_entry);
910145837Smlaier	}
911145837Smlaier
912145837Smlaier
913145837Smlaier
914145837Smlaier	/* Free any superblocks we couldn't link */
915145837Smlaier	while (blockcur) {
916145837Smlaier		block = TAILQ_NEXT(blockcur, sb_entry);
917145837Smlaier		superblock_free(pf, blockcur);
918145837Smlaier		blockcur = block;
919145837Smlaier	}
920145837Smlaier	return (0);
921145837Smlaier}
922145837Smlaier
923145837Smlaier
924145837Smlaier/*
925145837Smlaier * Compare a rule to a skiplist to see if the rule is a member
926145837Smlaier */
927145837Smlaierint
928145837Smlaierskip_compare(int skipnum, struct pf_skip_step *skiplist,
929145837Smlaier    struct pf_opt_rule *por)
930145837Smlaier{
931145837Smlaier	struct pf_rule *a, *b;
932145837Smlaier	if (skipnum >= PF_SKIP_COUNT || skipnum < 0)
933145837Smlaier		errx(1, "skip_compare() out of bounds");
934145837Smlaier	a = &por->por_rule;
935145837Smlaier	b = &TAILQ_FIRST(&skiplist->ps_rules)->por_rule;
936145837Smlaier
937145837Smlaier	return ((skip_comparitors[skipnum])(a, b));
938145837Smlaier}
939145837Smlaier
940145837Smlaier
941145837Smlaier/*
942145837Smlaier * Add a rule to a skiplist
943145837Smlaier */
944145837Smlaiervoid
945145837Smlaierskip_append(struct superblock *superblock, int skipnum,
946145837Smlaier    struct pf_skip_step *skiplist, struct pf_opt_rule *por)
947145837Smlaier{
948145837Smlaier	struct pf_skip_step *prev;
949145837Smlaier
950145837Smlaier	skiplist->ps_count++;
951145837Smlaier	TAILQ_INSERT_TAIL(&skiplist->ps_rules, por, por_skip_entry[skipnum]);
952145837Smlaier
953145837Smlaier	/* Keep the list of skiplists sorted by whichever is larger */
954145837Smlaier	while ((prev = TAILQ_PREV(skiplist, skiplist, ps_entry)) &&
955145837Smlaier	    prev->ps_count < skiplist->ps_count) {
956145837Smlaier		TAILQ_REMOVE(&superblock->sb_skipsteps[skipnum],
957145837Smlaier		    skiplist, ps_entry);
958145837Smlaier		TAILQ_INSERT_BEFORE(prev, skiplist, ps_entry);
959145837Smlaier	}
960145837Smlaier}
961145837Smlaier
962145837Smlaier
963145837Smlaier/*
964145837Smlaier * Remove a rule from the other skiplist calculations.
965145837Smlaier */
966145837Smlaiervoid
967145837Smlaierremove_from_skipsteps(struct skiplist *head, struct superblock *block,
968145837Smlaier    struct pf_opt_rule *por, struct pf_skip_step *active_list)
969145837Smlaier{
970145837Smlaier	struct pf_skip_step *sk, *next;
971145837Smlaier	struct pf_opt_rule *p2;
972145837Smlaier	int i, found;
973145837Smlaier
974145837Smlaier	for (i = 0; i < PF_SKIP_COUNT; i++) {
975145837Smlaier		sk = TAILQ_FIRST(&block->sb_skipsteps[i]);
976145837Smlaier		if (sk == NULL || sk == active_list || sk->ps_count <= 1)
977145837Smlaier			continue;
978145837Smlaier		found = 0;
979145837Smlaier		do {
980145837Smlaier			TAILQ_FOREACH(p2, &sk->ps_rules, por_skip_entry[i])
981145837Smlaier				if (p2 == por) {
982145837Smlaier					TAILQ_REMOVE(&sk->ps_rules, p2,
983145837Smlaier					    por_skip_entry[i]);
984145837Smlaier					found = 1;
985145837Smlaier					sk->ps_count--;
986145837Smlaier					break;
987145837Smlaier				}
988145837Smlaier		} while (!found && (sk = TAILQ_NEXT(sk, ps_entry)));
989145837Smlaier		if (found && sk) {
990145837Smlaier			/* Does this change the sorting order? */
991145837Smlaier			while ((next = TAILQ_NEXT(sk, ps_entry)) &&
992145837Smlaier			    next->ps_count > sk->ps_count) {
993145837Smlaier				TAILQ_REMOVE(head, sk, ps_entry);
994145837Smlaier				TAILQ_INSERT_AFTER(head, next, sk, ps_entry);
995145837Smlaier			}
996145837Smlaier#ifdef OPT_DEBUG
997145837Smlaier			next = TAILQ_NEXT(sk, ps_entry);
998145837Smlaier			assert(next == NULL || next->ps_count <= sk->ps_count);
999145837Smlaier#endif /* OPT_DEBUG */
1000145837Smlaier		}
1001145837Smlaier	}
1002145837Smlaier}
1003145837Smlaier
1004145837Smlaier
1005145837Smlaier/* Compare two rules AF field for skiplist construction */
1006145837Smlaierint
1007145837Smlaierskip_cmp_af(struct pf_rule *a, struct pf_rule *b)
1008145837Smlaier{
1009145837Smlaier	if (a->af != b->af || a->af == 0)
1010145837Smlaier		return (1);
1011145837Smlaier	return (0);
1012145837Smlaier}
1013145837Smlaier
1014145837Smlaier/* Compare two rules DIRECTION field for skiplist construction */
1015145837Smlaierint
1016145837Smlaierskip_cmp_dir(struct pf_rule *a, struct pf_rule *b)
1017145837Smlaier{
1018145837Smlaier	if (a->direction == 0 || a->direction != b->direction)
1019145837Smlaier		return (1);
1020145837Smlaier	return (0);
1021145837Smlaier}
1022145837Smlaier
1023145837Smlaier/* Compare two rules DST Address field for skiplist construction */
1024145837Smlaierint
1025145837Smlaierskip_cmp_dst_addr(struct pf_rule *a, struct pf_rule *b)
1026145837Smlaier{
1027145837Smlaier	if (a->dst.neg != b->dst.neg ||
1028145837Smlaier	    a->dst.addr.type != b->dst.addr.type)
1029145837Smlaier		return (1);
1030145837Smlaier	/* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
1031145837Smlaier	 *    && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
1032145837Smlaier	 *    a->proto == IPPROTO_ICMP
1033145837Smlaier	 *	return (1);
1034145837Smlaier	 */
1035145837Smlaier	switch (a->dst.addr.type) {
1036145837Smlaier	case PF_ADDR_ADDRMASK:
1037145837Smlaier		if (memcmp(&a->dst.addr.v.a.addr, &b->dst.addr.v.a.addr,
1038145837Smlaier		    sizeof(a->dst.addr.v.a.addr)) ||
1039145837Smlaier		    memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask,
1040145837Smlaier		    sizeof(a->dst.addr.v.a.mask)) ||
1041145837Smlaier		    (a->dst.addr.v.a.addr.addr32[0] == 0 &&
1042145837Smlaier		    a->dst.addr.v.a.addr.addr32[1] == 0 &&
1043145837Smlaier		    a->dst.addr.v.a.addr.addr32[2] == 0 &&
1044145837Smlaier		    a->dst.addr.v.a.addr.addr32[3] == 0))
1045145837Smlaier			return (1);
1046145837Smlaier		return (0);
1047145837Smlaier	case PF_ADDR_DYNIFTL:
1048145837Smlaier		if (strcmp(a->dst.addr.v.ifname, b->dst.addr.v.ifname) != 0 ||
1049145837Smlaier		    a->dst.addr.iflags != a->dst.addr.iflags ||
1050145837Smlaier		    memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask,
1051145837Smlaier		    sizeof(a->dst.addr.v.a.mask)))
1052145837Smlaier			return (1);
1053145837Smlaier		return (0);
1054145837Smlaier	case PF_ADDR_NOROUTE:
1055145837Smlaier		return (0);
1056145837Smlaier	case PF_ADDR_TABLE:
1057145837Smlaier		return (strcmp(a->dst.addr.v.tblname, b->dst.addr.v.tblname));
1058145837Smlaier	}
1059145837Smlaier	return (1);
1060145837Smlaier}
1061145837Smlaier
1062145837Smlaier/* Compare two rules DST port field for skiplist construction */
1063145837Smlaierint
1064145837Smlaierskip_cmp_dst_port(struct pf_rule *a, struct pf_rule *b)
1065145837Smlaier{
1066145837Smlaier	/* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
1067145837Smlaier	 *    && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
1068145837Smlaier	 *    a->proto == IPPROTO_ICMP
1069145837Smlaier	 *	return (1);
1070145837Smlaier	 */
1071145837Smlaier	if (a->dst.port_op == PF_OP_NONE || a->dst.port_op != b->dst.port_op ||
1072145837Smlaier	    a->dst.port[0] != b->dst.port[0] ||
1073145837Smlaier	    a->dst.port[1] != b->dst.port[1])
1074145837Smlaier		return (1);
1075145837Smlaier	return (0);
1076145837Smlaier}
1077145837Smlaier
1078145837Smlaier/* Compare two rules IFP field for skiplist construction */
1079145837Smlaierint
1080145837Smlaierskip_cmp_ifp(struct pf_rule *a, struct pf_rule *b)
1081145837Smlaier{
1082145837Smlaier	if (strcmp(a->ifname, b->ifname) || a->ifname[0] == '\0')
1083145837Smlaier		return (1);
1084145837Smlaier	return (a->ifnot != b->ifnot);
1085145837Smlaier}
1086145837Smlaier
1087145837Smlaier/* Compare two rules PROTO field for skiplist construction */
1088145837Smlaierint
1089145837Smlaierskip_cmp_proto(struct pf_rule *a, struct pf_rule *b)
1090145837Smlaier{
1091145837Smlaier	return (a->proto != b->proto || a->proto == 0);
1092145837Smlaier}
1093145837Smlaier
1094145837Smlaier/* Compare two rules SRC addr field for skiplist construction */
1095145837Smlaierint
1096145837Smlaierskip_cmp_src_addr(struct pf_rule *a, struct pf_rule *b)
1097145837Smlaier{
1098145837Smlaier	if (a->src.neg != b->src.neg ||
1099145837Smlaier	    a->src.addr.type != b->src.addr.type)
1100145837Smlaier		return (1);
1101145837Smlaier	/* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
1102145837Smlaier	 *    && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
1103145837Smlaier	 *    a->proto == IPPROTO_ICMP
1104145837Smlaier	 *	return (1);
1105145837Smlaier	 */
1106145837Smlaier	switch (a->src.addr.type) {
1107145837Smlaier	case PF_ADDR_ADDRMASK:
1108145837Smlaier		if (memcmp(&a->src.addr.v.a.addr, &b->src.addr.v.a.addr,
1109145837Smlaier		    sizeof(a->src.addr.v.a.addr)) ||
1110145837Smlaier		    memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask,
1111145837Smlaier		    sizeof(a->src.addr.v.a.mask)) ||
1112145837Smlaier		    (a->src.addr.v.a.addr.addr32[0] == 0 &&
1113145837Smlaier		    a->src.addr.v.a.addr.addr32[1] == 0 &&
1114145837Smlaier		    a->src.addr.v.a.addr.addr32[2] == 0 &&
1115145837Smlaier		    a->src.addr.v.a.addr.addr32[3] == 0))
1116145837Smlaier			return (1);
1117145837Smlaier		return (0);
1118145837Smlaier	case PF_ADDR_DYNIFTL:
1119145837Smlaier		if (strcmp(a->src.addr.v.ifname, b->src.addr.v.ifname) != 0 ||
1120145837Smlaier		    a->src.addr.iflags != a->src.addr.iflags ||
1121145837Smlaier		    memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask,
1122145837Smlaier		    sizeof(a->src.addr.v.a.mask)))
1123145837Smlaier			return (1);
1124145837Smlaier		return (0);
1125145837Smlaier	case PF_ADDR_NOROUTE:
1126145837Smlaier		return (0);
1127145837Smlaier	case PF_ADDR_TABLE:
1128145837Smlaier		return (strcmp(a->src.addr.v.tblname, b->src.addr.v.tblname));
1129145837Smlaier	}
1130145837Smlaier	return (1);
1131145837Smlaier}
1132145837Smlaier
1133145837Smlaier/* Compare two rules SRC port field for skiplist construction */
1134145837Smlaierint
1135145837Smlaierskip_cmp_src_port(struct pf_rule *a, struct pf_rule *b)
1136145837Smlaier{
1137145837Smlaier	if (a->src.port_op == PF_OP_NONE || a->src.port_op != b->src.port_op ||
1138145837Smlaier	    a->src.port[0] != b->src.port[0] ||
1139145837Smlaier	    a->src.port[1] != b->src.port[1])
1140145837Smlaier		return (1);
1141145837Smlaier	/* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
1142145837Smlaier	 *    && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
1143145837Smlaier	 *    a->proto == IPPROTO_ICMP
1144145837Smlaier	 *	return (1);
1145145837Smlaier	 */
1146145837Smlaier	return (0);
1147145837Smlaier}
1148145837Smlaier
1149145837Smlaier
1150145837Smlaiervoid
1151145837Smlaierskip_init(void)
1152145837Smlaier{
1153145837Smlaier	struct {
1154145837Smlaier		char *name;
1155145837Smlaier		int skipnum;
1156145837Smlaier		int (*func)(struct pf_rule *, struct pf_rule *);
1157145837Smlaier	} comps[] = PF_SKIP_COMPARITORS;
1158145837Smlaier	int skipnum, i;
1159145837Smlaier
1160145837Smlaier	for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) {
1161145837Smlaier		for (i = 0; i < sizeof(comps)/sizeof(*comps); i++)
1162145837Smlaier			if (comps[i].skipnum == skipnum) {
1163145837Smlaier				skip_comparitors[skipnum] = comps[i].func;
1164145837Smlaier				skip_comparitors_names[skipnum] = comps[i].name;
1165145837Smlaier			}
1166145837Smlaier	}
1167145837Smlaier	for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++)
1168145837Smlaier		if (skip_comparitors[skipnum] == NULL)
1169145837Smlaier			errx(1, "Need to add skip step comparitor to pfctl?!");
1170145837Smlaier}
1171145837Smlaier
1172145837Smlaier/*
1173145837Smlaier * Add a host/netmask to a table
1174145837Smlaier */
1175145837Smlaierint
1176145837Smlaieradd_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af,
1177145837Smlaier    struct pf_rule_addr *addr)
1178145837Smlaier{
1179145837Smlaier#ifdef OPT_DEBUG
1180145837Smlaier	char buf[128];
1181145837Smlaier#endif /* OPT_DEBUG */
1182145837Smlaier	static int tablenum = 0;
1183145837Smlaier	struct node_host node_host;
1184145837Smlaier
1185145837Smlaier	if (*tbl == NULL) {
1186145837Smlaier		if ((*tbl = calloc(1, sizeof(**tbl))) == NULL ||
1187145837Smlaier		    ((*tbl)->pt_buf = calloc(1, sizeof(*(*tbl)->pt_buf))) ==
1188145837Smlaier		    NULL)
1189145837Smlaier			err(1, "calloc");
1190145837Smlaier		(*tbl)->pt_buf->pfrb_type = PFRB_ADDRS;
1191145837Smlaier		SIMPLEQ_INIT(&(*tbl)->pt_nodes);
1192145837Smlaier
1193145837Smlaier		/* This is just a temporary table name */
1194145837Smlaier		snprintf((*tbl)->pt_name, sizeof((*tbl)->pt_name), "%s%d",
1195145837Smlaier		    PF_OPT_TABLE_PREFIX, tablenum++);
1196145837Smlaier		DEBUG("creating table <%s>", (*tbl)->pt_name);
1197145837Smlaier	}
1198145837Smlaier
1199145837Smlaier	memset(&node_host, 0, sizeof(node_host));
1200145837Smlaier	node_host.af = af;
1201145837Smlaier	node_host.addr = addr->addr;
1202145837Smlaier
1203145837Smlaier#ifdef OPT_DEBUG
1204145837Smlaier	DEBUG("<%s> adding %s/%d", (*tbl)->pt_name, inet_ntop(af,
1205145837Smlaier	    &node_host.addr.v.a.addr, buf, sizeof(buf)),
1206145837Smlaier	    unmask(&node_host.addr.v.a.mask, af));
1207145837Smlaier#endif /* OPT_DEBUG */
1208145837Smlaier
1209145837Smlaier	if (append_addr_host((*tbl)->pt_buf, &node_host, 0, 0)) {
1210145837Smlaier		warn("failed to add host");
1211145837Smlaier		return (1);
1212145837Smlaier	}
1213145837Smlaier	if (pf->opts & PF_OPT_VERBOSE) {
1214145837Smlaier		struct node_tinit *ti;
1215145837Smlaier
1216145837Smlaier		if ((ti = calloc(1, sizeof(*ti))) == NULL)
1217145837Smlaier			err(1, "malloc");
1218145837Smlaier		if ((ti->host = malloc(sizeof(*ti->host))) == NULL)
1219145837Smlaier			err(1, "malloc");
1220145837Smlaier		memcpy(ti->host, &node_host, sizeof(*ti->host));
1221145837Smlaier		SIMPLEQ_INSERT_TAIL(&(*tbl)->pt_nodes, ti, entries);
1222145837Smlaier	}
1223145837Smlaier
1224145837Smlaier	(*tbl)->pt_rulecount++;
1225145837Smlaier	if ((*tbl)->pt_rulecount == TABLE_THRESHOLD)
1226145837Smlaier		DEBUG("table <%s> now faster than skip steps", (*tbl)->pt_name);
1227145837Smlaier
1228145837Smlaier	return (0);
1229145837Smlaier}
1230145837Smlaier
1231145837Smlaier
1232145837Smlaier/*
1233145837Smlaier * Do the dirty work of choosing an unused table name and creating it.
1234145837Smlaier * (be careful with the table name, it might already be used in another anchor)
1235145837Smlaier */
1236145837Smlaierint
1237145837Smlaierpf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl)
1238145837Smlaier{
1239145837Smlaier	static int tablenum;
1240145837Smlaier	struct pfr_table *t;
1241145837Smlaier
1242145837Smlaier	if (table_buffer.pfrb_type == 0) {
1243145837Smlaier		/* Initialize the list of tables */
1244145837Smlaier		table_buffer.pfrb_type = PFRB_TABLES;
1245145837Smlaier		for (;;) {
1246145837Smlaier			pfr_buf_grow(&table_buffer, table_buffer.pfrb_size);
1247145837Smlaier			table_buffer.pfrb_size = table_buffer.pfrb_msize;
1248145837Smlaier			if (pfr_get_tables(NULL, table_buffer.pfrb_caddr,
1249145837Smlaier			    &table_buffer.pfrb_size, PFR_FLAG_ALLRSETS))
1250145837Smlaier				err(1, "pfr_get_tables");
1251145837Smlaier			if (table_buffer.pfrb_size <= table_buffer.pfrb_msize)
1252145837Smlaier				break;
1253145837Smlaier		}
1254145837Smlaier		table_identifier = arc4random();
1255145837Smlaier	}
1256145837Smlaier
1257145837Smlaier	/* XXX would be *really* nice to avoid duplicating identical tables */
1258145837Smlaier
1259145837Smlaier	/* Now we have to pick a table name that isn't used */
1260145837Smlaieragain:
1261145837Smlaier	DEBUG("translating temporary table <%s> to <%s%x_%d>", tbl->pt_name,
1262145837Smlaier	    PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
1263145837Smlaier	snprintf(tbl->pt_name, sizeof(tbl->pt_name), "%s%x_%d",
1264145837Smlaier	    PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
1265145837Smlaier	PFRB_FOREACH(t, &table_buffer) {
1266145837Smlaier		if (strcasecmp(t->pfrt_name, tbl->pt_name) == 0) {
1267145837Smlaier			/* Collision.  Try again */
1268145837Smlaier			DEBUG("wow, table <%s> in use.  trying again",
1269145837Smlaier			    tbl->pt_name);
1270145837Smlaier			table_identifier = arc4random();
1271145837Smlaier			goto again;
1272145837Smlaier		}
1273145837Smlaier	}
1274145837Smlaier	tablenum++;
1275145837Smlaier
1276145837Smlaier
1277145837Smlaier	if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1, pf->anchor,
1278145837Smlaier	    tbl->pt_buf, pf->tticket)) {
1279145837Smlaier		warn("failed to create table %s", tbl->pt_name);
1280145837Smlaier		return (1);
1281145837Smlaier	}
1282145837Smlaier	return (0);
1283145837Smlaier}
1284145837Smlaier
1285145837Smlaier/*
1286145837Smlaier * Partition the flat ruleset into a list of distinct superblocks
1287145837Smlaier */
1288145837Smlaierint
1289145837Smlaierconstruct_superblocks(struct pfctl *pf, struct pf_opt_queue *opt_queue,
1290145837Smlaier    struct superblocks *superblocks)
1291145837Smlaier{
1292145837Smlaier	struct superblock *block = NULL;
1293145837Smlaier	struct pf_opt_rule *por;
1294145837Smlaier	int i;
1295145837Smlaier
1296145837Smlaier	while (!TAILQ_EMPTY(opt_queue)) {
1297145837Smlaier		por = TAILQ_FIRST(opt_queue);
1298145837Smlaier		TAILQ_REMOVE(opt_queue, por, por_entry);
1299145837Smlaier		if (block == NULL || !superblock_inclusive(block, por)) {
1300145837Smlaier			if ((block = calloc(1, sizeof(*block))) == NULL) {
1301145837Smlaier				warn("calloc");
1302145837Smlaier				return (1);
1303145837Smlaier			}
1304145837Smlaier			TAILQ_INIT(&block->sb_rules);
1305145837Smlaier			for (i = 0; i < PF_SKIP_COUNT; i++)
1306145837Smlaier				TAILQ_INIT(&block->sb_skipsteps[i]);
1307145837Smlaier			TAILQ_INSERT_TAIL(superblocks, block, sb_entry);
1308145837Smlaier		}
1309145837Smlaier		TAILQ_INSERT_TAIL(&block->sb_rules, por, por_entry);
1310145837Smlaier	}
1311145837Smlaier
1312145837Smlaier	return (0);
1313145837Smlaier}
1314145837Smlaier
1315145837Smlaier
1316145837Smlaier/*
1317145837Smlaier * Compare two rule addresses
1318145837Smlaier */
1319145837Smlaierint
1320145837Smlaieraddrs_equal(struct pf_rule_addr *a, struct pf_rule_addr *b)
1321145837Smlaier{
1322145837Smlaier	if (a->neg != b->neg)
1323145837Smlaier		return (0);
1324145837Smlaier	return (memcmp(&a->addr, &b->addr, sizeof(a->addr)) == 0);
1325145837Smlaier}
1326145837Smlaier
1327145837Smlaier
1328145837Smlaier/*
1329145837Smlaier * The addresses are not equal, but can we combine them into one table?
1330145837Smlaier */
1331145837Smlaierint
1332145837Smlaieraddrs_combineable(struct pf_rule_addr *a, struct pf_rule_addr *b)
1333145837Smlaier{
1334145837Smlaier	if (a->addr.type != PF_ADDR_ADDRMASK ||
1335145837Smlaier	    b->addr.type != PF_ADDR_ADDRMASK)
1336145837Smlaier		return (0);
1337145837Smlaier	if (a->neg != b->neg || a->port_op != b->port_op ||
1338145837Smlaier	    a->port[0] != b->port[0] || a->port[1] != b->port[1])
1339145837Smlaier		return (0);
1340145837Smlaier	return (1);
1341145837Smlaier}
1342145837Smlaier
1343145837Smlaier
1344145837Smlaier/*
1345145837Smlaier * Are we allowed to combine these two rules
1346145837Smlaier */
1347145837Smlaierint
1348145837Smlaierrules_combineable(struct pf_rule *p1, struct pf_rule *p2)
1349145837Smlaier{
1350145837Smlaier	struct pf_rule a, b;
1351145837Smlaier
1352145837Smlaier	comparable_rule(&a, p1, COMBINED);
1353145837Smlaier	comparable_rule(&b, p2, COMBINED);
1354145837Smlaier	return (memcmp(&a, &b, sizeof(a)) == 0);
1355145837Smlaier}
1356145837Smlaier
1357145837Smlaier
1358145837Smlaier/*
1359145837Smlaier * Can a rule be included inside a superblock
1360145837Smlaier */
1361145837Smlaierint
1362145837Smlaiersuperblock_inclusive(struct superblock *block, struct pf_opt_rule *por)
1363145837Smlaier{
1364145837Smlaier	struct pf_rule a, b;
1365145837Smlaier	int i, j;
1366145837Smlaier
1367145837Smlaier	/* First check for hard breaks */
1368145837Smlaier	for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) {
1369145837Smlaier		if (pf_rule_desc[i].prf_type == BARRIER) {
1370145837Smlaier			for (j = 0; j < pf_rule_desc[i].prf_size; j++)
1371145837Smlaier				if (((char *)&por->por_rule)[j +
1372145837Smlaier				    pf_rule_desc[i].prf_offset] != 0)
1373145837Smlaier					return (0);
1374145837Smlaier		}
1375145837Smlaier	}
1376145837Smlaier
1377145837Smlaier	/* 'anchor' heads and per-rule src-track are also hard breaks */
1378145837Smlaier	if (por->por_anchor[0] != '\0' ||
1379145837Smlaier	    (por->por_rule.rule_flag & PFRULE_RULESRCTRACK))
1380145837Smlaier		return (0);
1381145837Smlaier
1382145837Smlaier	comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, NOMERGE);
1383145837Smlaier	comparable_rule(&b, &por->por_rule, NOMERGE);
1384145837Smlaier	if (strcmp(TAILQ_FIRST(&block->sb_rules)->por_anchor,
1385145837Smlaier	    por->por_anchor) == 0 && memcmp(&a, &b, sizeof(a)) == 0)
1386145837Smlaier		return (1);
1387145837Smlaier
1388145837Smlaier#ifdef OPT_DEBUG
1389145837Smlaier	for (i = 0; i < sizeof(por->por_rule); i++) {
1390145837Smlaier		int closest = -1;
1391145837Smlaier		if (((u_int8_t *)&a)[i] != ((u_int8_t *)&b)[i]) {
1392145837Smlaier			for (j = 0; j < sizeof(pf_rule_desc) /
1393145837Smlaier			    sizeof(*pf_rule_desc); j++) {
1394145837Smlaier				if (i >= pf_rule_desc[j].prf_offset &&
1395145837Smlaier				    i < pf_rule_desc[j].prf_offset +
1396145837Smlaier				    pf_rule_desc[j].prf_size) {
1397145837Smlaier					DEBUG("superblock break @ %d due to %s",
1398145837Smlaier					    por->por_rule.nr,
1399145837Smlaier					    pf_rule_desc[j].prf_name);
1400145837Smlaier					return (0);
1401145837Smlaier				}
1402145837Smlaier				if (i > pf_rule_desc[j].prf_offset) {
1403145837Smlaier					if (closest == -1 ||
1404145837Smlaier					    i-pf_rule_desc[j].prf_offset <
1405145837Smlaier					    i-pf_rule_desc[closest].prf_offset)
1406145837Smlaier						closest = j;
1407145837Smlaier				}
1408145837Smlaier			}
1409145837Smlaier
1410145837Smlaier			if (closest >= 0)
1411145837Smlaier				DEBUG("superblock break @ %d on %s+%xh",
1412145837Smlaier				    por->por_rule.nr,
1413145837Smlaier				    pf_rule_desc[closest].prf_name,
1414145837Smlaier				    i - pf_rule_desc[closest].prf_offset -
1415145837Smlaier				    pf_rule_desc[closest].prf_size);
1416145837Smlaier			else
1417145837Smlaier				DEBUG("superblock break @ %d on field @ %d",
1418145837Smlaier				    por->por_rule.nr, i);
1419145837Smlaier			return (0);
1420145837Smlaier		}
1421145837Smlaier	}
1422145837Smlaier#endif /* OPT_DEBUG */
1423145837Smlaier
1424145837Smlaier	return (0);
1425145837Smlaier}
1426145837Smlaier
1427145837Smlaier
1428145837Smlaier/*
1429145837Smlaier * Make a rule that can directly compared by memcmp()
1430145837Smlaier */
1431145837Smlaiervoid
1432145837Smlaiercomparable_rule(struct pf_rule *dst, const struct pf_rule *src, int type)
1433145837Smlaier{
1434145837Smlaier	int i;
1435145837Smlaier	/*
1436145837Smlaier	 * To simplify the comparison, we just zero out the fields that are
1437145837Smlaier	 * allowed to be different and then do a simple memcmp()
1438145837Smlaier	 */
1439145837Smlaier	memcpy(dst, src, sizeof(*dst));
1440145837Smlaier	for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++)
1441145837Smlaier		if (pf_rule_desc[i].prf_type >= type) {
1442145837Smlaier#ifdef OPT_DEBUG
1443145837Smlaier			assert(pf_rule_desc[i].prf_type != NEVER ||
1444145837Smlaier			    *(((char *)dst) + pf_rule_desc[i].prf_offset) == 0);
1445145837Smlaier#endif /* OPT_DEBUG */
1446145837Smlaier			memset(((char *)dst) + pf_rule_desc[i].prf_offset, 0,
1447145837Smlaier			    pf_rule_desc[i].prf_size);
1448145837Smlaier		}
1449145837Smlaier}
1450145837Smlaier
1451145837Smlaier
1452145837Smlaier/*
1453145837Smlaier * Remove superset information from two rules so we can directly compare them
1454145837Smlaier * with memcmp()
1455145837Smlaier */
1456145837Smlaiervoid
1457145837Smlaierexclude_supersets(struct pf_rule *super, struct pf_rule *sub)
1458145837Smlaier{
1459145837Smlaier	if (super->ifname[0] == '\0')
1460145837Smlaier		memset(sub->ifname, 0, sizeof(sub->ifname));
1461145837Smlaier	if (super->direction == PF_INOUT)
1462145837Smlaier		sub->direction = PF_INOUT;
1463145837Smlaier	if ((super->proto == 0 || super->proto == sub->proto) &&
1464145837Smlaier	    super->flags == 0 && super->flagset == 0 && (sub->flags ||
1465145837Smlaier	    sub->flagset)) {
1466145837Smlaier		sub->flags = super->flags;
1467145837Smlaier		sub->flagset = super->flagset;
1468145837Smlaier	}
1469145837Smlaier	if (super->proto == 0)
1470145837Smlaier		sub->proto = 0;
1471145837Smlaier
1472145837Smlaier	if (super->src.port_op == 0) {
1473145837Smlaier		sub->src.port_op = 0;
1474145837Smlaier		sub->src.port[0] = 0;
1475145837Smlaier		sub->src.port[1] = 0;
1476145837Smlaier	}
1477145837Smlaier	if (super->dst.port_op == 0) {
1478145837Smlaier		sub->dst.port_op = 0;
1479145837Smlaier		sub->dst.port[0] = 0;
1480145837Smlaier		sub->dst.port[1] = 0;
1481145837Smlaier	}
1482145837Smlaier
1483145837Smlaier	if (super->src.addr.type == PF_ADDR_ADDRMASK && !super->src.neg &&
1484145837Smlaier	    !sub->src.neg && super->src.addr.v.a.mask.addr32[0] == 0 &&
1485145837Smlaier	    super->src.addr.v.a.mask.addr32[1] == 0 &&
1486145837Smlaier	    super->src.addr.v.a.mask.addr32[2] == 0 &&
1487145837Smlaier	    super->src.addr.v.a.mask.addr32[3] == 0)
1488145837Smlaier		memset(&sub->src.addr, 0, sizeof(sub->src.addr));
1489145837Smlaier	else if (super->src.addr.type == PF_ADDR_ADDRMASK &&
1490145837Smlaier	    sub->src.addr.type == PF_ADDR_ADDRMASK &&
1491145837Smlaier	    super->src.neg == sub->src.neg &&
1492145837Smlaier	    super->af == sub->af &&
1493145837Smlaier	    unmask(&super->src.addr.v.a.mask, super->af) <
1494145837Smlaier	    unmask(&sub->src.addr.v.a.mask, sub->af) &&
1495145837Smlaier	    super->src.addr.v.a.addr.addr32[0] ==
1496145837Smlaier	    (sub->src.addr.v.a.addr.addr32[0] &
1497145837Smlaier	    super->src.addr.v.a.mask.addr32[0]) &&
1498145837Smlaier	    super->src.addr.v.a.addr.addr32[1] ==
1499145837Smlaier	    (sub->src.addr.v.a.addr.addr32[1] &
1500145837Smlaier	    super->src.addr.v.a.mask.addr32[1]) &&
1501145837Smlaier	    super->src.addr.v.a.addr.addr32[2] ==
1502145837Smlaier	    (sub->src.addr.v.a.addr.addr32[2] &
1503145837Smlaier	    super->src.addr.v.a.mask.addr32[2]) &&
1504145837Smlaier	    super->src.addr.v.a.addr.addr32[3] ==
1505145837Smlaier	    (sub->src.addr.v.a.addr.addr32[3] &
1506145837Smlaier	    super->src.addr.v.a.mask.addr32[3])) {
1507145837Smlaier		/* sub->src.addr is a subset of super->src.addr/mask */
1508145837Smlaier		memcpy(&sub->src.addr, &super->src.addr, sizeof(sub->src.addr));
1509145837Smlaier	}
1510145837Smlaier
1511145837Smlaier	if (super->dst.addr.type == PF_ADDR_ADDRMASK && !super->dst.neg &&
1512145837Smlaier	    !sub->dst.neg && super->dst.addr.v.a.mask.addr32[0] == 0 &&
1513145837Smlaier	    super->dst.addr.v.a.mask.addr32[1] == 0 &&
1514145837Smlaier	    super->dst.addr.v.a.mask.addr32[2] == 0 &&
1515145837Smlaier	    super->dst.addr.v.a.mask.addr32[3] == 0)
1516145837Smlaier		memset(&sub->dst.addr, 0, sizeof(sub->dst.addr));
1517145837Smlaier	else if (super->dst.addr.type == PF_ADDR_ADDRMASK &&
1518145837Smlaier	    sub->dst.addr.type == PF_ADDR_ADDRMASK &&
1519145837Smlaier	    super->dst.neg == sub->dst.neg &&
1520145837Smlaier	    super->af == sub->af &&
1521145837Smlaier	    unmask(&super->dst.addr.v.a.mask, super->af) <
1522145837Smlaier	    unmask(&sub->dst.addr.v.a.mask, sub->af) &&
1523145837Smlaier	    super->dst.addr.v.a.addr.addr32[0] ==
1524145837Smlaier	    (sub->dst.addr.v.a.addr.addr32[0] &
1525145837Smlaier	    super->dst.addr.v.a.mask.addr32[0]) &&
1526145837Smlaier	    super->dst.addr.v.a.addr.addr32[1] ==
1527145837Smlaier	    (sub->dst.addr.v.a.addr.addr32[1] &
1528145837Smlaier	    super->dst.addr.v.a.mask.addr32[1]) &&
1529145837Smlaier	    super->dst.addr.v.a.addr.addr32[2] ==
1530145837Smlaier	    (sub->dst.addr.v.a.addr.addr32[2] &
1531145837Smlaier	    super->dst.addr.v.a.mask.addr32[2]) &&
1532145837Smlaier	    super->dst.addr.v.a.addr.addr32[3] ==
1533145837Smlaier	    (sub->dst.addr.v.a.addr.addr32[3] &
1534145837Smlaier	    super->dst.addr.v.a.mask.addr32[3])) {
1535145837Smlaier		/* sub->dst.addr is a subset of super->dst.addr/mask */
1536145837Smlaier		memcpy(&sub->dst.addr, &super->dst.addr, sizeof(sub->dst.addr));
1537145837Smlaier	}
1538145837Smlaier
1539145837Smlaier	if (super->af == 0)
1540145837Smlaier		sub->af = 0;
1541145837Smlaier}
1542145837Smlaier
1543145837Smlaier
1544145837Smlaiervoid
1545145837Smlaiersuperblock_free(struct pfctl *pf, struct superblock *block)
1546145837Smlaier{
1547145837Smlaier	struct pf_opt_rule *por;
1548145837Smlaier	while ((por = TAILQ_FIRST(&block->sb_rules))) {
1549145837Smlaier		TAILQ_REMOVE(&block->sb_rules, por, por_entry);
1550145837Smlaier		if (por->por_src_tbl) {
1551145837Smlaier			if (por->por_src_tbl->pt_buf) {
1552145837Smlaier				pfr_buf_clear(por->por_src_tbl->pt_buf);
1553145837Smlaier				free(por->por_src_tbl->pt_buf);
1554145837Smlaier			}
1555145837Smlaier			free(por->por_src_tbl);
1556145837Smlaier		}
1557145837Smlaier		if (por->por_dst_tbl) {
1558145837Smlaier			if (por->por_dst_tbl->pt_buf) {
1559145837Smlaier				pfr_buf_clear(por->por_dst_tbl->pt_buf);
1560145837Smlaier				free(por->por_dst_tbl->pt_buf);
1561145837Smlaier			}
1562145837Smlaier			free(por->por_dst_tbl);
1563145837Smlaier		}
1564145837Smlaier		free(por);
1565145837Smlaier	}
1566145837Smlaier	if (block->sb_profiled_block)
1567145837Smlaier		superblock_free(pf, block->sb_profiled_block);
1568145837Smlaier	free(block);
1569145837Smlaier}
1570145837Smlaier
1571