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