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