xref: /netbsd-src/lib/libnpf/npf.c (revision 9ddb6ab554e70fb9bbd90c3d96b812bc57755a14)
1 /*	$NetBSD: npf.c,v 1.7 2012/02/05 00:37:13 rmind Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010-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 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.7 2012/02/05 00:37:13 rmind Exp $");
34 
35 #include <sys/types.h>
36 #include <netinet/in_systm.h>
37 #include <netinet/in.h>
38 #include <prop/proplib.h>
39 
40 #include <stdlib.h>
41 #include <string.h>
42 #include <assert.h>
43 #include <errno.h>
44 #include <err.h>
45 
46 #define	_NPF_PRIVATE
47 #include "npf.h"
48 
49 struct nl_config {
50 	/* Rules, translations, tables, procedures. */
51 	prop_array_t		ncf_rules_list;
52 	prop_array_t		ncf_rproc_list;
53 	prop_array_t		ncf_table_list;
54 	prop_array_t		ncf_nat_list;
55 	/* Priority counters. */
56 	pri_t			ncf_rule_pri;
57 	pri_t			ncf_nat_pri;
58 	/* Error report. */
59 	prop_dictionary_t	ncf_err;
60 	/* Custom file to externalise property-list. */
61 	const char *		ncf_plist;
62 	bool			ncf_flush;
63 };
64 
65 struct nl_rule {
66 	prop_dictionary_t	nrl_dict;
67 };
68 
69 struct nl_rproc {
70 	prop_dictionary_t	nrp_dict;
71 };
72 
73 struct nl_table {
74 	prop_dictionary_t	ntl_dict;
75 };
76 
77 /*
78  * CONFIGURATION INTERFACE.
79  */
80 
81 nl_config_t *
82 npf_config_create(void)
83 {
84 	nl_config_t *ncf;
85 
86 	ncf = calloc(1, sizeof(*ncf));
87 	if (ncf == NULL) {
88 		return NULL;
89 	}
90 	ncf->ncf_rules_list = prop_array_create();
91 	ncf->ncf_rproc_list = prop_array_create();
92 	ncf->ncf_table_list = prop_array_create();
93 	ncf->ncf_nat_list = prop_array_create();
94 
95 	ncf->ncf_rule_pri = 1;
96 	ncf->ncf_nat_pri = 1;
97 
98 	ncf->ncf_plist = NULL;
99 	ncf->ncf_flush = false;
100 
101 	return ncf;
102 }
103 
104 int
105 npf_config_submit(nl_config_t *ncf, int fd)
106 {
107 	const char *plist = ncf->ncf_plist;
108 	prop_dictionary_t npf_dict;
109 	int error = 0;
110 
111 	npf_dict = prop_dictionary_create();
112 	if (npf_dict == NULL) {
113 		return ENOMEM;
114 	}
115 	prop_dictionary_set(npf_dict, "rules", ncf->ncf_rules_list);
116 	prop_dictionary_set(npf_dict, "rprocs", ncf->ncf_rproc_list);
117 	prop_dictionary_set(npf_dict, "tables", ncf->ncf_table_list);
118 	prop_dictionary_set(npf_dict, "translation", ncf->ncf_nat_list);
119 	prop_dictionary_set_bool(npf_dict, "flush", ncf->ncf_flush);
120 
121 	if (plist) {
122 		if (!prop_dictionary_externalize_to_file(npf_dict, plist)) {
123 			error = errno;
124 		}
125 		prop_object_release(npf_dict);
126 		return error;
127 	}
128 
129 	error = prop_dictionary_sendrecv_ioctl(npf_dict, fd,
130 	    IOC_NPF_RELOAD, &ncf->ncf_err);
131 	if (error) {
132 		prop_object_release(npf_dict);
133 		assert(ncf->ncf_err == NULL);
134 		return error;
135 	}
136 
137 	prop_dictionary_get_int32(ncf->ncf_err, "errno", &error);
138 	prop_object_release(npf_dict);
139 	return error;
140 }
141 
142 int
143 npf_config_flush(int fd)
144 {
145 	nl_config_t *ncf;
146 	int error;
147 
148 	ncf = npf_config_create();
149 	if (ncf == NULL) {
150 		return ENOMEM;
151 	}
152 	ncf->ncf_flush = true;
153 	error = npf_config_submit(ncf, fd);
154 	npf_config_destroy(ncf);
155 	return error;
156 }
157 
158 void
159 _npf_config_error(nl_config_t *ncf, nl_error_t *ne)
160 {
161 	memset(ne, 0, sizeof(*ne));
162 	prop_dictionary_get_int32(ncf->ncf_err, "id", &ne->ne_id);
163 	prop_dictionary_get_cstring(ncf->ncf_err,
164 	    "source-file", &ne->ne_source_file);
165 	prop_dictionary_get_uint32(ncf->ncf_err,
166 	    "source-line", &ne->ne_source_line);
167 	prop_dictionary_get_int32(ncf->ncf_err,
168 	    "ncode-error", &ne->ne_ncode_error);
169 	prop_dictionary_get_int32(ncf->ncf_err,
170 	    "ncode-errat", &ne->ne_ncode_errat);
171 }
172 
173 void
174 npf_config_destroy(nl_config_t *ncf)
175 {
176 
177 	prop_object_release(ncf->ncf_rules_list);
178 	prop_object_release(ncf->ncf_rproc_list);
179 	prop_object_release(ncf->ncf_table_list);
180 	prop_object_release(ncf->ncf_nat_list);
181 	if (ncf->ncf_err) {
182 		prop_object_release(ncf->ncf_err);
183 	}
184 	free(ncf);
185 }
186 
187 void
188 _npf_config_setsubmit(nl_config_t *ncf, const char *plist_file)
189 {
190 
191 	ncf->ncf_plist = plist_file;
192 }
193 
194 static bool
195 _npf_prop_array_lookup(prop_array_t array, const char *key, const char *name)
196 {
197 	prop_dictionary_t dict;
198 	prop_object_iterator_t it;
199 
200 	it = prop_array_iterator(array);
201 	while ((dict = prop_object_iterator_next(it)) != NULL) {
202 		const char *lname;
203 		prop_dictionary_get_cstring_nocopy(dict, key, &lname);
204 		if (strcmp(name, lname) == 0)
205 			break;
206 	}
207 	prop_object_iterator_release(it);
208 	return dict ? true : false;
209 }
210 
211 /*
212  * RULE INTERFACE.
213  */
214 
215 nl_rule_t *
216 npf_rule_create(const char *name, uint32_t attr, u_int if_idx)
217 {
218 	prop_dictionary_t rldict;
219 	nl_rule_t *rl;
220 
221 	rl = malloc(sizeof(*rl));
222 	if (rl == NULL) {
223 		return NULL;
224 	}
225 	rldict = prop_dictionary_create();
226 	if (rldict == NULL) {
227 		free(rl);
228 		return NULL;
229 	}
230 	if (name) {
231 		prop_dictionary_set_cstring(rldict, "name", name);
232 	}
233 	prop_dictionary_set_uint32(rldict, "attributes", attr);
234 
235 	if (if_idx) {
236 		prop_dictionary_set_uint32(rldict, "interface", if_idx);
237 	}
238 	rl->nrl_dict = rldict;
239 	return rl;
240 }
241 
242 int
243 npf_rule_setcode(nl_rule_t *rl, int type, const void *code, size_t sz)
244 {
245 	prop_dictionary_t rldict = rl->nrl_dict;
246 	prop_data_t cdata;
247 
248 	if (type != NPF_CODE_NCODE) {
249 		return ENOTSUP;
250 	}
251 	cdata = prop_data_create_data(code, sz);
252 	if (cdata == NULL) {
253 		return ENOMEM;
254 	}
255 	prop_dictionary_set(rldict, "ncode", cdata);
256 	prop_object_release(cdata);
257 	return 0;
258 }
259 
260 int
261 npf_rule_setproc(nl_config_t *ncf, nl_rule_t *rl, const char *name)
262 {
263 	prop_dictionary_t rldict = rl->nrl_dict;
264 
265 	if (!npf_rproc_exists_p(ncf, name)) {
266 		return ENOENT;
267 	}
268 	prop_dictionary_set_cstring(rldict, "rproc", name);
269 	return 0;
270 }
271 
272 bool
273 npf_rule_exists_p(nl_config_t *ncf, const char *name)
274 {
275 
276 	return _npf_prop_array_lookup(ncf->ncf_rules_list, "name", name);
277 }
278 
279 int
280 npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl, pri_t pri)
281 {
282 	prop_dictionary_t rldict = rl->nrl_dict;
283 	prop_array_t rlset;
284 
285 	if (pri == NPF_PRI_NEXT) {
286 		pri = ncf->ncf_rule_pri++;
287 	} else if (ncf) {
288 		ncf->ncf_rule_pri = pri + 1;
289 	}
290 	prop_dictionary_set_int32(rldict, "priority", pri);
291 
292 	if (parent) {
293 		prop_dictionary_t pdict = parent->nrl_dict;
294 		rlset = prop_dictionary_get(pdict, "subrules");
295 		if (rlset == NULL) {
296 			rlset = prop_array_create();
297 			prop_dictionary_set(pdict, "subrules", rlset);
298 			prop_object_release(rlset);
299 		}
300 	} else {
301 		rlset = ncf->ncf_rules_list;
302 	}
303 	prop_array_add(rlset, rldict);
304 	return 0;
305 }
306 
307 void
308 npf_rule_destroy(nl_rule_t *rl)
309 {
310 
311 	prop_object_release(rl->nrl_dict);
312 	free(rl);
313 }
314 
315 /*
316  * RULE PROCEDURE INTERFACE.
317  */
318 
319 nl_rproc_t *
320 npf_rproc_create(const char *name)
321 {
322 	prop_dictionary_t rpdict;
323 	nl_rproc_t *nrp;
324 
325 	nrp = malloc(sizeof(nl_rproc_t));
326 	if (nrp == NULL) {
327 		return NULL;
328 	}
329 	rpdict = prop_dictionary_create();
330 	if (rpdict == NULL) {
331 		free(nrp);
332 		return NULL;
333 	}
334 	prop_dictionary_set_cstring(rpdict, "name", name);
335 	nrp->nrp_dict = rpdict;
336 	return nrp;
337 }
338 
339 bool
340 npf_rproc_exists_p(nl_config_t *ncf, const char *name)
341 {
342 
343 	return _npf_prop_array_lookup(ncf->ncf_rproc_list, "name", name);
344 }
345 
346 int
347 _npf_rproc_setnorm(nl_rproc_t *rp, bool rnd, bool no_df, u_int minttl,
348     u_int maxmss)
349 {
350 	prop_dictionary_t rpdict = rp->nrp_dict;
351 	uint32_t fl;
352 
353 	prop_dictionary_set_bool(rpdict, "randomize-id", rnd);
354 	prop_dictionary_set_bool(rpdict, "no-df", no_df);
355 	prop_dictionary_set_uint32(rpdict, "min-ttl", minttl);
356 	prop_dictionary_set_uint32(rpdict, "max-mss", maxmss);
357 
358 	prop_dictionary_get_uint32(rpdict, "flags", &fl);
359 	prop_dictionary_set_uint32(rpdict, "flags", fl | NPF_RPROC_NORMALIZE);
360 	return 0;
361 }
362 
363 int
364 _npf_rproc_setlog(nl_rproc_t *rp, u_int if_idx)
365 {
366 	prop_dictionary_t rpdict = rp->nrp_dict;
367 	uint32_t fl;
368 
369 	prop_dictionary_set_uint32(rpdict, "log-interface", if_idx);
370 
371 	prop_dictionary_get_uint32(rpdict, "flags", &fl);
372 	prop_dictionary_set_uint32(rpdict, "flags", fl | NPF_RPROC_LOG);
373 	return 0;
374 }
375 
376 int
377 npf_rproc_insert(nl_config_t *ncf, nl_rproc_t *rp)
378 {
379 	prop_dictionary_t rpdict = rp->nrp_dict;
380 	const char *name;
381 
382 	if (!prop_dictionary_get_cstring_nocopy(rpdict, "name", &name)) {
383 		return EINVAL;
384 	}
385 	if (npf_rproc_exists_p(ncf, name)) {
386 		return EEXIST;
387 	}
388 	prop_array_add(ncf->ncf_rproc_list, rpdict);
389 	return 0;
390 }
391 
392 /*
393  * TRANSLATION INTERFACE.
394  */
395 
396 nl_nat_t *
397 npf_nat_create(int type, u_int flags, u_int if_idx,
398     npf_addr_t *addr, int af, in_port_t port)
399 {
400 	nl_rule_t *rl;
401 	prop_dictionary_t rldict;
402 	prop_data_t addrdat;
403 	uint32_t attr;
404 	size_t sz;
405 
406 	if (af == AF_INET) {
407 		sz = sizeof(struct in_addr);
408 	} else if (af == AF_INET6) {
409 		sz = sizeof(struct in6_addr);
410 	} else {
411 		return NULL;
412 	}
413 
414 	attr = NPF_RULE_PASS | NPF_RULE_FINAL |
415 	    (type == NPF_NATOUT ? NPF_RULE_OUT : NPF_RULE_IN);
416 
417 	/* Create a rule for NAT policy.  Next, will add translation data. */
418 	rl = npf_rule_create(NULL, attr, if_idx);
419 	if (rl == NULL) {
420 		return NULL;
421 	}
422 	rldict = rl->nrl_dict;
423 
424 	/* Translation type and flags. */
425 	prop_dictionary_set_int32(rldict, "type", type);
426 	prop_dictionary_set_uint32(rldict, "flags", flags);
427 
428 	/* Translation IP. */
429 	addrdat = prop_data_create_data(addr, sz);
430 	if (addrdat == NULL) {
431 		npf_rule_destroy(rl);
432 		return NULL;
433 	}
434 	prop_dictionary_set(rldict, "translation-ip", addrdat);
435 	prop_object_release(addrdat);
436 
437 	/* Translation port (for redirect case). */
438 	prop_dictionary_set_uint16(rldict, "translation-port", port);
439 
440 	return (nl_nat_t *)rl;
441 }
442 
443 int
444 npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt, pri_t pri)
445 {
446 	prop_dictionary_t rldict = nt->nrl_dict;
447 
448 	if (pri == NPF_PRI_NEXT) {
449 		pri = ncf->ncf_nat_pri++;
450 	} else {
451 		ncf->ncf_nat_pri = pri + 1;
452 	}
453 	prop_dictionary_set_int32(rldict, "priority", pri);
454 	prop_array_add(ncf->ncf_nat_list, rldict);
455 	return 0;
456 }
457 
458 /*
459  * TABLE INTERFACE.
460  */
461 
462 nl_table_t *
463 npf_table_create(u_int id, int type)
464 {
465 	prop_dictionary_t tldict;
466 	prop_array_t tblents;
467 	nl_table_t *tl;
468 
469 	tl = malloc(sizeof(*tl));
470 	if (tl == NULL) {
471 		return NULL;
472 	}
473 	tldict = prop_dictionary_create();
474 	if (tldict == NULL) {
475 		free(tl);
476 		return NULL;
477 	}
478 	prop_dictionary_set_uint32(tldict, "id", id);
479 	prop_dictionary_set_int32(tldict, "type", type);
480 
481 	tblents = prop_array_create();
482 	if (tblents == NULL) {
483 		prop_object_release(tldict);
484 		free(tl);
485 		return NULL;
486 	}
487 	prop_dictionary_set(tldict, "entries", tblents);
488 	prop_object_release(tblents);
489 
490 	tl->ntl_dict = tldict;
491 	return tl;
492 }
493 
494 int
495 npf_table_add_entry(nl_table_t *tl, npf_addr_t *addr, npf_netmask_t mask)
496 {
497 	prop_dictionary_t tldict = tl->ntl_dict, entdict;
498 	prop_array_t tblents;
499 	prop_data_t addrdata;
500 
501 	/* Create the table entry. */
502 	entdict = prop_dictionary_create();
503 	if (entdict) {
504 		return ENOMEM;
505 	}
506 	addrdata = prop_data_create_data(addr, sizeof(npf_addr_t));
507 	prop_dictionary_set(entdict, "addr", addrdata);
508 	prop_dictionary_set_uint8(entdict, "mask", mask);
509 	prop_object_release(addrdata);
510 
511 	/* Insert the entry. */
512 	tblents = prop_dictionary_get(tldict, "entries");
513 	prop_array_add(tblents, entdict);
514 	prop_object_release(entdict);
515 	return 0;
516 }
517 
518 bool
519 npf_table_exists_p(nl_config_t *ncf, u_int tid)
520 {
521 	prop_dictionary_t tldict;
522 	prop_object_iterator_t it;
523 
524 	it = prop_array_iterator(ncf->ncf_table_list);
525 	while ((tldict = prop_object_iterator_next(it)) != NULL) {
526 		u_int i;
527 		if (prop_dictionary_get_uint32(tldict, "id", &i) && tid == i)
528 			break;
529 	}
530 	prop_object_iterator_release(it);
531 	return tldict ? true : false;
532 }
533 
534 int
535 npf_table_insert(nl_config_t *ncf, nl_table_t *tl)
536 {
537 	prop_dictionary_t tldict = tl->ntl_dict;
538 	u_int tid;
539 
540 	if (!prop_dictionary_get_uint32(tldict, "id", &tid)) {
541 		return EINVAL;
542 	}
543 	if (npf_table_exists_p(ncf, tid)) {
544 		return EEXIST;
545 	}
546 	prop_array_add(ncf->ncf_table_list, tldict);
547 	return 0;
548 }
549 
550 void
551 npf_table_destroy(nl_table_t *tl)
552 {
553 
554 	prop_object_release(tl->ntl_dict);
555 	free(tl);
556 }
557 
558 /*
559  * MISC.
560  */
561 
562 int
563 npf_update_rule(int fd, const char *rname __unused, nl_rule_t *rl)
564 {
565 	prop_dictionary_t rldict = rl->nrl_dict, errdict = NULL;
566 	int error;
567 
568 	error = prop_dictionary_sendrecv_ioctl(rldict, fd,
569 	    IOC_NPF_UPDATE_RULE, &errdict);
570 	if (errdict) {
571 		prop_object_release(errdict);
572 	}
573 	return error;
574 }
575 
576 int
577 npf_sessions_recv(int fd, const char *fpath)
578 {
579 	prop_dictionary_t sdict;
580 	int error;
581 
582 	error = prop_dictionary_recv_ioctl(fd, IOC_NPF_SESSIONS_SAVE, &sdict);
583 	if (error) {
584 		return error;
585 	}
586 	if (!prop_dictionary_externalize_to_file(sdict, fpath)) {
587 		error = errno;
588 	}
589 	prop_object_release(sdict);
590 	return error;
591 }
592 
593 int
594 npf_sessions_send(int fd, const char *fpath)
595 {
596 	prop_dictionary_t sdict;
597 	int error;
598 
599 	if (fpath) {
600 		sdict = prop_dictionary_internalize_from_file(fpath);
601 		if (sdict == NULL) {
602 			return errno;
603 		}
604 	} else {
605 		/* Empty: will flush the sessions. */
606 		prop_array_t selist = prop_array_create();
607 		sdict = prop_dictionary_create();
608 		prop_dictionary_set(sdict, "session-list", selist);
609 		prop_object_release(selist);
610 	}
611 	error = prop_dictionary_send_ioctl(sdict, fd, IOC_NPF_SESSIONS_LOAD);
612 	prop_object_release(sdict);
613 	return error;
614 }
615