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