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