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