npf_build.c revision 1.34
1/*	$NetBSD: npf_build.c,v 1.34 2014/02/06 18:48:09 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2011-2014 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This material is based upon work partially supported by The
8 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * npfctl(8) building of the configuration.
34 */
35
36#include <sys/cdefs.h>
37__RCSID("$NetBSD: npf_build.c,v 1.34 2014/02/06 18:48:09 christos Exp $");
38
39#include <sys/types.h>
40#include <sys/mman.h>
41#include <sys/stat.h>
42
43#include <stdlib.h>
44#include <inttypes.h>
45#include <string.h>
46#include <ctype.h>
47#include <unistd.h>
48#include <errno.h>
49#include <err.h>
50
51#include <pcap/pcap.h>
52#include <cdbw.h>
53
54#include "npfctl.h"
55
56#define	MAX_RULE_NESTING	16
57
58static nl_config_t *		npf_conf = NULL;
59static bool			npf_debug = false;
60static nl_rule_t *		the_rule = NULL;
61
62static nl_rule_t *		current_group[MAX_RULE_NESTING];
63static unsigned			rule_nesting_level = 0;
64static nl_rule_t *		defgroup = NULL;
65
66static void			npfctl_dump_bpf(struct bpf_program *);
67
68void
69npfctl_config_init(bool debug)
70{
71	npf_conf = npf_config_create();
72	if (npf_conf == NULL) {
73		errx(EXIT_FAILURE, "npf_config_create failed");
74	}
75	npf_debug = debug;
76	memset(current_group, 0, sizeof(current_group));
77}
78
79int
80npfctl_config_send(int fd, const char *out)
81{
82	int error;
83
84	if (out) {
85		_npf_config_setsubmit(npf_conf, out);
86		printf("\nSaving to %s\n", out);
87	}
88	if (!defgroup) {
89		errx(EXIT_FAILURE, "default group was not defined");
90	}
91	npf_rule_insert(npf_conf, NULL, defgroup);
92	error = npf_config_submit(npf_conf, fd);
93	if (error) {
94		nl_error_t ne;
95		_npf_config_error(npf_conf, &ne);
96		npfctl_print_error(&ne);
97	}
98	if (fd) {
99		npf_config_destroy(npf_conf);
100	}
101	return error;
102}
103
104nl_config_t *
105npfctl_config_ref(void)
106{
107	return npf_conf;
108}
109
110nl_rule_t *
111npfctl_rule_ref(void)
112{
113	return the_rule;
114}
115
116bool
117npfctl_debug_addif(const char *ifname)
118{
119	const char tname[] = "npftest";
120	const size_t tnamelen = sizeof(tname) - 1;
121
122	if (npf_debug) {
123		_npf_debug_addif(npf_conf, ifname);
124		return strncmp(ifname, tname, tnamelen) == 0;
125	}
126	return 0;
127}
128
129unsigned
130npfctl_table_getid(const char *name)
131{
132	unsigned tid = (unsigned)-1;
133	nl_table_t *tl;
134
135	/* XXX dynamic ruleset */
136	if (!npf_conf) {
137		return (unsigned)-1;
138	}
139
140	/* XXX: Iterating all as we need to rewind for the next call. */
141	while ((tl = npf_table_iterate(npf_conf)) != NULL) {
142		const char *tname = npf_table_getname(tl);
143		if (strcmp(tname, name) == 0) {
144			tid = npf_table_getid(tl);
145		}
146	}
147	return tid;
148}
149
150static in_port_t
151npfctl_get_singleport(const npfvar_t *vp)
152{
153	port_range_t *pr;
154	in_port_t *port;
155
156	if (npfvar_get_count(vp) > 1) {
157		yyerror("multiple ports are not valid");
158	}
159	pr = npfvar_get_data(vp, NPFVAR_PORT_RANGE, 0);
160	if (pr->pr_start != pr->pr_end) {
161		yyerror("port range is not valid");
162	}
163	port = &pr->pr_start;
164	return *port;
165}
166
167static fam_addr_mask_t *
168npfctl_get_singlefam(const npfvar_t *vp)
169{
170	if (npfvar_get_count(vp) > 1) {
171		yyerror("multiple addresses are not valid");
172	}
173	return npfvar_get_data(vp, NPFVAR_FAM, 0);
174}
175
176static bool
177npfctl_build_fam(npf_bpf_t *ctx, sa_family_t family,
178    fam_addr_mask_t *fam, int opts)
179{
180	/*
181	 * If family is specified, address does not match it and the
182	 * address is extracted from the interface, then simply ignore.
183	 * Otherwise, address of invalid family was passed manually.
184	 */
185	if (family != AF_UNSPEC && family != fam->fam_family) {
186		if (!fam->fam_ifindex) {
187			yyerror("specified address is not of the required "
188			    "family %d", family);
189		}
190		return false;
191	}
192
193	family = fam->fam_family;
194	if (family != AF_INET && family != AF_INET6) {
195		yyerror("family %d is not supported", family);
196	}
197
198	/*
199	 * Optimise 0.0.0.0/0 case to be NOP.  Otherwise, address with
200	 * zero mask would never match and therefore is not valid.
201	 */
202	if (fam->fam_mask == 0) {
203		static const npf_addr_t zero; /* must be static */
204
205		if (memcmp(&fam->fam_addr, &zero, sizeof(npf_addr_t))) {
206			yyerror("filter criterion would never match");
207		}
208		return false;
209	}
210
211	npfctl_bpf_cidr(ctx, opts, family, &fam->fam_addr, fam->fam_mask);
212	return true;
213}
214
215static void
216npfctl_build_vars(npf_bpf_t *ctx, sa_family_t family, npfvar_t *vars, int opts)
217{
218	const int type = npfvar_get_type(vars, 0);
219	size_t i;
220
221	npfctl_bpf_group(ctx);
222	for (i = 0; i < npfvar_get_count(vars); i++) {
223		void *data = npfvar_get_data(vars, type, i);
224		assert(data != NULL);
225
226		switch (type) {
227		case NPFVAR_FAM: {
228			fam_addr_mask_t *fam = data;
229			npfctl_build_fam(ctx, family, fam, opts);
230			break;
231		}
232		case NPFVAR_PORT_RANGE: {
233			port_range_t *pr = data;
234			npfctl_bpf_ports(ctx, opts, pr->pr_start, pr->pr_end);
235			break;
236		}
237		case NPFVAR_TABLE: {
238			u_int tid;
239			memcpy(&tid, data, sizeof(u_int));
240			npfctl_bpf_table(ctx, opts, tid);
241			break;
242		}
243		default:
244			assert(false);
245		}
246	}
247	npfctl_bpf_endgroup(ctx);
248}
249
250static void
251npfctl_build_proto(npf_bpf_t *ctx, sa_family_t family, const opt_proto_t *op)
252{
253	const npfvar_t *popts = op->op_opts;
254	const int proto = op->op_proto;
255
256	/* IP version and/or L4 protocol matching. */
257	if (family != AF_UNSPEC || proto != -1) {
258		npfctl_bpf_proto(ctx, family, proto);
259	}
260
261	switch (proto) {
262	case IPPROTO_TCP:
263		/* Build TCP flags matching (optional). */
264		if (popts) {
265			uint8_t *tf, *tf_mask;
266
267			assert(npfvar_get_count(popts) == 2);
268			tf = npfvar_get_data(popts, NPFVAR_TCPFLAG, 0);
269			tf_mask = npfvar_get_data(popts, NPFVAR_TCPFLAG, 1);
270			npfctl_bpf_tcpfl(ctx, *tf, *tf_mask);
271		}
272		break;
273	case IPPROTO_ICMP:
274	case IPPROTO_ICMPV6:
275		/* Build ICMP/ICMPv6 type and/or code matching. */
276		if (popts) {
277			int *icmp_type, *icmp_code;
278
279			assert(npfvar_get_count(popts) == 2);
280			icmp_type = npfvar_get_data(popts, NPFVAR_ICMP, 0);
281			icmp_code = npfvar_get_data(popts, NPFVAR_ICMP, 1);
282			npfctl_bpf_icmp(ctx, *icmp_type, *icmp_code);
283		}
284		break;
285	default:
286		/* No options for other protocols. */
287		break;
288	}
289}
290
291static bool
292npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
293    const filt_opts_t *fopts)
294{
295	const addr_port_t *apfrom = &fopts->fo_from;
296	const addr_port_t *apto = &fopts->fo_to;
297	const int proto = op->op_proto;
298	bool noproto, noaddrs, noports;
299	npf_bpf_t *bc;
300	size_t len;
301
302	/* If none specified, then no byte-code. */
303	noproto = family == AF_UNSPEC && proto == -1 && !op->op_opts;
304	noaddrs = !apfrom->ap_netaddr && !apto->ap_netaddr;
305	noports = !apfrom->ap_portrange && !apto->ap_portrange;
306	if (noproto && noaddrs && noports) {
307		return false;
308	}
309
310	/*
311	 * Sanity check: ports can only be used with TCP or UDP protocol.
312	 * No filter options are supported for other protocols, only the
313	 * IP addresses are allowed.
314	 */
315	if (!noports) {
316		switch (proto) {
317		case IPPROTO_TCP:
318		case IPPROTO_UDP:
319		case -1:
320			break;
321		default:
322			yyerror("invalid filter options for protocol %d", proto);
323		}
324	}
325
326	bc = npfctl_bpf_create();
327
328	/* Build layer 4 protocol blocks. */
329	npfctl_build_proto(bc, family, op);
330
331	/* Build IP address blocks. */
332	npfctl_build_vars(bc, family, apfrom->ap_netaddr, MATCH_SRC);
333	npfctl_build_vars(bc, family, apto->ap_netaddr, MATCH_DST);
334
335	/* Build port-range blocks. */
336	npfctl_build_vars(bc, family, apfrom->ap_portrange, MATCH_SRC);
337	npfctl_build_vars(bc, family, apto->ap_portrange, MATCH_DST);
338
339	/* Set the byte-code marks, if any. */
340	const void *bmarks = npfctl_bpf_bmarks(bc, &len);
341	if (npf_rule_setinfo(rl, bmarks, len) == -1) {
342		errx(EXIT_FAILURE, "npf_rule_setinfo failed");
343	}
344
345	/* Complete BPF byte-code and pass to the rule. */
346	struct bpf_program *bf = npfctl_bpf_complete(bc);
347	len = bf->bf_len * sizeof(struct bpf_insn);
348
349	if (npf_rule_setcode(rl, NPF_CODE_BPF, bf->bf_insns, len) == -1) {
350		errx(EXIT_FAILURE, "npf_rule_setcode failed");
351	}
352	npfctl_dump_bpf(bf);
353	npfctl_bpf_destroy(bc);
354
355	return true;
356}
357
358static void
359npfctl_build_pcap(nl_rule_t *rl, const char *filter)
360{
361	const size_t maxsnaplen = 64 * 1024;
362	struct bpf_program bf;
363	size_t len;
364
365	if (pcap_compile_nopcap(maxsnaplen, DLT_RAW, &bf,
366	    filter, 1, PCAP_NETMASK_UNKNOWN) == -1) {
367		yyerror("invalid pcap-filter(7) syntax");
368	}
369	len = bf.bf_len * sizeof(struct bpf_insn);
370
371	if (npf_rule_setcode(rl, NPF_CODE_BPF, bf.bf_insns, len) == -1) {
372		errx(EXIT_FAILURE, "npf_rule_setcode failed");
373	}
374	npfctl_dump_bpf(&bf);
375	pcap_freecode(&bf);
376}
377
378static void
379npfctl_build_rpcall(nl_rproc_t *rp, const char *name, npfvar_t *args)
380{
381	npf_extmod_t *extmod;
382	nl_ext_t *extcall;
383	int error;
384
385	extmod = npf_extmod_get(name, &extcall);
386	if (extmod == NULL) {
387		yyerror("unknown rule procedure '%s'", name);
388	}
389
390	for (size_t i = 0; i < npfvar_get_count(args); i++) {
391		const char *param, *value;
392		proc_param_t *p;
393
394		p = npfvar_get_data(args, NPFVAR_PROC_PARAM, i);
395		param = p->pp_param;
396		value = p->pp_value;
397
398		error = npf_extmod_param(extmod, extcall, param, value);
399		switch (error) {
400		case EINVAL:
401			yyerror("invalid parameter '%s'", param);
402		default:
403			break;
404		}
405	}
406	error = npf_rproc_extcall(rp, extcall);
407	if (error) {
408		yyerror(error == EEXIST ?
409		    "duplicate procedure call" : "unexpected error");
410	}
411}
412
413/*
414 * npfctl_build_rproc: create and insert a rule procedure.
415 */
416void
417npfctl_build_rproc(const char *name, npfvar_t *procs)
418{
419	nl_rproc_t *rp;
420	size_t i;
421
422	rp = npf_rproc_create(name);
423	if (rp == NULL) {
424		errx(EXIT_FAILURE, "%s failed", __func__);
425	}
426	npf_rproc_insert(npf_conf, rp);
427
428	for (i = 0; i < npfvar_get_count(procs); i++) {
429		proc_call_t *pc = npfvar_get_data(procs, NPFVAR_PROC, i);
430		npfctl_build_rpcall(rp, pc->pc_name, pc->pc_opts);
431	}
432}
433
434void
435npfctl_build_maprset(const char *name, int attr, const char *ifname)
436{
437	const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT);
438	nl_rule_t *rl;
439
440	/* If no direction is not specified, then both. */
441	if ((attr & attr_di) == 0) {
442		attr |= attr_di;
443	}
444	/* Allow only "in/out" attributes. */
445	attr = NPF_RULE_GROUP | NPF_RULE_GROUP | (attr & attr_di);
446	rl = npf_rule_create(name, attr, ifname);
447	npf_nat_insert(npf_conf, rl, NPF_PRI_LAST);
448}
449
450/*
451 * npfctl_build_group: create a group, insert into the global ruleset,
452 * update the current group pointer and increase the nesting level.
453 */
454void
455npfctl_build_group(const char *name, int attr, const char *ifname, bool def)
456{
457	const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT);
458	nl_rule_t *rl;
459
460	if (def || (attr & attr_di) == 0) {
461		attr |= attr_di;
462	}
463
464	rl = npf_rule_create(name, attr | NPF_RULE_GROUP, ifname);
465	npf_rule_setprio(rl, NPF_PRI_LAST);
466	if (def) {
467		if (defgroup) {
468			yyerror("multiple default groups are not valid");
469		}
470		if (rule_nesting_level) {
471			yyerror("default group can only be at the top level");
472		}
473		defgroup = rl;
474	} else {
475		nl_rule_t *cg = current_group[rule_nesting_level];
476		npf_rule_insert(npf_conf, cg, rl);
477	}
478
479	/* Set the current group and increase the nesting level. */
480	if (rule_nesting_level >= MAX_RULE_NESTING) {
481		yyerror("rule nesting limit reached");
482	}
483	current_group[++rule_nesting_level] = rl;
484}
485
486void
487npfctl_build_group_end(void)
488{
489	assert(rule_nesting_level > 0);
490	current_group[rule_nesting_level--] = NULL;
491}
492
493/*
494 * npfctl_build_rule: create a rule, build byte-code from filter options,
495 * if any, and insert into the ruleset of current group, or set the rule.
496 */
497void
498npfctl_build_rule(uint32_t attr, const char *ifname, sa_family_t family,
499    const opt_proto_t *op, const filt_opts_t *fopts,
500    const char *pcap_filter, const char *rproc)
501{
502	nl_rule_t *rl;
503
504	attr |= (npf_conf ? 0 : NPF_RULE_DYNAMIC);
505
506	rl = npf_rule_create(NULL, attr, ifname);
507	if (pcap_filter) {
508		npfctl_build_pcap(rl, pcap_filter);
509	} else {
510		npfctl_build_code(rl, family, op, fopts);
511	}
512
513	if (rproc) {
514		npf_rule_setproc(rl, rproc);
515	}
516
517	if (npf_conf) {
518		nl_rule_t *cg = current_group[rule_nesting_level];
519
520		if (rproc && !npf_rproc_exists_p(npf_conf, rproc)) {
521			yyerror("rule procedure '%s' is not defined", rproc);
522		}
523		assert(cg != NULL);
524		npf_rule_setprio(rl, NPF_PRI_LAST);
525		npf_rule_insert(npf_conf, cg, rl);
526	} else {
527		/* We have parsed a single rule - set it. */
528		the_rule = rl;
529	}
530}
531
532/*
533 * npfctl_build_nat: create a single NAT policy of a specified
534 * type with a given filter options.
535 */
536static void
537npfctl_build_nat(int type, const char *ifname, sa_family_t family,
538    const addr_port_t *ap, const filt_opts_t *fopts, bool binat)
539{
540	const opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
541	fam_addr_mask_t *am;
542	in_port_t port;
543	nl_nat_t *nat;
544
545	if (!ap->ap_netaddr) {
546		yyerror("%s network segment is not specified",
547		    type == NPF_NATIN ? "inbound" : "outbound");
548	}
549	am = npfctl_get_singlefam(ap->ap_netaddr);
550	if (am->fam_family != family) {
551		yyerror("IPv6 NAT is not supported");
552	}
553
554	switch (type) {
555	case NPF_NATOUT:
556		/*
557		 * Outbound NAT (or source NAT) policy, usually used for the
558		 * traditional NAPT.  If it is a half for bi-directional NAT,
559		 * then no port translation with mapping.
560		 */
561		nat = npf_nat_create(NPF_NATOUT, !binat ?
562		    (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0,
563		    ifname, &am->fam_addr, am->fam_family, 0);
564		break;
565	case NPF_NATIN:
566		/*
567		 * Inbound NAT (or destination NAT).  Unless bi-NAT, a port
568		 * must be specified, since it has to be redirection.
569		 */
570		port = 0;
571		if (!binat) {
572			if (!ap->ap_portrange) {
573				yyerror("inbound port is not specified");
574			}
575			port = npfctl_get_singleport(ap->ap_portrange);
576		}
577		nat = npf_nat_create(NPF_NATIN, !binat ? NPF_NAT_PORTS : 0,
578		    ifname, &am->fam_addr, am->fam_family, port);
579		break;
580	default:
581		assert(false);
582	}
583
584	npfctl_build_code(nat, family, &op, fopts);
585	npf_nat_insert(npf_conf, nat, NPF_PRI_LAST);
586}
587
588/*
589 * npfctl_build_natseg: validate and create NAT policies.
590 */
591void
592npfctl_build_natseg(int sd, int type, const char *ifname,
593    const addr_port_t *ap1, const addr_port_t *ap2,
594    const filt_opts_t *fopts)
595{
596	sa_family_t af = AF_INET;
597	filt_opts_t imfopts;
598	bool binat;
599
600	if (sd == NPFCTL_NAT_STATIC) {
601		yyerror("static NAT is not yet supported");
602	}
603	assert(sd == NPFCTL_NAT_DYNAMIC);
604	assert(ifname != NULL);
605
606	/*
607	 * Bi-directional NAT is a combination of inbound NAT and outbound
608	 * NAT policies.  Note that the translation address is local IP and
609	 * the filter criteria is inverted accordingly.
610	 */
611	binat = (NPF_NATIN | NPF_NATOUT) == type;
612
613	/*
614	 * If the filter criteria is not specified explicitly, apply implicit
615	 * filtering according to the given network segments.
616	 *
617	 * Note: filled below, depending on the type.
618	 */
619	if (__predict_true(!fopts)) {
620		fopts = &imfopts;
621	}
622
623	if (type & NPF_NATIN) {
624		memset(&imfopts, 0, sizeof(filt_opts_t));
625		memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t));
626		npfctl_build_nat(NPF_NATIN, ifname, af, ap1, fopts, binat);
627	}
628	if (type & NPF_NATOUT) {
629		memset(&imfopts, 0, sizeof(filt_opts_t));
630		memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t));
631		npfctl_build_nat(NPF_NATOUT, ifname, af, ap2, fopts, binat);
632	}
633}
634
635/*
636 * npfctl_fill_table: fill NPF table with entries from a specified file.
637 */
638static void
639npfctl_fill_table(nl_table_t *tl, u_int type, const char *fname)
640{
641	struct cdbw *cdbw = NULL;	/* XXX: gcc */
642	char *buf = NULL;
643	int l = 0;
644	FILE *fp;
645	size_t n;
646
647	if (type == NPF_TABLE_CDB && (cdbw = cdbw_open()) == NULL) {
648		err(EXIT_FAILURE, "cdbw_open");
649	}
650	fp = fopen(fname, "r");
651	if (fp == NULL) {
652		err(EXIT_FAILURE, "open '%s'", fname);
653	}
654	while (l++, getline(&buf, &n, fp) != -1) {
655		fam_addr_mask_t fam;
656		int alen;
657
658		if (*buf == '\n' || *buf == '#') {
659			continue;
660		}
661
662		if (!npfctl_parse_cidr(buf, &fam, &alen)) {
663			errx(EXIT_FAILURE,
664			    "%s:%d: invalid table entry", fname, l);
665		}
666		if (type != NPF_TABLE_TREE && fam.fam_mask != NPF_NO_NETMASK) {
667			errx(EXIT_FAILURE, "%s:%d: mask used with the "
668			    "non-tree table", fname, l);
669		}
670
671		/*
672		 * Create and add a table entry.
673		 */
674		if (type == NPF_TABLE_CDB) {
675			const npf_addr_t *addr = &fam.fam_addr;
676			if (cdbw_put(cdbw, addr, alen, addr, alen) == -1) {
677				err(EXIT_FAILURE, "cdbw_put");
678			}
679		} else {
680			npf_table_add_entry(tl, fam.fam_family,
681			    &fam.fam_addr, fam.fam_mask);
682		}
683	}
684	if (buf != NULL) {
685		free(buf);
686	}
687
688	if (type == NPF_TABLE_CDB) {
689		struct stat sb;
690		char sfn[32];
691		void *cdb;
692		int fd;
693
694		strlcpy(sfn, "/tmp/npfcdb.XXXXXX", sizeof(sfn));
695		if ((fd = mkstemp(sfn)) == -1) {
696			err(EXIT_FAILURE, "mkstemp");
697		}
698		unlink(sfn);
699
700		if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) {
701			err(EXIT_FAILURE, "cdbw_output");
702		}
703		cdbw_close(cdbw);
704
705		if (fstat(fd, &sb) == -1) {
706			err(EXIT_FAILURE, "fstat");
707		}
708		if ((cdb = mmap(NULL, sb.st_size, PROT_READ,
709		    MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
710			err(EXIT_FAILURE, "mmap");
711		}
712		npf_table_setdata(tl, cdb, sb.st_size);
713
714		close(fd);
715	}
716}
717
718/*
719 * npfctl_build_table: create an NPF table, add to the configuration and,
720 * if required, fill with contents from a file.
721 */
722void
723npfctl_build_table(const char *tname, u_int type, const char *fname)
724{
725	static unsigned tid = 0;
726	nl_table_t *tl;
727
728	tl = npf_table_create(tname, tid++, type);
729	assert(tl != NULL);
730
731	if (npf_table_insert(npf_conf, tl)) {
732		yyerror("table '%s' is already defined", tname);
733	}
734
735	if (fname) {
736		npfctl_fill_table(tl, type, fname);
737	} else if (type == NPF_TABLE_CDB) {
738		errx(EXIT_FAILURE, "tables of cdb type must be static");
739	}
740}
741
742/*
743 * npfctl_build_alg: create an NPF application level gateway and add it
744 * to the configuration.
745 */
746void
747npfctl_build_alg(const char *al_name)
748{
749	if (_npf_alg_load(npf_conf, al_name) != 0) {
750		errx(EXIT_FAILURE, "ALG '%s' already loaded", al_name);
751	}
752}
753
754static void
755npfctl_dump_bpf(struct bpf_program *bf)
756{
757	if (npf_debug) {
758		extern char *yytext;
759		extern int yylineno;
760
761		int rule_line = yylineno - (int)(*yytext == '\n');
762		printf("\nRULE AT LINE %d\n", rule_line);
763		bpf_dump(bf, 0);
764	}
765}
766