Annotation of src/usr.sbin/npf/npfctl/npf_build.c, Revision 1.4.2.2
1.4.2.2 ! riz 1: /* $NetBSD: npf_build.c,v 1.4.2.1 2012/04/03 17:22:53 riz Exp $ */
1.1 rmind 2:
3: /*-
4: * Copyright (c) 2011-2012 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>
1.4.2.2 ! riz 37: __RCSID("$NetBSD: npf_build.c,v 1.4.2.1 2012/04/03 17:22:53 riz Exp $");
1.1 rmind 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 <assert.h>
46: #include <err.h>
47:
48: #include "npfctl.h"
49:
50: static nl_config_t * npf_conf = NULL;
51: static nl_rule_t * current_group = NULL;
52: static bool npf_debug = false;
53: static bool defgroup_set = false;
54:
55: void
56: npfctl_config_init(bool debug)
57: {
58:
59: npf_conf = npf_config_create();
60: if (npf_conf == NULL) {
61: errx(EXIT_FAILURE, "npf_config_create failed");
62: }
63: npf_debug = debug;
64: }
65:
66: int
67: npfctl_config_send(int fd)
68: {
69: int error;
70:
71: if (!fd) {
72: _npf_config_setsubmit(npf_conf, "./npf.plist");
73: }
74: if (!defgroup_set) {
75: errx(EXIT_FAILURE, "default group was not defined");
76: }
77: error = npf_config_submit(npf_conf, fd);
1.3 rmind 78: if (error) {
79: nl_error_t ne;
80: _npf_config_error(npf_conf, &ne);
81: npfctl_print_error(&ne);
82: }
1.1 rmind 83: npf_config_destroy(npf_conf);
84: return error;
85: }
86:
87: bool
88: npfctl_table_exists_p(const char *id)
89: {
90: return npf_table_exists_p(npf_conf, atoi(id));
91: }
92:
1.4.2.2 ! riz 93: static in_port_t
1.1 rmind 94: npfctl_get_singleport(const npfvar_t *vp)
95: {
96: port_range_t *pr;
1.4.2.2 ! riz 97: in_port_t *port;
1.1 rmind 98:
99: if (npfvar_get_count(vp) > 1) {
100: yyerror("multiple ports are not valid");
101: }
102: pr = npfvar_get_data(vp, NPFVAR_PORT_RANGE, 0);
103: if (pr->pr_start != pr->pr_end) {
104: yyerror("port range is not valid");
105: }
1.4.2.2 ! riz 106: port = &pr->pr_start;
! 107: return *port;
1.1 rmind 108: }
109:
110: static fam_addr_mask_t *
111: npfctl_get_singlefam(const npfvar_t *vp)
112: {
113: if (npfvar_get_count(vp) > 1) {
114: yyerror("multiple addresses are not valid");
115: }
116: return npfvar_get_data(vp, NPFVAR_FAM, 0);
117: }
118:
119: static void
120: npfctl_build_fam(nc_ctx_t *nc, sa_family_t family,
121: fam_addr_mask_t *fam, int opts)
122: {
123: /*
124: * If family is specified, address does not match it and the
125: * address is extracted from the interface, then simply ignore.
126: * Otherwise, address of invalid family was passed manually.
127: */
128: if (family != AF_UNSPEC && family != fam->fam_family) {
129: if (!fam->fam_interface) {
130: yyerror("specified address is not of the required "
131: "family %d", family);
132: }
133: return;
134: }
135:
136: /*
137: * Optimise 0.0.0.0/0 case to be NOP. Otherwise, address with
138: * zero mask would never match and therefore is not valid.
139: */
140: if (fam->fam_mask == 0) {
141: npf_addr_t zero;
142: memset(&zero, 0, sizeof(npf_addr_t));
143: if (memcmp(&fam->fam_addr, &zero, sizeof(npf_addr_t))) {
144: yyerror("filter criterion would never match");
145: }
146: return;
147: }
148:
149: switch (fam->fam_family) {
150: case AF_INET:
151: npfctl_gennc_v4cidr(nc, opts,
152: &fam->fam_addr, fam->fam_mask);
153: break;
154: case AF_INET6:
155: npfctl_gennc_v6cidr(nc, opts,
156: &fam->fam_addr, fam->fam_mask);
157: break;
158: default:
159: yyerror("family %d is not supported", fam->fam_family);
160: }
161: }
162:
163: static void
164: npfctl_build_vars(nc_ctx_t *nc, sa_family_t family, npfvar_t *vars, int opts)
165: {
1.4.2.2 ! riz 166: const int type = npfvar_get_type(vars, 0);
1.1 rmind 167: size_t i;
168:
169: npfctl_ncgen_group(nc);
170: for (i = 0; i < npfvar_get_count(vars); i++) {
171: void *data = npfvar_get_data(vars, type, i);
172: assert(data != NULL);
173:
174: switch (type) {
175: case NPFVAR_FAM: {
176: fam_addr_mask_t *fam = data;
177: npfctl_build_fam(nc, family, fam, opts);
178: break;
179: }
180: case NPFVAR_PORT_RANGE: {
181: port_range_t *pr = data;
182: if (opts & NC_MATCH_TCP) {
183: npfctl_gennc_ports(nc, opts & ~NC_MATCH_UDP,
184: pr->pr_start, pr->pr_end);
185: }
186: if (opts & NC_MATCH_UDP) {
187: npfctl_gennc_ports(nc, opts & ~NC_MATCH_TCP,
188: pr->pr_start, pr->pr_end);
189: }
190: break;
191: }
192: case NPFVAR_TABLE: {
193: u_int tid = atoi(data);
194: npfctl_gennc_tbl(nc, opts, tid);
195: break;
196: }
197: default:
198: assert(false);
199: }
200: }
201: npfctl_ncgen_endgroup(nc);
202: }
203:
204: static int
205: npfctl_build_proto(nc_ctx_t *nc, const opt_proto_t *op)
206: {
207: const npfvar_t *popts = op->op_opts;
208: int pflag = 0;
209:
210: switch (op->op_proto) {
211: case IPPROTO_TCP:
212: pflag = NC_MATCH_TCP;
213: if (!popts) {
214: break;
215: }
216: assert(npfvar_get_count(popts) == 2);
217:
218: /* Build TCP flags block (optional). */
219: uint8_t *tf, *tf_mask;
220:
221: tf = npfvar_get_data(popts, NPFVAR_TCPFLAG, 0);
222: tf_mask = npfvar_get_data(popts, NPFVAR_TCPFLAG, 1);
223: npfctl_gennc_tcpfl(nc, *tf, *tf_mask);
224: break;
225: case IPPROTO_UDP:
226: pflag = NC_MATCH_UDP;
227: break;
228: case IPPROTO_ICMP:
229: /*
230: * Build ICMP block.
231: */
232: assert(npfvar_get_count(popts) == 2);
233:
234: int *icmp_type, *icmp_code;
235: icmp_type = npfvar_get_data(popts, NPFVAR_ICMP, 0);
236: icmp_code = npfvar_get_data(popts, NPFVAR_ICMP, 1);
237: npfctl_gennc_icmp(nc, *icmp_type, *icmp_code);
238: break;
239: case -1:
240: pflag = NC_MATCH_TCP | NC_MATCH_UDP;
241: break;
242: default:
243: yyerror("protocol %d is not supported", op->op_proto);
244: }
245: return pflag;
246: }
247:
248: static bool
249: npfctl_build_ncode(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
250: const filt_opts_t *fopts, bool invert)
251: {
1.4.2.2 ! riz 252: const addr_port_t *apfrom = &fopts->fo_from;
! 253: const addr_port_t *apto = &fopts->fo_to;
1.1 rmind 254: nc_ctx_t *nc;
255: void *code;
256: size_t len;
257:
258: if (family == AF_UNSPEC && op->op_proto == -1 &&
1.4.2.2 ! riz 259: op->op_opts == NULL && !apfrom->ap_netaddr && !apto->ap_netaddr &&
! 260: !apfrom->ap_portrange && !apto->ap_portrange)
1.1 rmind 261: return false;
262:
263: int srcflag = NC_MATCH_SRC;
264: int dstflag = NC_MATCH_DST;
265:
266: if (invert) {
267: srcflag = NC_MATCH_DST;
268: dstflag = NC_MATCH_SRC;
269: }
270:
271: nc = npfctl_ncgen_create();
272:
273: /* Build IP address blocks. */
1.4.2.2 ! riz 274: npfctl_build_vars(nc, family, apfrom->ap_netaddr, srcflag);
! 275: npfctl_build_vars(nc, family, apto->ap_netaddr, dstflag);
1.1 rmind 276:
277: /* Build layer 4 protocol blocks. */
278: int pflag = npfctl_build_proto(nc, op);
279:
280: /* Build port-range blocks. */
1.4.2.2 ! riz 281: if (apfrom->ap_portrange) {
! 282: npfctl_build_vars(nc, family, apfrom->ap_portrange,
1.1 rmind 283: srcflag | pflag);
284: }
1.4.2.2 ! riz 285: if (apto->ap_portrange) {
! 286: npfctl_build_vars(nc, family, apto->ap_portrange,
1.1 rmind 287: dstflag | pflag);
288: }
289:
290: /*
291: * Complete n-code (destroys the context) and pass to the rule.
292: */
293: code = npfctl_ncgen_complete(nc, &len);
294: if (npf_debug) {
295: extern int yylineno;
1.4.2.2 ! riz 296: printf("RULE AT LINE %d\n", yylineno);
1.1 rmind 297: npfctl_ncgen_print(code, len);
298: }
299: if (npf_rule_setcode(rl, NPF_CODE_NCODE, code, len) == -1) {
300: errx(EXIT_FAILURE, "npf_rule_setcode failed");
301: }
302: free(code);
303: return true;
304: }
305:
1.4 rmind 306: static void
307: npfctl_build_rpcall(nl_rproc_t *rp, const char *name, npfvar_t *args)
308: {
309: /*
310: * XXX/TODO: Hardcoded for the first release. However,
311: * rule procedures will become fully dynamic modules.
312: */
313:
314: bool log = false, norm = false;
315: bool rnd = false, no_df = false;
316: int minttl = 0, maxmss = 0;
317:
318: if (strcmp(name, "log") == 0) {
319: log = true;
320: } else if (strcmp(name, "normalise") == 0) {
321: norm = true;
322: } else {
323: yyerror("unknown rule procedure '%s'", name);
324: }
325:
326: for (size_t i = 0; i < npfvar_get_count(args); i++) {
327: module_arg_t *arg;
328: const char *aval;
329:
330: arg = npfvar_get_data(args, NPFVAR_MODULE_ARG, i);
331: aval = arg->ma_name;
332:
333: if (log) {
334: u_int if_idx = npfctl_find_ifindex(aval);
335: if (!if_idx) {
336: yyerror("unknown interface '%s'", aval);
337: }
338: _npf_rproc_setlog(rp, if_idx);
339: return;
340: }
341:
1.4.2.2 ! riz 342: const int type = npfvar_get_type(arg->ma_opts, 0);
1.4 rmind 343: if (type != -1 && type != NPFVAR_NUM) {
344: yyerror("option '%s' is not numeric", aval);
345: }
346: unsigned long *opt;
347:
348: if (strcmp(aval, "random-id") == 0) {
349: rnd = true;
350: } else if (strcmp(aval, "min-ttl") == 0) {
351: opt = npfvar_get_data(arg->ma_opts, NPFVAR_NUM, 0);
352: minttl = *opt;
353: } else if (strcmp(aval, "max-mss") == 0) {
354: opt = npfvar_get_data(arg->ma_opts, NPFVAR_NUM, 0);
355: maxmss = *opt;
356: } else if (strcmp(aval, "no-df") == 0) {
357: no_df = true;
358: } else {
359: yyerror("unknown argument '%s'", aval);
360: }
361: }
362: assert(norm == true);
363: _npf_rproc_setnorm(rp, rnd, no_df, minttl, maxmss);
364: }
365:
1.1 rmind 366: /*
367: * npfctl_build_rproc: create and insert a rule procedure.
368: */
369: void
1.4 rmind 370: npfctl_build_rproc(const char *name, npfvar_t *procs)
1.1 rmind 371: {
372: nl_rproc_t *rp;
1.4 rmind 373: size_t i;
1.1 rmind 374:
375: rp = npf_rproc_create(name);
376: if (rp == NULL) {
377: errx(EXIT_FAILURE, "npf_rproc_create failed");
378: }
379: npf_rproc_insert(npf_conf, rp);
1.4 rmind 380:
381: for (i = 0; i < npfvar_get_count(procs); i++) {
382: proc_op_t *po = npfvar_get_data(procs, NPFVAR_PROC_OP, i);
383: npfctl_build_rpcall(rp, po->po_name, po->po_opts);
384: }
1.1 rmind 385: }
386:
387: /*
388: * npfctl_build_group: create a group, insert into the global ruleset
389: * and update the current group pointer.
390: */
391: void
392: npfctl_build_group(const char *name, int attr, u_int if_idx)
393: {
394: const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT);
395: nl_rule_t *rl;
396:
397: if (attr & NPF_RULE_DEFAULT) {
398: if (defgroup_set) {
399: yyerror("multiple default groups are not valid");
400: }
401: defgroup_set = true;
402: attr |= attr_di;
403:
404: } else if ((attr & attr_di) == 0) {
405: attr |= attr_di;
406: }
407:
1.4.2.1 riz 408: rl = npf_rule_create(name, attr | NPF_RULE_FINAL, if_idx);
1.1 rmind 409: npf_rule_insert(npf_conf, NULL, rl, NPF_PRI_NEXT);
410: current_group = rl;
411: }
412:
413: /*
414: * npfctl_build_rule: create a rule, build n-code from filter options,
415: * if any, and insert into the ruleset of current group.
416: */
417: void
418: npfctl_build_rule(int attr, u_int if_idx, sa_family_t family,
419: const opt_proto_t *op, const filt_opts_t *fopts, const char *rproc)
420: {
421: nl_rule_t *rl;
422:
423: rl = npf_rule_create(NULL, attr, if_idx);
424: npfctl_build_ncode(rl, family, op, fopts, false);
425: if (rproc && npf_rule_setproc(npf_conf, rl, rproc) != 0) {
426: yyerror("rule procedure '%s' is not defined", rproc);
427: }
428: assert(current_group != NULL);
429: npf_rule_insert(npf_conf, current_group, rl, NPF_PRI_NEXT);
430: }
431:
432: /*
433: * npfctl_build_nat: create a NAT policy of a specified type with a
434: * given filter options.
435: */
436: void
1.4.2.2 ! riz 437: npfctl_build_nat(int sd, int type, u_int if_idx, const addr_port_t *ap1,
! 438: const addr_port_t *ap2, const filt_opts_t *fopts)
1.1 rmind 439: {
1.4.2.2 ! riz 440: const opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
! 441: fam_addr_mask_t *am1, *am2;
! 442: filt_opts_t imfopts;
! 443: sa_family_t family;
1.1 rmind 444: nl_nat_t *nat;
445:
1.4.2.2 ! riz 446: if (sd == NPFCTL_NAT_STATIC) {
! 447: yyerror("static NAT is not yet supported");
! 448: }
! 449: assert(sd == NPFCTL_NAT_DYNAMIC);
! 450: assert(if_idx != 0);
! 451:
! 452: family = AF_INET;
1.1 rmind 453:
1.4.2.2 ! riz 454: if (type & NPF_NATIN) {
! 455: if (!ap1->ap_netaddr) {
! 456: yyerror("inbound network segment is not specified");
! 457: }
! 458: am1 = npfctl_get_singlefam(ap1->ap_netaddr);
! 459: if (am1->fam_family != AF_INET) {
! 460: yyerror("IPv6 NAT is not supported");
! 461: }
! 462: assert(am1 != NULL);
! 463: } else
! 464: am1 = NULL;
! 465:
! 466: if (type & NPF_NATOUT) {
! 467: if (!ap2->ap_netaddr) {
! 468: yyerror("outbound network segment is not specified");
! 469: }
! 470: am2 = npfctl_get_singlefam(ap2->ap_netaddr);
! 471: if (am2->fam_family != family) {
! 472: yyerror("IPv6 NAT is not supported");
! 473: }
! 474: assert(am2 != NULL);
! 475: } else
! 476: am2 = NULL;
! 477:
! 478: /*
! 479: * If filter criteria is not specified explicitly, apply implicit
! 480: * filtering according to the given network segements.
! 481: */
! 482: if (!fopts) {
! 483: memset(&imfopts, 0, sizeof(filt_opts_t));
! 484: if (type & NPF_NATOUT) {
! 485: memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t));
! 486: }
! 487: if (type & NPF_NATIN) {
! 488: memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t));
! 489: }
! 490: fopts = &imfopts;
1.1 rmind 491: }
492:
493: switch (type) {
1.4.2.2 ! riz 494: case NPF_NATIN:
! 495: assert(am1 != NULL);
1.1 rmind 496: /*
497: * Redirection: an inbound NAT with a specific port.
498: */
1.4.2.2 ! riz 499: if (!ap1->ap_portrange) {
! 500: yyerror("inbound port is not specified");
! 501: }
! 502: in_port_t port = npfctl_get_singleport(ap1->ap_portrange);
1.1 rmind 503: nat = npf_nat_create(NPF_NATIN, NPF_NAT_PORTS,
1.4.2.2 ! riz 504: if_idx, &am1->fam_addr, am1->fam_family, port);
1.1 rmind 505: break;
1.4.2.2 ! riz 506:
! 507: case (NPF_NATIN | NPF_NATOUT):
! 508: assert(am1 != NULL);
1.1 rmind 509: /*
510: * Bi-directional NAT: a combination of inbound NAT and
511: * outbound NAT policies. Note that the translation address
512: * is local IP and filter criteria is inverted accordingly.
513: */
514: nat = npf_nat_create(NPF_NATIN, 0, if_idx,
1.4.2.2 ! riz 515: &am1->fam_addr, am1->fam_family, 0);
! 516: npfctl_build_ncode(nat, family, &op, fopts, true);
1.1 rmind 517: npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
518: /* FALLTHROUGH */
1.4.2.2 ! riz 519:
! 520: case NPF_NATOUT:
! 521: assert(am2 != NULL);
1.1 rmind 522: /*
523: * Traditional NAPT: an outbound NAT policy with port.
1.4.2.2 ! riz 524: * If this is another half for bi-directional NAT, then
1.1 rmind 525: * no port translation with mapping.
526: */
1.4.2.2 ! riz 527: nat = npf_nat_create(NPF_NATOUT, type == NPF_NATOUT ?
1.1 rmind 528: (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0,
1.4.2.2 ! riz 529: if_idx, &am2->fam_addr, am2->fam_family, 0);
1.1 rmind 530: break;
1.4.2.2 ! riz 531:
1.1 rmind 532: default:
533: assert(false);
534: }
1.4.2.2 ! riz 535: npfctl_build_ncode(nat, family, &op, fopts, false);
1.1 rmind 536: npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
537: }
538:
539: /*
540: * npfctl_fill_table: fill NPF table with entries from a specified file.
541: */
542: static void
543: npfctl_fill_table(nl_table_t *tl, const char *fname)
544: {
545: char *buf = NULL;
546: int l = 0;
547: FILE *fp;
548: size_t n;
549:
550: fp = fopen(fname, "r");
551: if (fp == NULL) {
552: err(EXIT_FAILURE, "open '%s'", fname);
553: }
554: while (l++, getline(&buf, &n, fp) != -1) {
555: fam_addr_mask_t *fam;
556:
557: if (*buf == '\n' || *buf == '#') {
558: continue;
559: }
560: fam = npfctl_parse_cidr(buf);
561: if (fam == NULL) {
562: errx(EXIT_FAILURE, "%s:%d: invalid table entry",
563: fname, l);
564: }
565:
566: /* Create and add a table entry. */
567: npf_table_add_entry(tl, &fam->fam_addr, fam->fam_mask);
568: }
569: if (buf != NULL) {
570: free(buf);
571: }
572: }
573:
574: /*
575: * npfctl_build_table: create an NPF table, add to the configuration and,
576: * if required, fill with contents from a file.
577: */
578: void
579: npfctl_build_table(const char *tid, u_int type, const char *fname)
580: {
581: nl_table_t *tl;
582: u_int id;
583:
584: id = atoi(tid);
585: tl = npf_table_create(id, type);
586: assert(tl != NULL);
587:
588: if (npf_table_insert(npf_conf, tl)) {
589: errx(EXIT_FAILURE, "table '%d' is already defined\n", id);
590: }
591:
592: if (fname) {
593: npfctl_fill_table(tl, fname);
594: }
595: }
CVSweb <webmaster@jp.NetBSD.org>