npf_build.c revision 1.43
1/*	$NetBSD: npf_build.c,v 1.43 2017/01/03 01:29:49 rmind Exp $	*/
2
3/*-
4 * Copyright (c) 2011-2017 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.43 2017/01/03 01:29:49 rmind Exp $");
38
39#include <sys/types.h>
40#include <sys/mman.h>
41#include <sys/stat.h>
42#define	__FAVOR_BSD
43#include <netinet/tcp.h>
44
45#include <stdlib.h>
46#include <inttypes.h>
47#include <string.h>
48#include <ctype.h>
49#include <unistd.h>
50#include <fcntl.h>
51#include <errno.h>
52#include <err.h>
53
54#include <pcap/pcap.h>
55#include <cdbw.h>
56
57#include "npfctl.h"
58
59#define	MAX_RULE_NESTING	16
60
61static nl_config_t *		npf_conf = NULL;
62static bool			npf_debug = false;
63static nl_rule_t *		the_rule = NULL;
64
65static nl_rule_t *		current_group[MAX_RULE_NESTING];
66static unsigned			rule_nesting_level = 0;
67static nl_rule_t *		defgroup = NULL;
68static unsigned			npfctl_tid_counter = 0;
69
70static void			npfctl_dump_bpf(struct bpf_program *);
71
72void
73npfctl_config_init(bool debug)
74{
75	npf_conf = npf_config_create();
76	if (npf_conf == NULL) {
77		errx(EXIT_FAILURE, "npf_config_create failed");
78	}
79	npf_debug = debug;
80	memset(current_group, 0, sizeof(current_group));
81}
82
83int
84npfctl_config_send(int fd, const char *out)
85{
86	npf_error_t errinfo;
87	int error = 0;
88
89	if (!defgroup) {
90		errx(EXIT_FAILURE, "default group was not defined");
91	}
92	npf_rule_insert(npf_conf, NULL, defgroup);
93	if (out) {
94		printf("\nSaving to %s\n", out);
95		npfctl_config_save(npf_conf, out);
96	} else {
97		error = npf_config_submit(npf_conf, fd, &errinfo);
98	}
99	if (error == EEXIST) { /* XXX */
100		errx(EXIT_FAILURE, "(re)load failed: "
101		    "some table has a duplicate entry?");
102	}
103	if (error) {
104		npfctl_print_error(&errinfo);
105	}
106	npf_config_destroy(npf_conf);
107	return error;
108}
109
110void
111npfctl_config_save(nl_config_t *ncf, const char *outfile)
112{
113	void *blob;
114	size_t len;
115	int fd;
116
117	blob = npf_config_export(ncf, &len);
118	if (!blob)
119		err(EXIT_FAILURE, "npf_config_export");
120	if ((fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1)
121		err(EXIT_FAILURE, "could not open %s", outfile);
122	if (write(fd, blob, len) != (ssize_t)len) {
123		err(EXIT_FAILURE, "write to %s failed", outfile);
124	}
125	free(blob);
126	close(fd);
127}
128
129nl_config_t *
130npfctl_config_ref(void)
131{
132	return npf_conf;
133}
134
135nl_rule_t *
136npfctl_rule_ref(void)
137{
138	return the_rule;
139}
140
141bool
142npfctl_debug_addif(const char *ifname)
143{
144	const char tname[] = "npftest";
145	const size_t tnamelen = sizeof(tname) - 1;
146
147	if (npf_debug) {
148		_npf_debug_addif(npf_conf, ifname);
149		return strncmp(ifname, tname, tnamelen) == 0;
150	}
151	return 0;
152}
153
154unsigned
155npfctl_table_getid(const char *name)
156{
157	unsigned tid = (unsigned)-1;
158	nl_table_t *tl;
159
160	/* XXX dynamic ruleset */
161	if (!npf_conf) {
162		return (unsigned)-1;
163	}
164
165	/* XXX: Iterating all as we need to rewind for the next call. */
166	while ((tl = npf_table_iterate(npf_conf)) != NULL) {
167		const char *tname = npf_table_getname(tl);
168		if (strcmp(tname, name) == 0) {
169			tid = npf_table_getid(tl);
170		}
171	}
172	return tid;
173}
174
175static in_port_t
176npfctl_get_singleport(const npfvar_t *vp)
177{
178	port_range_t *pr;
179	in_port_t *port;
180
181	if (npfvar_get_count(vp) > 1) {
182		yyerror("multiple ports are not valid");
183	}
184	pr = npfvar_get_data(vp, NPFVAR_PORT_RANGE, 0);
185	if (pr->pr_start != pr->pr_end) {
186		yyerror("port range is not valid");
187	}
188	port = &pr->pr_start;
189	return *port;
190}
191
192static fam_addr_mask_t *
193npfctl_get_singlefam(const npfvar_t *vp)
194{
195	if (npfvar_get_count(vp) > 1) {
196		yyerror("multiple addresses are not valid");
197	}
198	return npfvar_get_data(vp, NPFVAR_FAM, 0);
199}
200
201static bool
202npfctl_build_fam(npf_bpf_t *ctx, sa_family_t family,
203    fam_addr_mask_t *fam, int opts)
204{
205	/*
206	 * If family is specified, address does not match it and the
207	 * address is extracted from the interface, then simply ignore.
208	 * Otherwise, address of invalid family was passed manually.
209	 */
210	if (family != AF_UNSPEC && family != fam->fam_family) {
211		if (!fam->fam_ifindex) {
212			yyerror("specified address is not of the required "
213			    "family %d", family);
214		}
215		return false;
216	}
217
218	family = fam->fam_family;
219	if (family != AF_INET && family != AF_INET6) {
220		yyerror("family %d is not supported", family);
221	}
222
223	/*
224	 * Optimise 0.0.0.0/0 case to be NOP.  Otherwise, address with
225	 * zero mask would never match and therefore is not valid.
226	 */
227	if (fam->fam_mask == 0) {
228		static const npf_addr_t zero; /* must be static */
229
230		if (memcmp(&fam->fam_addr, &zero, sizeof(npf_addr_t))) {
231			yyerror("filter criterion would never match");
232		}
233		return false;
234	}
235
236	npfctl_bpf_cidr(ctx, opts, family, &fam->fam_addr, fam->fam_mask);
237	return true;
238}
239
240static void
241npfctl_build_vars(npf_bpf_t *ctx, sa_family_t family, npfvar_t *vars, int opts)
242{
243	const int type = npfvar_get_type(vars, 0);
244	size_t i;
245
246	npfctl_bpf_group(ctx);
247	for (i = 0; i < npfvar_get_count(vars); i++) {
248		void *data = npfvar_get_data(vars, type, i);
249		assert(data != NULL);
250
251		switch (type) {
252		case NPFVAR_FAM: {
253			fam_addr_mask_t *fam = data;
254			npfctl_build_fam(ctx, family, fam, opts);
255			break;
256		}
257		case NPFVAR_PORT_RANGE: {
258			port_range_t *pr = data;
259			npfctl_bpf_ports(ctx, opts, pr->pr_start, pr->pr_end);
260			break;
261		}
262		case NPFVAR_TABLE: {
263			u_int tid;
264			memcpy(&tid, data, sizeof(u_int));
265			npfctl_bpf_table(ctx, opts, tid);
266			break;
267		}
268		default:
269			assert(false);
270		}
271	}
272	npfctl_bpf_endgroup(ctx, (opts & MATCH_INVERT) != 0);
273}
274
275static void
276npfctl_build_proto(npf_bpf_t *ctx, sa_family_t family, const opt_proto_t *op)
277{
278	const npfvar_t *popts = op->op_opts;
279	const int proto = op->op_proto;
280
281	/* IP version and/or L4 protocol matching. */
282	if (family != AF_UNSPEC || proto != -1) {
283		npfctl_bpf_proto(ctx, family, proto);
284	}
285
286	switch (proto) {
287	case IPPROTO_TCP:
288		/* Build TCP flags matching (optional). */
289		if (popts) {
290			uint8_t *tf, *tf_mask;
291
292			assert(npfvar_get_count(popts) == 2);
293			tf = npfvar_get_data(popts, NPFVAR_TCPFLAG, 0);
294			tf_mask = npfvar_get_data(popts, NPFVAR_TCPFLAG, 1);
295			npfctl_bpf_tcpfl(ctx, *tf, *tf_mask, false);
296		}
297		break;
298	case IPPROTO_ICMP:
299	case IPPROTO_ICMPV6:
300		/* Build ICMP/ICMPv6 type and/or code matching. */
301		if (popts) {
302			int *icmp_type, *icmp_code;
303
304			assert(npfvar_get_count(popts) == 2);
305			icmp_type = npfvar_get_data(popts, NPFVAR_ICMP, 0);
306			icmp_code = npfvar_get_data(popts, NPFVAR_ICMP, 1);
307			npfctl_bpf_icmp(ctx, *icmp_type, *icmp_code);
308		}
309		break;
310	default:
311		/* No options for other protocols. */
312		break;
313	}
314}
315
316static bool
317npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
318    const filt_opts_t *fopts)
319{
320	bool noproto, noaddrs, noports, need_tcpudp = false;
321	const addr_port_t *apfrom = &fopts->fo_from;
322	const addr_port_t *apto = &fopts->fo_to;
323	const int proto = op->op_proto;
324	npf_bpf_t *bc;
325	unsigned opts;
326	size_t len;
327
328	/* If none specified, then no byte-code. */
329	noproto = family == AF_UNSPEC && proto == -1 && !op->op_opts;
330	noaddrs = !apfrom->ap_netaddr && !apto->ap_netaddr;
331	noports = !apfrom->ap_portrange && !apto->ap_portrange;
332	if (noproto && noaddrs && noports) {
333		return false;
334	}
335
336	/*
337	 * Sanity check: ports can only be used with TCP or UDP protocol.
338	 * No filter options are supported for other protocols, only the
339	 * IP addresses are allowed.
340	 */
341	if (!noports) {
342		switch (proto) {
343		case IPPROTO_TCP:
344		case IPPROTO_UDP:
345			break;
346		case -1:
347			need_tcpudp = true;
348			break;
349		default:
350			yyerror("invalid filter options for protocol %d", proto);
351		}
352	}
353
354	bc = npfctl_bpf_create();
355
356	/* Build layer 4 protocol blocks. */
357	npfctl_build_proto(bc, family, op);
358
359	/*
360	 * If this is a stateful rule and TCP flags are not specified,
361	 * then add "flags S/SAFR" filter for TCP protocol case.
362	 */
363	if ((npf_rule_getattr(rl) & NPF_RULE_STATEFUL) != 0 &&
364	    (proto == -1 || (proto == IPPROTO_TCP && !op->op_opts))) {
365		npfctl_bpf_tcpfl(bc, TH_SYN,
366		    TH_SYN | TH_ACK | TH_FIN | TH_RST, proto == -1);
367	}
368
369	/* Build IP address blocks. */
370	opts = MATCH_SRC | (fopts->fo_finvert ? MATCH_INVERT : 0);
371	npfctl_build_vars(bc, family, apfrom->ap_netaddr, opts);
372	opts = MATCH_DST | (fopts->fo_tinvert ? MATCH_INVERT : 0);
373	npfctl_build_vars(bc, family, apto->ap_netaddr, opts);
374
375	/* Build port-range blocks. */
376	if (need_tcpudp) {
377		/* TCP/UDP check for the ports. */
378		npfctl_bpf_group(bc);
379		npfctl_bpf_proto(bc, AF_UNSPEC, IPPROTO_TCP);
380		npfctl_bpf_proto(bc, AF_UNSPEC, IPPROTO_UDP);
381		npfctl_bpf_endgroup(bc, false);
382	}
383	npfctl_build_vars(bc, family, apfrom->ap_portrange, MATCH_SRC);
384	npfctl_build_vars(bc, family, apto->ap_portrange, MATCH_DST);
385
386	/* Set the byte-code marks, if any. */
387	const void *bmarks = npfctl_bpf_bmarks(bc, &len);
388	if (npf_rule_setinfo(rl, bmarks, len) == -1) {
389		errx(EXIT_FAILURE, "npf_rule_setinfo failed");
390	}
391
392	/* Complete BPF byte-code and pass to the rule. */
393	struct bpf_program *bf = npfctl_bpf_complete(bc);
394	if (bf == NULL) {
395		npfctl_bpf_destroy(bc);
396		return true;
397	}
398	len = bf->bf_len * sizeof(struct bpf_insn);
399
400	if (npf_rule_setcode(rl, NPF_CODE_BPF, bf->bf_insns, len) == -1) {
401		errx(EXIT_FAILURE, "npf_rule_setcode failed");
402	}
403	npfctl_dump_bpf(bf);
404	npfctl_bpf_destroy(bc);
405
406	return true;
407}
408
409static void
410npfctl_build_pcap(nl_rule_t *rl, const char *filter)
411{
412	const size_t maxsnaplen = 64 * 1024;
413	struct bpf_program bf;
414	size_t len;
415
416	if (pcap_compile_nopcap(maxsnaplen, DLT_RAW, &bf,
417	    filter, 1, PCAP_NETMASK_UNKNOWN) == -1) {
418		yyerror("invalid pcap-filter(7) syntax");
419	}
420	len = bf.bf_len * sizeof(struct bpf_insn);
421
422	if (npf_rule_setcode(rl, NPF_CODE_BPF, bf.bf_insns, len) == -1) {
423		errx(EXIT_FAILURE, "npf_rule_setcode failed");
424	}
425	npfctl_dump_bpf(&bf);
426	pcap_freecode(&bf);
427}
428
429static void
430npfctl_build_rpcall(nl_rproc_t *rp, const char *name, npfvar_t *args)
431{
432	npf_extmod_t *extmod;
433	nl_ext_t *extcall;
434	int error;
435
436	extmod = npf_extmod_get(name, &extcall);
437	if (extmod == NULL) {
438		yyerror("unknown rule procedure '%s'", name);
439	}
440
441	for (size_t i = 0; i < npfvar_get_count(args); i++) {
442		const char *param, *value;
443		proc_param_t *p;
444
445		p = npfvar_get_data(args, NPFVAR_PROC_PARAM, i);
446		param = p->pp_param;
447		value = p->pp_value;
448
449		error = npf_extmod_param(extmod, extcall, param, value);
450		switch (error) {
451		case EINVAL:
452			yyerror("invalid parameter '%s'", param);
453		default:
454			break;
455		}
456	}
457	error = npf_rproc_extcall(rp, extcall);
458	if (error) {
459		yyerror(error == EEXIST ?
460		    "duplicate procedure call" : "unexpected error");
461	}
462}
463
464/*
465 * npfctl_build_rproc: create and insert a rule procedure.
466 */
467void
468npfctl_build_rproc(const char *name, npfvar_t *procs)
469{
470	nl_rproc_t *rp;
471	size_t i;
472
473	rp = npf_rproc_create(name);
474	if (rp == NULL) {
475		errx(EXIT_FAILURE, "%s failed", __func__);
476	}
477	npf_rproc_insert(npf_conf, rp);
478
479	for (i = 0; i < npfvar_get_count(procs); i++) {
480		proc_call_t *pc = npfvar_get_data(procs, NPFVAR_PROC, i);
481		npfctl_build_rpcall(rp, pc->pc_name, pc->pc_opts);
482	}
483}
484
485void
486npfctl_build_maprset(const char *name, int attr, const char *ifname)
487{
488	const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT);
489	nl_rule_t *rl;
490
491	/* If no direction is not specified, then both. */
492	if ((attr & attr_di) == 0) {
493		attr |= attr_di;
494	}
495	/* Allow only "in/out" attributes. */
496	attr = NPF_RULE_GROUP | NPF_RULE_GROUP | (attr & attr_di);
497	rl = npf_rule_create(name, attr, ifname);
498	npf_nat_insert(npf_conf, rl, NPF_PRI_LAST);
499}
500
501/*
502 * npfctl_build_group: create a group, insert into the global ruleset,
503 * update the current group pointer and increase the nesting level.
504 */
505void
506npfctl_build_group(const char *name, int attr, const char *ifname, bool def)
507{
508	const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT);
509	nl_rule_t *rl;
510
511	if (def || (attr & attr_di) == 0) {
512		attr |= attr_di;
513	}
514
515	rl = npf_rule_create(name, attr | NPF_RULE_GROUP, ifname);
516	npf_rule_setprio(rl, NPF_PRI_LAST);
517	if (def) {
518		if (defgroup) {
519			yyerror("multiple default groups are not valid");
520		}
521		if (rule_nesting_level) {
522			yyerror("default group can only be at the top level");
523		}
524		defgroup = rl;
525	} else {
526		nl_rule_t *cg = current_group[rule_nesting_level];
527		npf_rule_insert(npf_conf, cg, rl);
528	}
529
530	/* Set the current group and increase the nesting level. */
531	if (rule_nesting_level >= MAX_RULE_NESTING) {
532		yyerror("rule nesting limit reached");
533	}
534	current_group[++rule_nesting_level] = rl;
535}
536
537void
538npfctl_build_group_end(void)
539{
540	assert(rule_nesting_level > 0);
541	current_group[rule_nesting_level--] = NULL;
542}
543
544/*
545 * npfctl_build_rule: create a rule, build byte-code from filter options,
546 * if any, and insert into the ruleset of current group, or set the rule.
547 */
548void
549npfctl_build_rule(uint32_t attr, const char *ifname, sa_family_t family,
550    const opt_proto_t *op, const filt_opts_t *fopts,
551    const char *pcap_filter, const char *rproc)
552{
553	nl_rule_t *rl;
554
555	attr |= (npf_conf ? 0 : NPF_RULE_DYNAMIC);
556
557	rl = npf_rule_create(NULL, attr, ifname);
558	if (pcap_filter) {
559		npfctl_build_pcap(rl, pcap_filter);
560	} else {
561		npfctl_build_code(rl, family, op, fopts);
562	}
563
564	if (rproc) {
565		npf_rule_setproc(rl, rproc);
566	}
567
568	if (npf_conf) {
569		nl_rule_t *cg = current_group[rule_nesting_level];
570
571		if (rproc && !npf_rproc_exists_p(npf_conf, rproc)) {
572			yyerror("rule procedure '%s' is not defined", rproc);
573		}
574		assert(cg != NULL);
575		npf_rule_setprio(rl, NPF_PRI_LAST);
576		npf_rule_insert(npf_conf, cg, rl);
577	} else {
578		/* We have parsed a single rule - set it. */
579		the_rule = rl;
580	}
581}
582
583/*
584 * npfctl_build_nat: create a single NAT policy of a specified
585 * type with a given filter options.
586 */
587static nl_nat_t *
588npfctl_build_nat(int type, const char *ifname, const addr_port_t *ap,
589    const filt_opts_t *fopts, u_int flags)
590{
591	const opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
592	fam_addr_mask_t *am = npfctl_get_singlefam(ap->ap_netaddr);
593	in_port_t port;
594	nl_nat_t *nat;
595
596	if (ap->ap_portrange) {
597		port = npfctl_get_singleport(ap->ap_portrange);
598		flags &= ~NPF_NAT_PORTMAP;
599		flags |= NPF_NAT_PORTS;
600	} else {
601		port = 0;
602	}
603
604	nat = npf_nat_create(type, flags, ifname, am->fam_family,
605	    &am->fam_addr, am->fam_mask, port);
606	npfctl_build_code(nat, am->fam_family, &op, fopts);
607	npf_nat_insert(npf_conf, nat, NPF_PRI_LAST);
608	return nat;
609}
610
611/*
612 * npfctl_build_natseg: validate and create NAT policies.
613 */
614void
615npfctl_build_natseg(int sd, int type, const char *ifname,
616    const addr_port_t *ap1, const addr_port_t *ap2,
617    const filt_opts_t *fopts, u_int algo)
618{
619	fam_addr_mask_t *am1 = NULL, *am2 = NULL;
620	nl_nat_t *nt1 = NULL, *nt2 = NULL;
621	filt_opts_t imfopts;
622	uint16_t adj = 0;
623	u_int flags;
624	bool binat;
625
626	assert(ifname != NULL);
627
628	/*
629	 * Bi-directional NAT is a combination of inbound NAT and outbound
630	 * NAT policies with the translation segments inverted respectively.
631	 */
632	binat = (NPF_NATIN | NPF_NATOUT) == type;
633
634	switch (sd) {
635	case NPFCTL_NAT_DYNAMIC:
636		/*
637		 * Dynamic NAT: traditional NAPT is expected.  Unless it
638		 * is bi-directional NAT, perform port mapping.
639		 */
640		flags = !binat ? (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0;
641		break;
642	case NPFCTL_NAT_STATIC:
643		/* Static NAT: mechanic translation. */
644		flags = NPF_NAT_STATIC;
645		break;
646	default:
647		abort();
648	}
649
650	/*
651	 * Validate the mappings and their configuration.
652	 */
653
654	if ((type & NPF_NATIN) != 0) {
655		if (!ap1->ap_netaddr)
656			yyerror("inbound network segment is not specified");
657		am1 = npfctl_get_singlefam(ap1->ap_netaddr);
658	}
659	if ((type & NPF_NATOUT) != 0) {
660		if (!ap2->ap_netaddr)
661			yyerror("outbound network segment is not specified");
662		am2 = npfctl_get_singlefam(ap2->ap_netaddr);
663	}
664
665	switch (algo) {
666	case NPF_ALGO_NPT66:
667		if (am1 == NULL || am2 == NULL)
668			yyerror("1:1 mapping of two segments must be "
669			    "used for NPTv6");
670		if (am1->fam_mask != am2->fam_mask)
671			yyerror("asymmetric translation is not supported");
672		adj = npfctl_npt66_calcadj(am1->fam_mask,
673		    &am1->fam_addr, &am2->fam_addr);
674		break;
675	default:
676		if ((am1 && am1->fam_mask != NPF_NO_NETMASK) ||
677		    (am2 && am2->fam_mask != NPF_NO_NETMASK))
678			yyerror("net-to-net translation is not supported");
679		break;
680	}
681
682	/*
683	 * If the filter criteria is not specified explicitly, apply implicit
684	 * filtering according to the given network segments.
685	 *
686	 * Note: filled below, depending on the type.
687	 */
688	if (__predict_true(!fopts)) {
689		fopts = &imfopts;
690	}
691
692	if (type & NPF_NATIN) {
693		memset(&imfopts, 0, sizeof(filt_opts_t));
694		memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t));
695		nt1 = npfctl_build_nat(NPF_NATIN, ifname, ap1, fopts, flags);
696	}
697	if (type & NPF_NATOUT) {
698		memset(&imfopts, 0, sizeof(filt_opts_t));
699		memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t));
700		nt2 = npfctl_build_nat(NPF_NATOUT, ifname, ap2, fopts, flags);
701	}
702
703	if (algo == NPF_ALGO_NPT66) {
704		npf_nat_setnpt66(nt1, ~adj);
705		npf_nat_setnpt66(nt2, adj);
706	}
707}
708
709/*
710 * npfctl_fill_table: fill NPF table with entries from a specified file.
711 */
712static void
713npfctl_fill_table(nl_table_t *tl, u_int type, const char *fname)
714{
715	struct cdbw *cdbw = NULL;	/* XXX: gcc */
716	char *buf = NULL;
717	int l = 0;
718	FILE *fp;
719	size_t n;
720
721	if (type == NPF_TABLE_CDB && (cdbw = cdbw_open()) == NULL) {
722		err(EXIT_FAILURE, "cdbw_open");
723	}
724	fp = fopen(fname, "r");
725	if (fp == NULL) {
726		err(EXIT_FAILURE, "open '%s'", fname);
727	}
728	while (l++, getline(&buf, &n, fp) != -1) {
729		fam_addr_mask_t fam;
730		int alen;
731
732		if (*buf == '\n' || *buf == '#') {
733			continue;
734		}
735
736		if (!npfctl_parse_cidr(buf, &fam, &alen)) {
737			errx(EXIT_FAILURE,
738			    "%s:%d: invalid table entry", fname, l);
739		}
740		if (type != NPF_TABLE_TREE && fam.fam_mask != NPF_NO_NETMASK) {
741			errx(EXIT_FAILURE, "%s:%d: mask used with the "
742			    "non-tree table", fname, l);
743		}
744
745		/*
746		 * Create and add a table entry.
747		 */
748		if (type == NPF_TABLE_CDB) {
749			const npf_addr_t *addr = &fam.fam_addr;
750			if (cdbw_put(cdbw, addr, alen, addr, alen) == -1) {
751				err(EXIT_FAILURE, "cdbw_put");
752			}
753		} else {
754			npf_table_add_entry(tl, fam.fam_family,
755			    &fam.fam_addr, fam.fam_mask);
756		}
757	}
758	if (buf != NULL) {
759		free(buf);
760	}
761
762	if (type == NPF_TABLE_CDB) {
763		struct stat sb;
764		char sfn[32];
765		void *cdb;
766		int fd;
767
768		strncpy(sfn, "/tmp/npfcdb.XXXXXX", sizeof(sfn));
769		sfn[sizeof(sfn) - 1] = '\0';
770
771		if ((fd = mkstemp(sfn)) == -1) {
772			err(EXIT_FAILURE, "mkstemp");
773		}
774		unlink(sfn);
775
776		if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) {
777			err(EXIT_FAILURE, "cdbw_output");
778		}
779		cdbw_close(cdbw);
780
781		if (fstat(fd, &sb) == -1) {
782			err(EXIT_FAILURE, "fstat");
783		}
784		if ((cdb = mmap(NULL, sb.st_size, PROT_READ,
785		    MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
786			err(EXIT_FAILURE, "mmap");
787		}
788		npf_table_setdata(tl, cdb, sb.st_size);
789
790		close(fd);
791	}
792}
793
794/*
795 * npfctl_build_table: create an NPF table, add to the configuration and,
796 * if required, fill with contents from a file.
797 */
798void
799npfctl_build_table(const char *tname, u_int type, const char *fname)
800{
801	nl_table_t *tl;
802
803	tl = npf_table_create(tname, npfctl_tid_counter++, type);
804	assert(tl != NULL);
805
806	if (npf_table_insert(npf_conf, tl)) {
807		yyerror("table '%s' is already defined", tname);
808	}
809
810	if (fname) {
811		npfctl_fill_table(tl, type, fname);
812	} else if (type == NPF_TABLE_CDB) {
813		errx(EXIT_FAILURE, "tables of cdb type must be static");
814	}
815}
816
817npfvar_t *
818npfctl_ifnet_table(const char *ifname)
819{
820	char tname[NPF_TABLE_MAXNAMELEN];
821	nl_table_t *tl;
822	u_int tid;
823
824	snprintf(tname, sizeof(tname), ".ifnet-%s", ifname);
825
826	tid = npfctl_table_getid(tname);
827	if (tid == (unsigned)-1) {
828		tid = npfctl_tid_counter++;
829		tl = npf_table_create(tname, tid, NPF_TABLE_TREE);
830		(void)npf_table_insert(npf_conf, tl);
831	}
832	return npfvar_create_element(NPFVAR_TABLE, &tid, sizeof(u_int));
833}
834
835/*
836 * npfctl_build_alg: create an NPF application level gateway and add it
837 * to the configuration.
838 */
839void
840npfctl_build_alg(const char *al_name)
841{
842	if (_npf_alg_load(npf_conf, al_name) != 0) {
843		errx(EXIT_FAILURE, "ALG '%s' already loaded", al_name);
844	}
845}
846
847static void
848npfctl_dump_bpf(struct bpf_program *bf)
849{
850	if (npf_debug) {
851		extern char *yytext;
852		extern int yylineno;
853
854		int rule_line = yylineno - (int)(*yytext == '\n');
855		printf("\nRULE AT LINE %d\n", rule_line);
856		bpf_dump(bf, 0);
857	}
858}
859