ip_fw_sockopt.c revision 304079
1/*- 2 * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa 3 * Copyright (c) 2014 Yandex LLC 4 * Copyright (c) 2014 Alexander V. Chernikov 5 * 6 * Supported by: Valeria Paoli 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/ip_fw_sockopt.c 304079 2016-08-14 14:50:32Z ae $"); 32 33/* 34 * Control socket and rule management routines for ipfw. 35 * Control is currently implemented via IP_FW3 setsockopt() code. 36 */ 37 38#include "opt_ipfw.h" 39#include "opt_inet.h" 40#ifndef INET 41#error IPFIREWALL requires INET. 42#endif /* INET */ 43#include "opt_inet6.h" 44 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/malloc.h> 48#include <sys/mbuf.h> /* struct m_tag used by nested headers */ 49#include <sys/kernel.h> 50#include <sys/lock.h> 51#include <sys/priv.h> 52#include <sys/proc.h> 53#include <sys/rwlock.h> 54#include <sys/rmlock.h> 55#include <sys/socket.h> 56#include <sys/socketvar.h> 57#include <sys/sysctl.h> 58#include <sys/syslog.h> 59#include <sys/fnv_hash.h> 60#include <net/if.h> 61#include <net/route.h> 62#include <net/vnet.h> 63#include <vm/vm.h> 64#include <vm/vm_extern.h> 65 66#include <netinet/in.h> 67#include <netinet/ip_var.h> /* hooks */ 68#include <netinet/ip_fw.h> 69 70#include <netpfil/ipfw/ip_fw_private.h> 71#include <netpfil/ipfw/ip_fw_table.h> 72 73#ifdef MAC 74#include <security/mac/mac_framework.h> 75#endif 76 77static int ipfw_ctl(struct sockopt *sopt); 78static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, 79 struct rule_check_info *ci); 80static int check_ipfw_rule1(struct ip_fw_rule *rule, int size, 81 struct rule_check_info *ci); 82static int check_ipfw_rule0(struct ip_fw_rule0 *rule, int size, 83 struct rule_check_info *ci); 84static int rewrite_rule_uidx(struct ip_fw_chain *chain, 85 struct rule_check_info *ci); 86 87#define NAMEDOBJ_HASH_SIZE 32 88 89struct namedobj_instance { 90 struct namedobjects_head *names; 91 struct namedobjects_head *values; 92 uint32_t nn_size; /* names hash size */ 93 uint32_t nv_size; /* number hash size */ 94 u_long *idx_mask; /* used items bitmask */ 95 uint32_t max_blocks; /* number of "long" blocks in bitmask */ 96 uint32_t count; /* number of items */ 97 uint16_t free_off[IPFW_MAX_SETS]; /* first possible free offset */ 98 objhash_hash_f *hash_f; 99 objhash_cmp_f *cmp_f; 100}; 101#define BLOCK_ITEMS (8 * sizeof(u_long)) /* Number of items for ffsl() */ 102 103static uint32_t objhash_hash_name(struct namedobj_instance *ni, 104 const void *key, uint32_t kopt); 105static uint32_t objhash_hash_idx(struct namedobj_instance *ni, uint32_t val); 106static int objhash_cmp_name(struct named_object *no, const void *name, 107 uint32_t set); 108 109MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); 110 111static int dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 112 struct sockopt_data *sd); 113static int add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 114 struct sockopt_data *sd); 115static int del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 116 struct sockopt_data *sd); 117static int clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 118 struct sockopt_data *sd); 119static int move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 120 struct sockopt_data *sd); 121static int manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 122 struct sockopt_data *sd); 123static int dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 124 struct sockopt_data *sd); 125static int dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 126 struct sockopt_data *sd); 127 128/* ctl3 handler data */ 129struct mtx ctl3_lock; 130#define CTL3_LOCK_INIT() mtx_init(&ctl3_lock, "ctl3_lock", NULL, MTX_DEF) 131#define CTL3_LOCK_DESTROY() mtx_destroy(&ctl3_lock) 132#define CTL3_LOCK() mtx_lock(&ctl3_lock) 133#define CTL3_UNLOCK() mtx_unlock(&ctl3_lock) 134 135static struct ipfw_sopt_handler *ctl3_handlers; 136static size_t ctl3_hsize; 137static uint64_t ctl3_refct, ctl3_gencnt; 138#define CTL3_SMALLBUF 4096 /* small page-size write buffer */ 139#define CTL3_LARGEBUF 16 * 1024 * 1024 /* handle large rulesets */ 140 141static int ipfw_flush_sopt_data(struct sockopt_data *sd); 142 143static struct ipfw_sopt_handler scodes[] = { 144 { IP_FW_XGET, 0, HDIR_GET, dump_config }, 145 { IP_FW_XADD, 0, HDIR_BOTH, add_rules }, 146 { IP_FW_XDEL, 0, HDIR_BOTH, del_rules }, 147 { IP_FW_XZERO, 0, HDIR_SET, clear_rules }, 148 { IP_FW_XRESETLOG, 0, HDIR_SET, clear_rules }, 149 { IP_FW_XMOVE, 0, HDIR_SET, move_rules }, 150 { IP_FW_SET_SWAP, 0, HDIR_SET, manage_sets }, 151 { IP_FW_SET_MOVE, 0, HDIR_SET, manage_sets }, 152 { IP_FW_SET_ENABLE, 0, HDIR_SET, manage_sets }, 153 { IP_FW_DUMP_SOPTCODES, 0, HDIR_GET, dump_soptcodes }, 154 { IP_FW_DUMP_SRVOBJECTS,0, HDIR_GET, dump_srvobjects }, 155}; 156 157static int 158set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule); 159static struct opcode_obj_rewrite *find_op_rw(ipfw_insn *cmd, 160 uint16_t *puidx, uint8_t *ptype); 161static int mark_object_kidx(struct ip_fw_chain *ch, struct ip_fw *rule, 162 uint32_t *bmask); 163static int ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule, 164 struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti); 165static int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, 166 struct tid_info *ti, struct obj_idx *pidx, int *unresolved); 167static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule); 168static void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, 169 struct obj_idx *oib, struct obj_idx *end); 170static int export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx, 171 struct sockopt_data *sd); 172 173/* 174 * Opcode object rewriter variables 175 */ 176struct opcode_obj_rewrite *ctl3_rewriters; 177static size_t ctl3_rsize; 178 179/* 180 * static variables followed by global ones 181 */ 182 183static VNET_DEFINE(uma_zone_t, ipfw_cntr_zone); 184#define V_ipfw_cntr_zone VNET(ipfw_cntr_zone) 185 186void 187ipfw_init_counters() 188{ 189 190 V_ipfw_cntr_zone = uma_zcreate("IPFW counters", 191 IPFW_RULE_CNTR_SIZE, NULL, NULL, NULL, NULL, 192 UMA_ALIGN_PTR, UMA_ZONE_PCPU); 193} 194 195void 196ipfw_destroy_counters() 197{ 198 199 uma_zdestroy(V_ipfw_cntr_zone); 200} 201 202struct ip_fw * 203ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize) 204{ 205 struct ip_fw *rule; 206 207 rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO); 208 rule->cntr = uma_zalloc(V_ipfw_cntr_zone, M_WAITOK | M_ZERO); 209 210 return (rule); 211} 212 213static void 214free_rule(struct ip_fw *rule) 215{ 216 217 uma_zfree(V_ipfw_cntr_zone, rule->cntr); 218 free(rule, M_IPFW); 219} 220 221 222/* 223 * Find the smallest rule >= key, id. 224 * We could use bsearch but it is so simple that we code it directly 225 */ 226int 227ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id) 228{ 229 int i, lo, hi; 230 struct ip_fw *r; 231 232 for (lo = 0, hi = chain->n_rules - 1; lo < hi;) { 233 i = (lo + hi) / 2; 234 r = chain->map[i]; 235 if (r->rulenum < key) 236 lo = i + 1; /* continue from the next one */ 237 else if (r->rulenum > key) 238 hi = i; /* this might be good */ 239 else if (r->id < id) 240 lo = i + 1; /* continue from the next one */ 241 else /* r->id >= id */ 242 hi = i; /* this might be good */ 243 } 244 return hi; 245} 246 247/* 248 * Builds skipto cache on rule set @map. 249 */ 250static void 251update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map) 252{ 253 int *smap, rulenum; 254 int i, mi; 255 256 IPFW_UH_WLOCK_ASSERT(chain); 257 258 mi = 0; 259 rulenum = map[mi]->rulenum; 260 smap = chain->idxmap_back; 261 262 if (smap == NULL) 263 return; 264 265 for (i = 0; i < 65536; i++) { 266 smap[i] = mi; 267 /* Use the same rule index until i < rulenum */ 268 if (i != rulenum || i == 65535) 269 continue; 270 /* Find next rule with num > i */ 271 rulenum = map[++mi]->rulenum; 272 while (rulenum == i) 273 rulenum = map[++mi]->rulenum; 274 } 275} 276 277/* 278 * Swaps prepared (backup) index with current one. 279 */ 280static void 281swap_skipto_cache(struct ip_fw_chain *chain) 282{ 283 int *map; 284 285 IPFW_UH_WLOCK_ASSERT(chain); 286 IPFW_WLOCK_ASSERT(chain); 287 288 map = chain->idxmap; 289 chain->idxmap = chain->idxmap_back; 290 chain->idxmap_back = map; 291} 292 293/* 294 * Allocate and initialize skipto cache. 295 */ 296void 297ipfw_init_skipto_cache(struct ip_fw_chain *chain) 298{ 299 int *idxmap, *idxmap_back; 300 301 idxmap = malloc(65536 * sizeof(uint32_t *), M_IPFW, 302 M_WAITOK | M_ZERO); 303 idxmap_back = malloc(65536 * sizeof(uint32_t *), M_IPFW, 304 M_WAITOK | M_ZERO); 305 306 /* 307 * Note we may be called at any time after initialization, 308 * for example, on first skipto rule, so we need to 309 * provide valid chain->idxmap on return 310 */ 311 312 IPFW_UH_WLOCK(chain); 313 if (chain->idxmap != NULL) { 314 IPFW_UH_WUNLOCK(chain); 315 free(idxmap, M_IPFW); 316 free(idxmap_back, M_IPFW); 317 return; 318 } 319 320 /* Set backup pointer first to permit building cache */ 321 chain->idxmap_back = idxmap_back; 322 update_skipto_cache(chain, chain->map); 323 IPFW_WLOCK(chain); 324 /* It is now safe to set chain->idxmap ptr */ 325 chain->idxmap = idxmap; 326 swap_skipto_cache(chain); 327 IPFW_WUNLOCK(chain); 328 IPFW_UH_WUNLOCK(chain); 329} 330 331/* 332 * Destroys skipto cache. 333 */ 334void 335ipfw_destroy_skipto_cache(struct ip_fw_chain *chain) 336{ 337 338 if (chain->idxmap != NULL) 339 free(chain->idxmap, M_IPFW); 340 if (chain->idxmap != NULL) 341 free(chain->idxmap_back, M_IPFW); 342} 343 344 345/* 346 * allocate a new map, returns the chain locked. extra is the number 347 * of entries to add or delete. 348 */ 349static struct ip_fw ** 350get_map(struct ip_fw_chain *chain, int extra, int locked) 351{ 352 353 for (;;) { 354 struct ip_fw **map; 355 int i, mflags; 356 357 mflags = M_ZERO | ((locked != 0) ? M_NOWAIT : M_WAITOK); 358 359 i = chain->n_rules + extra; 360 map = malloc(i * sizeof(struct ip_fw *), M_IPFW, mflags); 361 if (map == NULL) { 362 printf("%s: cannot allocate map\n", __FUNCTION__); 363 return NULL; 364 } 365 if (!locked) 366 IPFW_UH_WLOCK(chain); 367 if (i >= chain->n_rules + extra) /* good */ 368 return map; 369 /* otherwise we lost the race, free and retry */ 370 if (!locked) 371 IPFW_UH_WUNLOCK(chain); 372 free(map, M_IPFW); 373 } 374} 375 376/* 377 * swap the maps. It is supposed to be called with IPFW_UH_WLOCK 378 */ 379static struct ip_fw ** 380swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len) 381{ 382 struct ip_fw **old_map; 383 384 IPFW_WLOCK(chain); 385 chain->id++; 386 chain->n_rules = new_len; 387 old_map = chain->map; 388 chain->map = new_map; 389 swap_skipto_cache(chain); 390 IPFW_WUNLOCK(chain); 391 return old_map; 392} 393 394 395static void 396export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr) 397{ 398 399 cntr->size = sizeof(*cntr); 400 401 if (krule->cntr != NULL) { 402 cntr->pcnt = counter_u64_fetch(krule->cntr); 403 cntr->bcnt = counter_u64_fetch(krule->cntr + 1); 404 cntr->timestamp = krule->timestamp; 405 } 406 if (cntr->timestamp > 0) 407 cntr->timestamp += boottime.tv_sec; 408} 409 410static void 411export_cntr0_base(struct ip_fw *krule, struct ip_fw_bcounter0 *cntr) 412{ 413 414 if (krule->cntr != NULL) { 415 cntr->pcnt = counter_u64_fetch(krule->cntr); 416 cntr->bcnt = counter_u64_fetch(krule->cntr + 1); 417 cntr->timestamp = krule->timestamp; 418 } 419 if (cntr->timestamp > 0) 420 cntr->timestamp += boottime.tv_sec; 421} 422 423/* 424 * Copies rule @urule from v1 userland format (current). 425 * to kernel @krule. 426 * Assume @krule is zeroed. 427 */ 428static void 429import_rule1(struct rule_check_info *ci) 430{ 431 struct ip_fw_rule *urule; 432 struct ip_fw *krule; 433 434 urule = (struct ip_fw_rule *)ci->urule; 435 krule = (struct ip_fw *)ci->krule; 436 437 /* copy header */ 438 krule->act_ofs = urule->act_ofs; 439 krule->cmd_len = urule->cmd_len; 440 krule->rulenum = urule->rulenum; 441 krule->set = urule->set; 442 krule->flags = urule->flags; 443 444 /* Save rulenum offset */ 445 ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum); 446 447 /* Copy opcodes */ 448 memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); 449} 450 451/* 452 * Export rule into v1 format (Current). 453 * Layout: 454 * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT) 455 * [ ip_fw_rule ] OR 456 * [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs). 457 * ] 458 * Assume @data is zeroed. 459 */ 460static void 461export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs) 462{ 463 struct ip_fw_bcounter *cntr; 464 struct ip_fw_rule *urule; 465 ipfw_obj_tlv *tlv; 466 467 /* Fill in TLV header */ 468 tlv = (ipfw_obj_tlv *)data; 469 tlv->type = IPFW_TLV_RULE_ENT; 470 tlv->length = len; 471 472 if (rcntrs != 0) { 473 /* Copy counters */ 474 cntr = (struct ip_fw_bcounter *)(tlv + 1); 475 urule = (struct ip_fw_rule *)(cntr + 1); 476 export_cntr1_base(krule, cntr); 477 } else 478 urule = (struct ip_fw_rule *)(tlv + 1); 479 480 /* copy header */ 481 urule->act_ofs = krule->act_ofs; 482 urule->cmd_len = krule->cmd_len; 483 urule->rulenum = krule->rulenum; 484 urule->set = krule->set; 485 urule->flags = krule->flags; 486 urule->id = krule->id; 487 488 /* Copy opcodes */ 489 memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t)); 490} 491 492 493/* 494 * Copies rule @urule from FreeBSD8 userland format (v0) 495 * to kernel @krule. 496 * Assume @krule is zeroed. 497 */ 498static void 499import_rule0(struct rule_check_info *ci) 500{ 501 struct ip_fw_rule0 *urule; 502 struct ip_fw *krule; 503 int cmdlen, l; 504 ipfw_insn *cmd; 505 ipfw_insn_limit *lcmd; 506 ipfw_insn_if *cmdif; 507 508 urule = (struct ip_fw_rule0 *)ci->urule; 509 krule = (struct ip_fw *)ci->krule; 510 511 /* copy header */ 512 krule->act_ofs = urule->act_ofs; 513 krule->cmd_len = urule->cmd_len; 514 krule->rulenum = urule->rulenum; 515 krule->set = urule->set; 516 if ((urule->_pad & 1) != 0) 517 krule->flags |= IPFW_RULE_NOOPT; 518 519 /* Save rulenum offset */ 520 ci->urule_numoff = offsetof(struct ip_fw_rule0, rulenum); 521 522 /* Copy opcodes */ 523 memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); 524 525 /* 526 * Alter opcodes: 527 * 1) convert tablearg value from 65535 to 0 528 * 2) Add high bit to O_SETFIB/O_SETDSCP values (to make room 529 * for targ). 530 * 3) convert table number in iface opcodes to u16 531 * 4) convert old `nat global` into new 65535 532 */ 533 l = krule->cmd_len; 534 cmd = krule->cmd; 535 cmdlen = 0; 536 537 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 538 cmdlen = F_LEN(cmd); 539 540 switch (cmd->opcode) { 541 /* Opcodes supporting tablearg */ 542 case O_TAG: 543 case O_TAGGED: 544 case O_PIPE: 545 case O_QUEUE: 546 case O_DIVERT: 547 case O_TEE: 548 case O_SKIPTO: 549 case O_CALLRETURN: 550 case O_NETGRAPH: 551 case O_NGTEE: 552 case O_NAT: 553 if (cmd->arg1 == IP_FW_TABLEARG) 554 cmd->arg1 = IP_FW_TARG; 555 else if (cmd->arg1 == 0) 556 cmd->arg1 = IP_FW_NAT44_GLOBAL; 557 break; 558 case O_SETFIB: 559 case O_SETDSCP: 560 if (cmd->arg1 == IP_FW_TABLEARG) 561 cmd->arg1 = IP_FW_TARG; 562 else 563 cmd->arg1 |= 0x8000; 564 break; 565 case O_LIMIT: 566 lcmd = (ipfw_insn_limit *)cmd; 567 if (lcmd->conn_limit == IP_FW_TABLEARG) 568 lcmd->conn_limit = IP_FW_TARG; 569 break; 570 /* Interface tables */ 571 case O_XMIT: 572 case O_RECV: 573 case O_VIA: 574 /* Interface table, possibly */ 575 cmdif = (ipfw_insn_if *)cmd; 576 if (cmdif->name[0] != '\1') 577 break; 578 579 cmdif->p.kidx = (uint16_t)cmdif->p.glob; 580 break; 581 } 582 } 583} 584 585/* 586 * Copies rule @krule from kernel to FreeBSD8 userland format (v0) 587 */ 588static void 589export_rule0(struct ip_fw *krule, struct ip_fw_rule0 *urule, int len) 590{ 591 int cmdlen, l; 592 ipfw_insn *cmd; 593 ipfw_insn_limit *lcmd; 594 ipfw_insn_if *cmdif; 595 596 /* copy header */ 597 memset(urule, 0, len); 598 urule->act_ofs = krule->act_ofs; 599 urule->cmd_len = krule->cmd_len; 600 urule->rulenum = krule->rulenum; 601 urule->set = krule->set; 602 if ((krule->flags & IPFW_RULE_NOOPT) != 0) 603 urule->_pad |= 1; 604 605 /* Copy opcodes */ 606 memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t)); 607 608 /* Export counters */ 609 export_cntr0_base(krule, (struct ip_fw_bcounter0 *)&urule->pcnt); 610 611 /* 612 * Alter opcodes: 613 * 1) convert tablearg value from 0 to 65535 614 * 2) Remove highest bit from O_SETFIB/O_SETDSCP values. 615 * 3) convert table number in iface opcodes to int 616 */ 617 l = urule->cmd_len; 618 cmd = urule->cmd; 619 cmdlen = 0; 620 621 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 622 cmdlen = F_LEN(cmd); 623 624 switch (cmd->opcode) { 625 /* Opcodes supporting tablearg */ 626 case O_TAG: 627 case O_TAGGED: 628 case O_PIPE: 629 case O_QUEUE: 630 case O_DIVERT: 631 case O_TEE: 632 case O_SKIPTO: 633 case O_CALLRETURN: 634 case O_NETGRAPH: 635 case O_NGTEE: 636 case O_NAT: 637 if (cmd->arg1 == IP_FW_TARG) 638 cmd->arg1 = IP_FW_TABLEARG; 639 else if (cmd->arg1 == IP_FW_NAT44_GLOBAL) 640 cmd->arg1 = 0; 641 break; 642 case O_SETFIB: 643 case O_SETDSCP: 644 if (cmd->arg1 == IP_FW_TARG) 645 cmd->arg1 = IP_FW_TABLEARG; 646 else 647 cmd->arg1 &= ~0x8000; 648 break; 649 case O_LIMIT: 650 lcmd = (ipfw_insn_limit *)cmd; 651 if (lcmd->conn_limit == IP_FW_TARG) 652 lcmd->conn_limit = IP_FW_TABLEARG; 653 break; 654 /* Interface tables */ 655 case O_XMIT: 656 case O_RECV: 657 case O_VIA: 658 /* Interface table, possibly */ 659 cmdif = (ipfw_insn_if *)cmd; 660 if (cmdif->name[0] != '\1') 661 break; 662 663 cmdif->p.glob = cmdif->p.kidx; 664 break; 665 } 666 } 667} 668 669/* 670 * Add new rule(s) to the list possibly creating rule number for each. 671 * Update the rule_number in the input struct so the caller knows it as well. 672 * Must be called without IPFW_UH held 673 */ 674static int 675commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count) 676{ 677 int error, i, insert_before, tcount; 678 uint16_t rulenum, *pnum; 679 struct rule_check_info *ci; 680 struct ip_fw *krule; 681 struct ip_fw **map; /* the new array of pointers */ 682 683 /* Check if we need to do table/obj index remap */ 684 tcount = 0; 685 for (ci = rci, i = 0; i < count; ci++, i++) { 686 if (ci->object_opcodes == 0) 687 continue; 688 689 /* 690 * Rule has some object opcodes. 691 * We need to find (and create non-existing) 692 * kernel objects, and reference existing ones. 693 */ 694 error = rewrite_rule_uidx(chain, ci); 695 if (error != 0) { 696 697 /* 698 * rewrite failed, state for current rule 699 * has been reverted. Check if we need to 700 * revert more. 701 */ 702 if (tcount > 0) { 703 704 /* 705 * We have some more table rules 706 * we need to rollback. 707 */ 708 709 IPFW_UH_WLOCK(chain); 710 while (ci != rci) { 711 ci--; 712 if (ci->object_opcodes == 0) 713 continue; 714 unref_rule_objects(chain,ci->krule); 715 716 } 717 IPFW_UH_WUNLOCK(chain); 718 719 } 720 721 return (error); 722 } 723 724 tcount++; 725 } 726 727 /* get_map returns with IPFW_UH_WLOCK if successful */ 728 map = get_map(chain, count, 0 /* not locked */); 729 if (map == NULL) { 730 if (tcount > 0) { 731 /* Unbind tables */ 732 IPFW_UH_WLOCK(chain); 733 for (ci = rci, i = 0; i < count; ci++, i++) { 734 if (ci->object_opcodes == 0) 735 continue; 736 737 unref_rule_objects(chain, ci->krule); 738 } 739 IPFW_UH_WUNLOCK(chain); 740 } 741 742 return (ENOSPC); 743 } 744 745 if (V_autoinc_step < 1) 746 V_autoinc_step = 1; 747 else if (V_autoinc_step > 1000) 748 V_autoinc_step = 1000; 749 750 /* FIXME: Handle count > 1 */ 751 ci = rci; 752 krule = ci->krule; 753 rulenum = krule->rulenum; 754 755 /* find the insertion point, we will insert before */ 756 insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE; 757 i = ipfw_find_rule(chain, insert_before, 0); 758 /* duplicate first part */ 759 if (i > 0) 760 bcopy(chain->map, map, i * sizeof(struct ip_fw *)); 761 map[i] = krule; 762 /* duplicate remaining part, we always have the default rule */ 763 bcopy(chain->map + i, map + i + 1, 764 sizeof(struct ip_fw *) *(chain->n_rules - i)); 765 if (rulenum == 0) { 766 /* Compute rule number and write it back */ 767 rulenum = i > 0 ? map[i-1]->rulenum : 0; 768 if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) 769 rulenum += V_autoinc_step; 770 krule->rulenum = rulenum; 771 /* Save number to userland rule */ 772 pnum = (uint16_t *)((caddr_t)ci->urule + ci->urule_numoff); 773 *pnum = rulenum; 774 } 775 776 krule->id = chain->id + 1; 777 update_skipto_cache(chain, map); 778 map = swap_map(chain, map, chain->n_rules + 1); 779 chain->static_len += RULEUSIZE0(krule); 780 IPFW_UH_WUNLOCK(chain); 781 if (map) 782 free(map, M_IPFW); 783 return (0); 784} 785 786/* 787 * Adds @rule to the list of rules to reap 788 */ 789void 790ipfw_reap_add(struct ip_fw_chain *chain, struct ip_fw **head, 791 struct ip_fw *rule) 792{ 793 794 IPFW_UH_WLOCK_ASSERT(chain); 795 796 /* Unlink rule from everywhere */ 797 unref_rule_objects(chain, rule); 798 799 *((struct ip_fw **)rule) = *head; 800 *head = rule; 801} 802 803/* 804 * Reclaim storage associated with a list of rules. This is 805 * typically the list created using remove_rule. 806 * A NULL pointer on input is handled correctly. 807 */ 808void 809ipfw_reap_rules(struct ip_fw *head) 810{ 811 struct ip_fw *rule; 812 813 while ((rule = head) != NULL) { 814 head = *((struct ip_fw **)head); 815 free_rule(rule); 816 } 817} 818 819/* 820 * Rules to keep are 821 * (default || reserved || !match_set || !match_number) 822 * where 823 * default ::= (rule->rulenum == IPFW_DEFAULT_RULE) 824 * // the default rule is always protected 825 * 826 * reserved ::= (cmd == 0 && n == 0 && rule->set == RESVD_SET) 827 * // RESVD_SET is protected only if cmd == 0 and n == 0 ("ipfw flush") 828 * 829 * match_set ::= (cmd == 0 || rule->set == set) 830 * // set number is ignored for cmd == 0 831 * 832 * match_number ::= (cmd == 1 || n == 0 || n == rule->rulenum) 833 * // number is ignored for cmd == 1 or n == 0 834 * 835 */ 836int 837ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt) 838{ 839 840 /* Don't match default rule for modification queries */ 841 if (rule->rulenum == IPFW_DEFAULT_RULE && 842 (rt->flags & IPFW_RCFLAG_DEFAULT) == 0) 843 return (0); 844 845 /* Don't match rules in reserved set for flush requests */ 846 if ((rt->flags & IPFW_RCFLAG_ALL) != 0 && rule->set == RESVD_SET) 847 return (0); 848 849 /* If we're filtering by set, don't match other sets */ 850 if ((rt->flags & IPFW_RCFLAG_SET) != 0 && rule->set != rt->set) 851 return (0); 852 853 if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 && 854 (rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule)) 855 return (0); 856 857 return (1); 858} 859 860struct manage_sets_args { 861 uint16_t set; 862 uint8_t new_set; 863}; 864 865static int 866swap_sets_cb(struct namedobj_instance *ni, struct named_object *no, 867 void *arg) 868{ 869 struct manage_sets_args *args; 870 871 args = (struct manage_sets_args *)arg; 872 if (no->set == (uint8_t)args->set) 873 no->set = args->new_set; 874 else if (no->set == args->new_set) 875 no->set = (uint8_t)args->set; 876 return (0); 877} 878 879static int 880move_sets_cb(struct namedobj_instance *ni, struct named_object *no, 881 void *arg) 882{ 883 struct manage_sets_args *args; 884 885 args = (struct manage_sets_args *)arg; 886 if (no->set == (uint8_t)args->set) 887 no->set = args->new_set; 888 return (0); 889} 890 891static int 892test_sets_cb(struct namedobj_instance *ni, struct named_object *no, 893 void *arg) 894{ 895 struct manage_sets_args *args; 896 897 args = (struct manage_sets_args *)arg; 898 if (no->set != (uint8_t)args->set) 899 return (0); 900 if (ipfw_objhash_lookup_name_type(ni, args->new_set, 901 no->etlv, no->name) != NULL) 902 return (EEXIST); 903 return (0); 904} 905 906/* 907 * Generic function to handler moving and swapping sets. 908 */ 909int 910ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type, 911 uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) 912{ 913 struct manage_sets_args args; 914 struct named_object *no; 915 916 args.set = set; 917 args.new_set = new_set; 918 switch (cmd) { 919 case SWAP_ALL: 920 return (ipfw_objhash_foreach_type(ni, swap_sets_cb, 921 &args, type)); 922 case TEST_ALL: 923 return (ipfw_objhash_foreach_type(ni, test_sets_cb, 924 &args, type)); 925 case MOVE_ALL: 926 return (ipfw_objhash_foreach_type(ni, move_sets_cb, 927 &args, type)); 928 case COUNT_ONE: 929 /* 930 * @set used to pass kidx. 931 * When @new_set is zero - reset object counter, 932 * otherwise increment it. 933 */ 934 no = ipfw_objhash_lookup_kidx(ni, set); 935 if (new_set != 0) 936 no->ocnt++; 937 else 938 no->ocnt = 0; 939 return (0); 940 case TEST_ONE: 941 /* @set used to pass kidx */ 942 no = ipfw_objhash_lookup_kidx(ni, set); 943 /* 944 * First check number of references: 945 * when it differs, this mean other rules are holding 946 * reference to given object, so it is not possible to 947 * change its set. Note that refcnt may account references 948 * to some going-to-be-added rules. Since we don't know 949 * their numbers (and even if they will be added) it is 950 * perfectly OK to return error here. 951 */ 952 if (no->ocnt != no->refcnt) 953 return (EBUSY); 954 if (ipfw_objhash_lookup_name_type(ni, new_set, type, 955 no->name) != NULL) 956 return (EEXIST); 957 return (0); 958 case MOVE_ONE: 959 /* @set used to pass kidx */ 960 no = ipfw_objhash_lookup_kidx(ni, set); 961 no->set = new_set; 962 return (0); 963 } 964 return (EINVAL); 965} 966 967/* 968 * Delete rules matching range @rt. 969 * Saves number of deleted rules in @ndel. 970 * 971 * Returns 0 on success. 972 */ 973static int 974delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel) 975{ 976 struct ip_fw *reap, *rule, **map; 977 int end, start; 978 int i, n, ndyn, ofs; 979 980 reap = NULL; 981 IPFW_UH_WLOCK(chain); /* arbitrate writers */ 982 983 /* 984 * Stage 1: Determine range to inspect. 985 * Range is half-inclusive, e.g [start, end). 986 */ 987 start = 0; 988 end = chain->n_rules - 1; 989 990 if ((rt->flags & IPFW_RCFLAG_RANGE) != 0) { 991 start = ipfw_find_rule(chain, rt->start_rule, 0); 992 993 end = ipfw_find_rule(chain, rt->end_rule, 0); 994 if (rt->end_rule != IPFW_DEFAULT_RULE) 995 while (chain->map[end]->rulenum == rt->end_rule) 996 end++; 997 } 998 999 /* Allocate new map of the same size */ 1000 map = get_map(chain, 0, 1 /* locked */); 1001 if (map == NULL) { 1002 IPFW_UH_WUNLOCK(chain); 1003 return (ENOMEM); 1004 } 1005 1006 n = 0; 1007 ndyn = 0; 1008 ofs = start; 1009 /* 1. bcopy the initial part of the map */ 1010 if (start > 0) 1011 bcopy(chain->map, map, start * sizeof(struct ip_fw *)); 1012 /* 2. copy active rules between start and end */ 1013 for (i = start; i < end; i++) { 1014 rule = chain->map[i]; 1015 if (ipfw_match_range(rule, rt) == 0) { 1016 map[ofs++] = rule; 1017 continue; 1018 } 1019 1020 n++; 1021 if (ipfw_is_dyn_rule(rule) != 0) 1022 ndyn++; 1023 } 1024 /* 3. copy the final part of the map */ 1025 bcopy(chain->map + end, map + ofs, 1026 (chain->n_rules - end) * sizeof(struct ip_fw *)); 1027 /* 4. recalculate skipto cache */ 1028 update_skipto_cache(chain, map); 1029 /* 5. swap the maps (under UH_WLOCK + WHLOCK) */ 1030 map = swap_map(chain, map, chain->n_rules - n); 1031 /* 6. Remove all dynamic states originated by deleted rules */ 1032 if (ndyn > 0) 1033 ipfw_expire_dyn_rules(chain, rt); 1034 /* 7. now remove the rules deleted from the old map */ 1035 for (i = start; i < end; i++) { 1036 rule = map[i]; 1037 if (ipfw_match_range(rule, rt) == 0) 1038 continue; 1039 chain->static_len -= RULEUSIZE0(rule); 1040 ipfw_reap_add(chain, &reap, rule); 1041 } 1042 IPFW_UH_WUNLOCK(chain); 1043 1044 ipfw_reap_rules(reap); 1045 if (map != NULL) 1046 free(map, M_IPFW); 1047 *ndel = n; 1048 return (0); 1049} 1050 1051static int 1052move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt) 1053{ 1054 struct opcode_obj_rewrite *rw; 1055 struct ip_fw *rule; 1056 ipfw_insn *cmd; 1057 int cmdlen, i, l, c; 1058 uint16_t kidx; 1059 1060 IPFW_UH_WLOCK_ASSERT(ch); 1061 1062 /* Stage 1: count number of references by given rules */ 1063 for (c = 0, i = 0; i < ch->n_rules - 1; i++) { 1064 rule = ch->map[i]; 1065 if (ipfw_match_range(rule, rt) == 0) 1066 continue; 1067 if (rule->set == rt->new_set) /* nothing to do */ 1068 continue; 1069 /* Search opcodes with named objects */ 1070 for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd; 1071 l > 0; l -= cmdlen, cmd += cmdlen) { 1072 cmdlen = F_LEN(cmd); 1073 rw = find_op_rw(cmd, &kidx, NULL); 1074 if (rw == NULL || rw->manage_sets == NULL) 1075 continue; 1076 /* 1077 * When manage_sets() returns non-zero value to 1078 * COUNT_ONE command, consider this as an object 1079 * doesn't support sets (e.g. disabled with sysctl). 1080 * So, skip checks for this object. 1081 */ 1082 if (rw->manage_sets(ch, kidx, 1, COUNT_ONE) != 0) 1083 continue; 1084 c++; 1085 } 1086 } 1087 if (c == 0) /* No objects found */ 1088 return (0); 1089 /* Stage 2: verify "ownership" */ 1090 for (c = 0, i = 0; (i < ch->n_rules - 1) && c == 0; i++) { 1091 rule = ch->map[i]; 1092 if (ipfw_match_range(rule, rt) == 0) 1093 continue; 1094 if (rule->set == rt->new_set) /* nothing to do */ 1095 continue; 1096 /* Search opcodes with named objects */ 1097 for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd; 1098 l > 0 && c == 0; l -= cmdlen, cmd += cmdlen) { 1099 cmdlen = F_LEN(cmd); 1100 rw = find_op_rw(cmd, &kidx, NULL); 1101 if (rw == NULL || rw->manage_sets == NULL) 1102 continue; 1103 /* Test for ownership and conflicting names */ 1104 c = rw->manage_sets(ch, kidx, 1105 (uint8_t)rt->new_set, TEST_ONE); 1106 } 1107 } 1108 /* Stage 3: change set and cleanup */ 1109 for (i = 0; i < ch->n_rules - 1; i++) { 1110 rule = ch->map[i]; 1111 if (ipfw_match_range(rule, rt) == 0) 1112 continue; 1113 if (rule->set == rt->new_set) /* nothing to do */ 1114 continue; 1115 /* Search opcodes with named objects */ 1116 for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd; 1117 l > 0; l -= cmdlen, cmd += cmdlen) { 1118 cmdlen = F_LEN(cmd); 1119 rw = find_op_rw(cmd, &kidx, NULL); 1120 if (rw == NULL || rw->manage_sets == NULL) 1121 continue; 1122 /* cleanup object counter */ 1123 rw->manage_sets(ch, kidx, 1124 0 /* reset counter */, COUNT_ONE); 1125 if (c != 0) 1126 continue; 1127 /* change set */ 1128 rw->manage_sets(ch, kidx, 1129 (uint8_t)rt->new_set, MOVE_ONE); 1130 } 1131 } 1132 return (c); 1133}/* 1134 * Changes set of given rule rannge @rt 1135 * with each other. 1136 * 1137 * Returns 0 on success. 1138 */ 1139static int 1140move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt) 1141{ 1142 struct ip_fw *rule; 1143 int i; 1144 1145 IPFW_UH_WLOCK(chain); 1146 1147 /* 1148 * Move rules with matching paramenerts to a new set. 1149 * This one is much more complex. We have to ensure 1150 * that all referenced tables (if any) are referenced 1151 * by given rule subset only. Otherwise, we can't move 1152 * them to new set and have to return error. 1153 */ 1154 if ((i = move_objects(chain, rt)) != 0) { 1155 IPFW_UH_WUNLOCK(chain); 1156 return (i); 1157 } 1158 1159 /* XXX: We have to do swap holding WLOCK */ 1160 for (i = 0; i < chain->n_rules; i++) { 1161 rule = chain->map[i]; 1162 if (ipfw_match_range(rule, rt) == 0) 1163 continue; 1164 rule->set = rt->new_set; 1165 } 1166 1167 IPFW_UH_WUNLOCK(chain); 1168 1169 return (0); 1170} 1171 1172/* 1173 * Clear counters for a specific rule. 1174 * Normally run under IPFW_UH_RLOCK, but these are idempotent ops 1175 * so we only care that rules do not disappear. 1176 */ 1177static void 1178clear_counters(struct ip_fw *rule, int log_only) 1179{ 1180 ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule); 1181 1182 if (log_only == 0) 1183 IPFW_ZERO_RULE_COUNTER(rule); 1184 if (l->o.opcode == O_LOG) 1185 l->log_left = l->max_log; 1186} 1187 1188/* 1189 * Flushes rules counters and/or log values on matching range. 1190 * 1191 * Returns number of items cleared. 1192 */ 1193static int 1194clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only) 1195{ 1196 struct ip_fw *rule; 1197 int num; 1198 int i; 1199 1200 num = 0; 1201 rt->flags |= IPFW_RCFLAG_DEFAULT; 1202 1203 IPFW_UH_WLOCK(chain); /* arbitrate writers */ 1204 for (i = 0; i < chain->n_rules; i++) { 1205 rule = chain->map[i]; 1206 if (ipfw_match_range(rule, rt) == 0) 1207 continue; 1208 clear_counters(rule, log_only); 1209 num++; 1210 } 1211 IPFW_UH_WUNLOCK(chain); 1212 1213 return (num); 1214} 1215 1216static int 1217check_range_tlv(ipfw_range_tlv *rt) 1218{ 1219 1220 if (rt->head.length != sizeof(*rt)) 1221 return (1); 1222 if (rt->start_rule > rt->end_rule) 1223 return (1); 1224 if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS) 1225 return (1); 1226 1227 if ((rt->flags & IPFW_RCFLAG_USER) != rt->flags) 1228 return (1); 1229 1230 return (0); 1231} 1232 1233/* 1234 * Delete rules matching specified parameters 1235 * Data layout (v0)(current): 1236 * Request: [ ipfw_obj_header ipfw_range_tlv ] 1237 * Reply: [ ipfw_obj_header ipfw_range_tlv ] 1238 * 1239 * Saves number of deleted rules in ipfw_range_tlv->new_set. 1240 * 1241 * Returns 0 on success. 1242 */ 1243static int 1244del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 1245 struct sockopt_data *sd) 1246{ 1247 ipfw_range_header *rh; 1248 int error, ndel; 1249 1250 if (sd->valsize != sizeof(*rh)) 1251 return (EINVAL); 1252 1253 rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize); 1254 1255 if (check_range_tlv(&rh->range) != 0) 1256 return (EINVAL); 1257 1258 ndel = 0; 1259 if ((error = delete_range(chain, &rh->range, &ndel)) != 0) 1260 return (error); 1261 1262 /* Save number of rules deleted */ 1263 rh->range.new_set = ndel; 1264 return (0); 1265} 1266 1267/* 1268 * Move rules/sets matching specified parameters 1269 * Data layout (v0)(current): 1270 * Request: [ ipfw_obj_header ipfw_range_tlv ] 1271 * 1272 * Returns 0 on success. 1273 */ 1274static int 1275move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 1276 struct sockopt_data *sd) 1277{ 1278 ipfw_range_header *rh; 1279 1280 if (sd->valsize != sizeof(*rh)) 1281 return (EINVAL); 1282 1283 rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize); 1284 1285 if (check_range_tlv(&rh->range) != 0) 1286 return (EINVAL); 1287 1288 return (move_range(chain, &rh->range)); 1289} 1290 1291/* 1292 * Clear rule accounting data matching specified parameters 1293 * Data layout (v0)(current): 1294 * Request: [ ipfw_obj_header ipfw_range_tlv ] 1295 * Reply: [ ipfw_obj_header ipfw_range_tlv ] 1296 * 1297 * Saves number of cleared rules in ipfw_range_tlv->new_set. 1298 * 1299 * Returns 0 on success. 1300 */ 1301static int 1302clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 1303 struct sockopt_data *sd) 1304{ 1305 ipfw_range_header *rh; 1306 int log_only, num; 1307 char *msg; 1308 1309 if (sd->valsize != sizeof(*rh)) 1310 return (EINVAL); 1311 1312 rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize); 1313 1314 if (check_range_tlv(&rh->range) != 0) 1315 return (EINVAL); 1316 1317 log_only = (op3->opcode == IP_FW_XRESETLOG); 1318 1319 num = clear_range(chain, &rh->range, log_only); 1320 1321 if (rh->range.flags & IPFW_RCFLAG_ALL) 1322 msg = log_only ? "All logging counts reset" : 1323 "Accounting cleared"; 1324 else 1325 msg = log_only ? "logging count reset" : "cleared"; 1326 1327 if (V_fw_verbose) { 1328 int lev = LOG_SECURITY | LOG_NOTICE; 1329 log(lev, "ipfw: %s.\n", msg); 1330 } 1331 1332 /* Save number of rules cleared */ 1333 rh->range.new_set = num; 1334 return (0); 1335} 1336 1337static void 1338enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt) 1339{ 1340 uint32_t v_set; 1341 1342 IPFW_UH_WLOCK_ASSERT(chain); 1343 1344 /* Change enabled/disabled sets mask */ 1345 v_set = (V_set_disable | rt->set) & ~rt->new_set; 1346 v_set &= ~(1 << RESVD_SET); /* set RESVD_SET always enabled */ 1347 IPFW_WLOCK(chain); 1348 V_set_disable = v_set; 1349 IPFW_WUNLOCK(chain); 1350} 1351 1352static int 1353swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv) 1354{ 1355 struct opcode_obj_rewrite *rw; 1356 struct ip_fw *rule; 1357 int i; 1358 1359 IPFW_UH_WLOCK_ASSERT(chain); 1360 1361 if (rt->set == rt->new_set) /* nothing to do */ 1362 return (0); 1363 1364 if (mv != 0) { 1365 /* 1366 * Berfore moving the rules we need to check that 1367 * there aren't any conflicting named objects. 1368 */ 1369 for (rw = ctl3_rewriters; 1370 rw < ctl3_rewriters + ctl3_rsize; rw++) { 1371 if (rw->manage_sets == NULL) 1372 continue; 1373 i = rw->manage_sets(chain, (uint8_t)rt->set, 1374 (uint8_t)rt->new_set, TEST_ALL); 1375 if (i != 0) 1376 return (EEXIST); 1377 } 1378 } 1379 /* Swap or move two sets */ 1380 for (i = 0; i < chain->n_rules - 1; i++) { 1381 rule = chain->map[i]; 1382 if (rule->set == (uint8_t)rt->set) 1383 rule->set = (uint8_t)rt->new_set; 1384 else if (rule->set == (uint8_t)rt->new_set && mv == 0) 1385 rule->set = (uint8_t)rt->set; 1386 } 1387 for (rw = ctl3_rewriters; rw < ctl3_rewriters + ctl3_rsize; rw++) { 1388 if (rw->manage_sets == NULL) 1389 continue; 1390 rw->manage_sets(chain, (uint8_t)rt->set, 1391 (uint8_t)rt->new_set, mv != 0 ? MOVE_ALL: SWAP_ALL); 1392 } 1393 return (0); 1394} 1395 1396/* 1397 * Swaps or moves set 1398 * Data layout (v0)(current): 1399 * Request: [ ipfw_obj_header ipfw_range_tlv ] 1400 * 1401 * Returns 0 on success. 1402 */ 1403static int 1404manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 1405 struct sockopt_data *sd) 1406{ 1407 ipfw_range_header *rh; 1408 int ret; 1409 1410 if (sd->valsize != sizeof(*rh)) 1411 return (EINVAL); 1412 1413 rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize); 1414 1415 if (rh->range.head.length != sizeof(ipfw_range_tlv)) 1416 return (1); 1417 if (rh->range.set >= IPFW_MAX_SETS || 1418 rh->range.new_set >= IPFW_MAX_SETS) 1419 return (EINVAL); 1420 1421 ret = 0; 1422 IPFW_UH_WLOCK(chain); 1423 switch (op3->opcode) { 1424 case IP_FW_SET_SWAP: 1425 case IP_FW_SET_MOVE: 1426 ret = swap_sets(chain, &rh->range, 1427 op3->opcode == IP_FW_SET_MOVE); 1428 break; 1429 case IP_FW_SET_ENABLE: 1430 enable_sets(chain, &rh->range); 1431 break; 1432 } 1433 IPFW_UH_WUNLOCK(chain); 1434 1435 return (ret); 1436} 1437 1438/** 1439 * Remove all rules with given number, or do set manipulation. 1440 * Assumes chain != NULL && *chain != NULL. 1441 * 1442 * The argument is an uint32_t. The low 16 bit are the rule or set number; 1443 * the next 8 bits are the new set; the top 8 bits indicate the command: 1444 * 1445 * 0 delete rules numbered "rulenum" 1446 * 1 delete rules in set "rulenum" 1447 * 2 move rules "rulenum" to set "new_set" 1448 * 3 move rules from set "rulenum" to set "new_set" 1449 * 4 swap sets "rulenum" and "new_set" 1450 * 5 delete rules "rulenum" and set "new_set" 1451 */ 1452static int 1453del_entry(struct ip_fw_chain *chain, uint32_t arg) 1454{ 1455 uint32_t num; /* rule number or old_set */ 1456 uint8_t cmd, new_set; 1457 int do_del, ndel; 1458 int error = 0; 1459 ipfw_range_tlv rt; 1460 1461 num = arg & 0xffff; 1462 cmd = (arg >> 24) & 0xff; 1463 new_set = (arg >> 16) & 0xff; 1464 1465 if (cmd > 5 || new_set > RESVD_SET) 1466 return EINVAL; 1467 if (cmd == 0 || cmd == 2 || cmd == 5) { 1468 if (num >= IPFW_DEFAULT_RULE) 1469 return EINVAL; 1470 } else { 1471 if (num > RESVD_SET) /* old_set */ 1472 return EINVAL; 1473 } 1474 1475 /* Convert old requests into new representation */ 1476 memset(&rt, 0, sizeof(rt)); 1477 rt.start_rule = num; 1478 rt.end_rule = num; 1479 rt.set = num; 1480 rt.new_set = new_set; 1481 do_del = 0; 1482 1483 switch (cmd) { 1484 case 0: /* delete rules numbered "rulenum" */ 1485 if (num == 0) 1486 rt.flags |= IPFW_RCFLAG_ALL; 1487 else 1488 rt.flags |= IPFW_RCFLAG_RANGE; 1489 do_del = 1; 1490 break; 1491 case 1: /* delete rules in set "rulenum" */ 1492 rt.flags |= IPFW_RCFLAG_SET; 1493 do_del = 1; 1494 break; 1495 case 5: /* delete rules "rulenum" and set "new_set" */ 1496 rt.flags |= IPFW_RCFLAG_RANGE | IPFW_RCFLAG_SET; 1497 rt.set = new_set; 1498 rt.new_set = 0; 1499 do_del = 1; 1500 break; 1501 case 2: /* move rules "rulenum" to set "new_set" */ 1502 rt.flags |= IPFW_RCFLAG_RANGE; 1503 break; 1504 case 3: /* move rules from set "rulenum" to set "new_set" */ 1505 IPFW_UH_WLOCK(chain); 1506 error = swap_sets(chain, &rt, 1); 1507 IPFW_UH_WUNLOCK(chain); 1508 return (error); 1509 case 4: /* swap sets "rulenum" and "new_set" */ 1510 IPFW_UH_WLOCK(chain); 1511 error = swap_sets(chain, &rt, 0); 1512 IPFW_UH_WUNLOCK(chain); 1513 return (error); 1514 default: 1515 return (ENOTSUP); 1516 } 1517 1518 if (do_del != 0) { 1519 if ((error = delete_range(chain, &rt, &ndel)) != 0) 1520 return (error); 1521 1522 if (ndel == 0 && (cmd != 1 && num != 0)) 1523 return (EINVAL); 1524 1525 return (0); 1526 } 1527 1528 return (move_range(chain, &rt)); 1529} 1530 1531/** 1532 * Reset some or all counters on firewall rules. 1533 * The argument `arg' is an u_int32_t. The low 16 bit are the rule number, 1534 * the next 8 bits are the set number, the top 8 bits are the command: 1535 * 0 work with rules from all set's; 1536 * 1 work with rules only from specified set. 1537 * Specified rule number is zero if we want to clear all entries. 1538 * log_only is 1 if we only want to reset logs, zero otherwise. 1539 */ 1540static int 1541zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only) 1542{ 1543 struct ip_fw *rule; 1544 char *msg; 1545 int i; 1546 1547 uint16_t rulenum = arg & 0xffff; 1548 uint8_t set = (arg >> 16) & 0xff; 1549 uint8_t cmd = (arg >> 24) & 0xff; 1550 1551 if (cmd > 1) 1552 return (EINVAL); 1553 if (cmd == 1 && set > RESVD_SET) 1554 return (EINVAL); 1555 1556 IPFW_UH_RLOCK(chain); 1557 if (rulenum == 0) { 1558 V_norule_counter = 0; 1559 for (i = 0; i < chain->n_rules; i++) { 1560 rule = chain->map[i]; 1561 /* Skip rules not in our set. */ 1562 if (cmd == 1 && rule->set != set) 1563 continue; 1564 clear_counters(rule, log_only); 1565 } 1566 msg = log_only ? "All logging counts reset" : 1567 "Accounting cleared"; 1568 } else { 1569 int cleared = 0; 1570 for (i = 0; i < chain->n_rules; i++) { 1571 rule = chain->map[i]; 1572 if (rule->rulenum == rulenum) { 1573 if (cmd == 0 || rule->set == set) 1574 clear_counters(rule, log_only); 1575 cleared = 1; 1576 } 1577 if (rule->rulenum > rulenum) 1578 break; 1579 } 1580 if (!cleared) { /* we did not find any matching rules */ 1581 IPFW_UH_RUNLOCK(chain); 1582 return (EINVAL); 1583 } 1584 msg = log_only ? "logging count reset" : "cleared"; 1585 } 1586 IPFW_UH_RUNLOCK(chain); 1587 1588 if (V_fw_verbose) { 1589 int lev = LOG_SECURITY | LOG_NOTICE; 1590 1591 if (rulenum) 1592 log(lev, "ipfw: Entry %d %s.\n", rulenum, msg); 1593 else 1594 log(lev, "ipfw: %s.\n", msg); 1595 } 1596 return (0); 1597} 1598 1599 1600/* 1601 * Check rule head in FreeBSD11 format 1602 * 1603 */ 1604static int 1605check_ipfw_rule1(struct ip_fw_rule *rule, int size, 1606 struct rule_check_info *ci) 1607{ 1608 int l; 1609 1610 if (size < sizeof(*rule)) { 1611 printf("ipfw: rule too short\n"); 1612 return (EINVAL); 1613 } 1614 1615 /* Check for valid cmd_len */ 1616 l = roundup2(RULESIZE(rule), sizeof(uint64_t)); 1617 if (l != size) { 1618 printf("ipfw: size mismatch (have %d want %d)\n", size, l); 1619 return (EINVAL); 1620 } 1621 if (rule->act_ofs >= rule->cmd_len) { 1622 printf("ipfw: bogus action offset (%u > %u)\n", 1623 rule->act_ofs, rule->cmd_len - 1); 1624 return (EINVAL); 1625 } 1626 1627 if (rule->rulenum > IPFW_DEFAULT_RULE - 1) 1628 return (EINVAL); 1629 1630 return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci)); 1631} 1632 1633/* 1634 * Check rule head in FreeBSD8 format 1635 * 1636 */ 1637static int 1638check_ipfw_rule0(struct ip_fw_rule0 *rule, int size, 1639 struct rule_check_info *ci) 1640{ 1641 int l; 1642 1643 if (size < sizeof(*rule)) { 1644 printf("ipfw: rule too short\n"); 1645 return (EINVAL); 1646 } 1647 1648 /* Check for valid cmd_len */ 1649 l = sizeof(*rule) + rule->cmd_len * 4 - 4; 1650 if (l != size) { 1651 printf("ipfw: size mismatch (have %d want %d)\n", size, l); 1652 return (EINVAL); 1653 } 1654 if (rule->act_ofs >= rule->cmd_len) { 1655 printf("ipfw: bogus action offset (%u > %u)\n", 1656 rule->act_ofs, rule->cmd_len - 1); 1657 return (EINVAL); 1658 } 1659 1660 if (rule->rulenum > IPFW_DEFAULT_RULE - 1) 1661 return (EINVAL); 1662 1663 return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci)); 1664} 1665 1666static int 1667check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) 1668{ 1669 int cmdlen, l; 1670 int have_action; 1671 1672 have_action = 0; 1673 1674 /* 1675 * Now go for the individual checks. Very simple ones, basically only 1676 * instruction sizes. 1677 */ 1678 for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1679 cmdlen = F_LEN(cmd); 1680 if (cmdlen > l) { 1681 printf("ipfw: opcode %d size truncated\n", 1682 cmd->opcode); 1683 return EINVAL; 1684 } 1685 switch (cmd->opcode) { 1686 case O_PROBE_STATE: 1687 case O_KEEP_STATE: 1688 case O_PROTO: 1689 case O_IP_SRC_ME: 1690 case O_IP_DST_ME: 1691 case O_LAYER2: 1692 case O_IN: 1693 case O_FRAG: 1694 case O_DIVERTED: 1695 case O_IPOPT: 1696 case O_IPTOS: 1697 case O_IPPRECEDENCE: 1698 case O_IPVER: 1699 case O_SOCKARG: 1700 case O_TCPFLAGS: 1701 case O_TCPOPTS: 1702 case O_ESTAB: 1703 case O_VERREVPATH: 1704 case O_VERSRCREACH: 1705 case O_ANTISPOOF: 1706 case O_IPSEC: 1707#ifdef INET6 1708 case O_IP6_SRC_ME: 1709 case O_IP6_DST_ME: 1710 case O_EXT_HDR: 1711 case O_IP6: 1712#endif 1713 case O_IP4: 1714 case O_TAG: 1715 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 1716 goto bad_size; 1717 break; 1718 1719 case O_EXTERNAL_ACTION: 1720 if (cmd->arg1 == 0 || 1721 cmdlen != F_INSN_SIZE(ipfw_insn)) { 1722 printf("ipfw: invalid external " 1723 "action opcode\n"); 1724 return (EINVAL); 1725 } 1726 ci->object_opcodes++; 1727 /* Do we have O_EXTERNAL_INSTANCE opcode? */ 1728 if (l != cmdlen) { 1729 l -= cmdlen; 1730 cmd += cmdlen; 1731 cmdlen = F_LEN(cmd); 1732 if (cmd->opcode != O_EXTERNAL_INSTANCE) { 1733 printf("ipfw: invalid opcode " 1734 "next to external action %u\n", 1735 cmd->opcode); 1736 return (EINVAL); 1737 } 1738 if (cmd->arg1 == 0 || 1739 cmdlen != F_INSN_SIZE(ipfw_insn)) { 1740 printf("ipfw: invalid external " 1741 "action instance opcode\n"); 1742 return (EINVAL); 1743 } 1744 ci->object_opcodes++; 1745 } 1746 goto check_action; 1747 1748 case O_FIB: 1749 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 1750 goto bad_size; 1751 if (cmd->arg1 >= rt_numfibs) { 1752 printf("ipfw: invalid fib number %d\n", 1753 cmd->arg1); 1754 return EINVAL; 1755 } 1756 break; 1757 1758 case O_SETFIB: 1759 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 1760 goto bad_size; 1761 if ((cmd->arg1 != IP_FW_TARG) && 1762 ((cmd->arg1 & 0x7FFF) >= rt_numfibs)) { 1763 printf("ipfw: invalid fib number %d\n", 1764 cmd->arg1 & 0x7FFF); 1765 return EINVAL; 1766 } 1767 goto check_action; 1768 1769 case O_UID: 1770 case O_GID: 1771 case O_JAIL: 1772 case O_IP_SRC: 1773 case O_IP_DST: 1774 case O_TCPSEQ: 1775 case O_TCPACK: 1776 case O_PROB: 1777 case O_ICMPTYPE: 1778 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 1779 goto bad_size; 1780 break; 1781 1782 case O_LIMIT: 1783 if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) 1784 goto bad_size; 1785 break; 1786 1787 case O_LOG: 1788 if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) 1789 goto bad_size; 1790 1791 ((ipfw_insn_log *)cmd)->log_left = 1792 ((ipfw_insn_log *)cmd)->max_log; 1793 1794 break; 1795 1796 case O_IP_SRC_MASK: 1797 case O_IP_DST_MASK: 1798 /* only odd command lengths */ 1799 if ((cmdlen & 1) == 0) 1800 goto bad_size; 1801 break; 1802 1803 case O_IP_SRC_SET: 1804 case O_IP_DST_SET: 1805 if (cmd->arg1 == 0 || cmd->arg1 > 256) { 1806 printf("ipfw: invalid set size %d\n", 1807 cmd->arg1); 1808 return EINVAL; 1809 } 1810 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1811 (cmd->arg1+31)/32 ) 1812 goto bad_size; 1813 break; 1814 1815 case O_IP_SRC_LOOKUP: 1816 case O_IP_DST_LOOKUP: 1817 if (cmd->arg1 >= V_fw_tables_max) { 1818 printf("ipfw: invalid table number %d\n", 1819 cmd->arg1); 1820 return (EINVAL); 1821 } 1822 if (cmdlen != F_INSN_SIZE(ipfw_insn) && 1823 cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 && 1824 cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 1825 goto bad_size; 1826 ci->object_opcodes++; 1827 break; 1828 case O_IP_FLOW_LOOKUP: 1829 if (cmd->arg1 >= V_fw_tables_max) { 1830 printf("ipfw: invalid table number %d\n", 1831 cmd->arg1); 1832 return (EINVAL); 1833 } 1834 if (cmdlen != F_INSN_SIZE(ipfw_insn) && 1835 cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 1836 goto bad_size; 1837 ci->object_opcodes++; 1838 break; 1839 case O_MACADDR2: 1840 if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) 1841 goto bad_size; 1842 break; 1843 1844 case O_NOP: 1845 case O_IPID: 1846 case O_IPTTL: 1847 case O_IPLEN: 1848 case O_TCPDATALEN: 1849 case O_TCPWIN: 1850 case O_TAGGED: 1851 if (cmdlen < 1 || cmdlen > 31) 1852 goto bad_size; 1853 break; 1854 1855 case O_DSCP: 1856 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1) 1857 goto bad_size; 1858 break; 1859 1860 case O_MAC_TYPE: 1861 case O_IP_SRCPORT: 1862 case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ 1863 if (cmdlen < 2 || cmdlen > 31) 1864 goto bad_size; 1865 break; 1866 1867 case O_RECV: 1868 case O_XMIT: 1869 case O_VIA: 1870 if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) 1871 goto bad_size; 1872 ci->object_opcodes++; 1873 break; 1874 1875 case O_ALTQ: 1876 if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) 1877 goto bad_size; 1878 break; 1879 1880 case O_PIPE: 1881 case O_QUEUE: 1882 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 1883 goto bad_size; 1884 goto check_action; 1885 1886 case O_FORWARD_IP: 1887 if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) 1888 goto bad_size; 1889 goto check_action; 1890#ifdef INET6 1891 case O_FORWARD_IP6: 1892 if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6)) 1893 goto bad_size; 1894 goto check_action; 1895#endif /* INET6 */ 1896 1897 case O_DIVERT: 1898 case O_TEE: 1899 if (ip_divert_ptr == NULL) 1900 return EINVAL; 1901 else 1902 goto check_size; 1903 case O_NETGRAPH: 1904 case O_NGTEE: 1905 if (ng_ipfw_input_p == NULL) 1906 return EINVAL; 1907 else 1908 goto check_size; 1909 case O_NAT: 1910 if (!IPFW_NAT_LOADED) 1911 return EINVAL; 1912 if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) 1913 goto bad_size; 1914 goto check_action; 1915 case O_FORWARD_MAC: /* XXX not implemented yet */ 1916 case O_CHECK_STATE: 1917 case O_COUNT: 1918 case O_ACCEPT: 1919 case O_DENY: 1920 case O_REJECT: 1921 case O_SETDSCP: 1922#ifdef INET6 1923 case O_UNREACH6: 1924#endif 1925 case O_SKIPTO: 1926 case O_REASS: 1927 case O_CALLRETURN: 1928check_size: 1929 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 1930 goto bad_size; 1931check_action: 1932 if (have_action) { 1933 printf("ipfw: opcode %d, multiple actions" 1934 " not allowed\n", 1935 cmd->opcode); 1936 return (EINVAL); 1937 } 1938 have_action = 1; 1939 if (l != cmdlen) { 1940 printf("ipfw: opcode %d, action must be" 1941 " last opcode\n", 1942 cmd->opcode); 1943 return (EINVAL); 1944 } 1945 break; 1946#ifdef INET6 1947 case O_IP6_SRC: 1948 case O_IP6_DST: 1949 if (cmdlen != F_INSN_SIZE(struct in6_addr) + 1950 F_INSN_SIZE(ipfw_insn)) 1951 goto bad_size; 1952 break; 1953 1954 case O_FLOW6ID: 1955 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1956 ((ipfw_insn_u32 *)cmd)->o.arg1) 1957 goto bad_size; 1958 break; 1959 1960 case O_IP6_SRC_MASK: 1961 case O_IP6_DST_MASK: 1962 if ( !(cmdlen & 1) || cmdlen > 127) 1963 goto bad_size; 1964 break; 1965 case O_ICMP6TYPE: 1966 if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) 1967 goto bad_size; 1968 break; 1969#endif 1970 1971 default: 1972 switch (cmd->opcode) { 1973#ifndef INET6 1974 case O_IP6_SRC_ME: 1975 case O_IP6_DST_ME: 1976 case O_EXT_HDR: 1977 case O_IP6: 1978 case O_UNREACH6: 1979 case O_IP6_SRC: 1980 case O_IP6_DST: 1981 case O_FLOW6ID: 1982 case O_IP6_SRC_MASK: 1983 case O_IP6_DST_MASK: 1984 case O_ICMP6TYPE: 1985 printf("ipfw: no IPv6 support in kernel\n"); 1986 return (EPROTONOSUPPORT); 1987#endif 1988 default: 1989 printf("ipfw: opcode %d, unknown opcode\n", 1990 cmd->opcode); 1991 return (EINVAL); 1992 } 1993 } 1994 } 1995 if (have_action == 0) { 1996 printf("ipfw: missing action\n"); 1997 return (EINVAL); 1998 } 1999 return 0; 2000 2001bad_size: 2002 printf("ipfw: opcode %d size %d wrong\n", 2003 cmd->opcode, cmdlen); 2004 return (EINVAL); 2005} 2006 2007 2008/* 2009 * Translation of requests for compatibility with FreeBSD 7.2/8. 2010 * a static variable tells us if we have an old client from userland, 2011 * and if necessary we translate requests and responses between the 2012 * two formats. 2013 */ 2014static int is7 = 0; 2015 2016struct ip_fw7 { 2017 struct ip_fw7 *next; /* linked list of rules */ 2018 struct ip_fw7 *next_rule; /* ptr to next [skipto] rule */ 2019 /* 'next_rule' is used to pass up 'set_disable' status */ 2020 2021 uint16_t act_ofs; /* offset of action in 32-bit units */ 2022 uint16_t cmd_len; /* # of 32-bit words in cmd */ 2023 uint16_t rulenum; /* rule number */ 2024 uint8_t set; /* rule set (0..31) */ 2025 // #define RESVD_SET 31 /* set for default and persistent rules */ 2026 uint8_t _pad; /* padding */ 2027 // uint32_t id; /* rule id, only in v.8 */ 2028 /* These fields are present in all rules. */ 2029 uint64_t pcnt; /* Packet counter */ 2030 uint64_t bcnt; /* Byte counter */ 2031 uint32_t timestamp; /* tv_sec of last match */ 2032 2033 ipfw_insn cmd[1]; /* storage for commands */ 2034}; 2035 2036static int convert_rule_to_7(struct ip_fw_rule0 *rule); 2037static int convert_rule_to_8(struct ip_fw_rule0 *rule); 2038 2039#ifndef RULESIZE7 2040#define RULESIZE7(rule) (sizeof(struct ip_fw7) + \ 2041 ((struct ip_fw7 *)(rule))->cmd_len * 4 - 4) 2042#endif 2043 2044 2045/* 2046 * Copy the static and dynamic rules to the supplied buffer 2047 * and return the amount of space actually used. 2048 * Must be run under IPFW_UH_RLOCK 2049 */ 2050static size_t 2051ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) 2052{ 2053 char *bp = buf; 2054 char *ep = bp + space; 2055 struct ip_fw *rule; 2056 struct ip_fw_rule0 *dst; 2057 int error, i, l, warnflag; 2058 time_t boot_seconds; 2059 2060 warnflag = 0; 2061 2062 boot_seconds = boottime.tv_sec; 2063 for (i = 0; i < chain->n_rules; i++) { 2064 rule = chain->map[i]; 2065 2066 if (is7) { 2067 /* Convert rule to FreeBSd 7.2 format */ 2068 l = RULESIZE7(rule); 2069 if (bp + l + sizeof(uint32_t) <= ep) { 2070 bcopy(rule, bp, l + sizeof(uint32_t)); 2071 error = set_legacy_obj_kidx(chain, 2072 (struct ip_fw_rule0 *)bp); 2073 if (error != 0) 2074 return (0); 2075 error = convert_rule_to_7((struct ip_fw_rule0 *) bp); 2076 if (error) 2077 return 0; /*XXX correct? */ 2078 /* 2079 * XXX HACK. Store the disable mask in the "next" 2080 * pointer in a wild attempt to keep the ABI the same. 2081 * Why do we do this on EVERY rule? 2082 */ 2083 bcopy(&V_set_disable, 2084 &(((struct ip_fw7 *)bp)->next_rule), 2085 sizeof(V_set_disable)); 2086 if (((struct ip_fw7 *)bp)->timestamp) 2087 ((struct ip_fw7 *)bp)->timestamp += boot_seconds; 2088 bp += l; 2089 } 2090 continue; /* go to next rule */ 2091 } 2092 2093 l = RULEUSIZE0(rule); 2094 if (bp + l > ep) { /* should not happen */ 2095 printf("overflow dumping static rules\n"); 2096 break; 2097 } 2098 dst = (struct ip_fw_rule0 *)bp; 2099 export_rule0(rule, dst, l); 2100 error = set_legacy_obj_kidx(chain, dst); 2101 2102 /* 2103 * XXX HACK. Store the disable mask in the "next" 2104 * pointer in a wild attempt to keep the ABI the same. 2105 * Why do we do this on EVERY rule? 2106 * 2107 * XXX: "ipfw set show" (ab)uses IP_FW_GET to read disabled mask 2108 * so we need to fail _after_ saving at least one mask. 2109 */ 2110 bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable)); 2111 if (dst->timestamp) 2112 dst->timestamp += boot_seconds; 2113 bp += l; 2114 2115 if (error != 0) { 2116 if (error == 2) { 2117 /* Non-fatal table rewrite error. */ 2118 warnflag = 1; 2119 continue; 2120 } 2121 printf("Stop on rule %d. Fail to convert table\n", 2122 rule->rulenum); 2123 break; 2124 } 2125 } 2126 if (warnflag != 0) 2127 printf("ipfw: process %s is using legacy interfaces," 2128 " consider rebuilding\n", ""); 2129 ipfw_get_dynamic(chain, &bp, ep); /* protected by the dynamic lock */ 2130 return (bp - (char *)buf); 2131} 2132 2133 2134struct dump_args { 2135 uint32_t b; /* start rule */ 2136 uint32_t e; /* end rule */ 2137 uint32_t rcount; /* number of rules */ 2138 uint32_t rsize; /* rules size */ 2139 uint32_t tcount; /* number of tables */ 2140 int rcounters; /* counters */ 2141}; 2142 2143void 2144ipfw_export_obj_ntlv(struct named_object *no, ipfw_obj_ntlv *ntlv) 2145{ 2146 2147 ntlv->head.type = no->etlv; 2148 ntlv->head.length = sizeof(*ntlv); 2149 ntlv->idx = no->kidx; 2150 strlcpy(ntlv->name, no->name, sizeof(ntlv->name)); 2151} 2152 2153/* 2154 * Export named object info in instance @ni, identified by @kidx 2155 * to ipfw_obj_ntlv. TLV is allocated from @sd space. 2156 * 2157 * Returns 0 on success. 2158 */ 2159static int 2160export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx, 2161 struct sockopt_data *sd) 2162{ 2163 struct named_object *no; 2164 ipfw_obj_ntlv *ntlv; 2165 2166 no = ipfw_objhash_lookup_kidx(ni, kidx); 2167 KASSERT(no != NULL, ("invalid object kernel index passed")); 2168 2169 ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); 2170 if (ntlv == NULL) 2171 return (ENOMEM); 2172 2173 ipfw_export_obj_ntlv(no, ntlv); 2174 return (0); 2175} 2176 2177/* 2178 * Dumps static rules with table TLVs in buffer @sd. 2179 * 2180 * Returns 0 on success. 2181 */ 2182static int 2183dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da, 2184 uint32_t *bmask, struct sockopt_data *sd) 2185{ 2186 int error; 2187 int i, l; 2188 uint32_t tcount; 2189 ipfw_obj_ctlv *ctlv; 2190 struct ip_fw *krule; 2191 struct namedobj_instance *ni; 2192 caddr_t dst; 2193 2194 /* Dump table names first (if any) */ 2195 if (da->tcount > 0) { 2196 /* Header first */ 2197 ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv)); 2198 if (ctlv == NULL) 2199 return (ENOMEM); 2200 ctlv->head.type = IPFW_TLV_TBLNAME_LIST; 2201 ctlv->head.length = da->tcount * sizeof(ipfw_obj_ntlv) + 2202 sizeof(*ctlv); 2203 ctlv->count = da->tcount; 2204 ctlv->objsize = sizeof(ipfw_obj_ntlv); 2205 } 2206 2207 i = 0; 2208 tcount = da->tcount; 2209 ni = ipfw_get_table_objhash(chain); 2210 while (tcount > 0) { 2211 if ((bmask[i / 32] & (1 << (i % 32))) == 0) { 2212 i++; 2213 continue; 2214 } 2215 2216 /* Jump to shared named object bitmask */ 2217 if (i >= IPFW_TABLES_MAX) { 2218 ni = CHAIN_TO_SRV(chain); 2219 i -= IPFW_TABLES_MAX; 2220 bmask += IPFW_TABLES_MAX / 32; 2221 } 2222 2223 if ((error = export_objhash_ntlv(ni, i, sd)) != 0) 2224 return (error); 2225 2226 i++; 2227 tcount--; 2228 } 2229 2230 /* Dump rules */ 2231 ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv)); 2232 if (ctlv == NULL) 2233 return (ENOMEM); 2234 ctlv->head.type = IPFW_TLV_RULE_LIST; 2235 ctlv->head.length = da->rsize + sizeof(*ctlv); 2236 ctlv->count = da->rcount; 2237 2238 for (i = da->b; i < da->e; i++) { 2239 krule = chain->map[i]; 2240 2241 l = RULEUSIZE1(krule) + sizeof(ipfw_obj_tlv); 2242 if (da->rcounters != 0) 2243 l += sizeof(struct ip_fw_bcounter); 2244 dst = (caddr_t)ipfw_get_sopt_space(sd, l); 2245 if (dst == NULL) 2246 return (ENOMEM); 2247 2248 export_rule1(krule, dst, l, da->rcounters); 2249 } 2250 2251 return (0); 2252} 2253 2254/* 2255 * Marks every object index used in @rule with bit in @bmask. 2256 * Used to generate bitmask of referenced tables/objects for given ruleset 2257 * or its part. 2258 * 2259 * Returns number of newly-referenced objects. 2260 */ 2261static int 2262mark_object_kidx(struct ip_fw_chain *ch, struct ip_fw *rule, 2263 uint32_t *bmask) 2264{ 2265 struct opcode_obj_rewrite *rw; 2266 ipfw_insn *cmd; 2267 int bidx, cmdlen, l, count; 2268 uint16_t kidx; 2269 uint8_t subtype; 2270 2271 l = rule->cmd_len; 2272 cmd = rule->cmd; 2273 cmdlen = 0; 2274 count = 0; 2275 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2276 cmdlen = F_LEN(cmd); 2277 2278 rw = find_op_rw(cmd, &kidx, &subtype); 2279 if (rw == NULL) 2280 continue; 2281 2282 bidx = kidx / 32; 2283 /* 2284 * Maintain separate bitmasks for table and 2285 * non-table objects. 2286 */ 2287 if (rw->etlv != IPFW_TLV_TBL_NAME) 2288 bidx += IPFW_TABLES_MAX / 32; 2289 2290 if ((bmask[bidx] & (1 << (kidx % 32))) == 0) 2291 count++; 2292 2293 bmask[bidx] |= 1 << (kidx % 32); 2294 } 2295 2296 return (count); 2297} 2298 2299/* 2300 * Dumps requested objects data 2301 * Data layout (version 0)(current): 2302 * Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags 2303 * size = ipfw_cfg_lheader.size 2304 * Reply: [ ipfw_cfg_lheader 2305 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional) 2306 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) 2307 * ipfw_obj_tlv(IPFW_TLV_RULE_ENT) [ ip_fw_bcounter (optional) ip_fw_rule ] 2308 * ] (optional) 2309 * [ ipfw_obj_ctlv(IPFW_TLV_STATE_LIST) ipfw_obj_dyntlv x N ] (optional) 2310 * ] 2311 * * NOTE IPFW_TLV_STATE_LIST has the single valid field: objsize. 2312 * The rest (size, count) are set to zero and needs to be ignored. 2313 * 2314 * Returns 0 on success. 2315 */ 2316static int 2317dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 2318 struct sockopt_data *sd) 2319{ 2320 ipfw_cfg_lheader *hdr; 2321 struct ip_fw *rule; 2322 size_t sz, rnum; 2323 uint32_t hdr_flags; 2324 int error, i; 2325 struct dump_args da; 2326 uint32_t *bmask; 2327 2328 hdr = (ipfw_cfg_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr)); 2329 if (hdr == NULL) 2330 return (EINVAL); 2331 2332 error = 0; 2333 bmask = NULL; 2334 /* Allocate needed state. Note we allocate 2xspace mask, for table&srv */ 2335 if (hdr->flags & IPFW_CFG_GET_STATIC) 2336 bmask = malloc(IPFW_TABLES_MAX / 4, M_TEMP, M_WAITOK | M_ZERO); 2337 2338 IPFW_UH_RLOCK(chain); 2339 2340 /* 2341 * STAGE 1: Determine size/count for objects in range. 2342 * Prepare used tables bitmask. 2343 */ 2344 sz = sizeof(ipfw_cfg_lheader); 2345 memset(&da, 0, sizeof(da)); 2346 2347 da.b = 0; 2348 da.e = chain->n_rules; 2349 2350 if (hdr->end_rule != 0) { 2351 /* Handle custom range */ 2352 if ((rnum = hdr->start_rule) > IPFW_DEFAULT_RULE) 2353 rnum = IPFW_DEFAULT_RULE; 2354 da.b = ipfw_find_rule(chain, rnum, 0); 2355 rnum = hdr->end_rule; 2356 rnum = (rnum < IPFW_DEFAULT_RULE) ? rnum+1 : IPFW_DEFAULT_RULE; 2357 da.e = ipfw_find_rule(chain, rnum, 0) + 1; 2358 } 2359 2360 if (hdr->flags & IPFW_CFG_GET_STATIC) { 2361 for (i = da.b; i < da.e; i++) { 2362 rule = chain->map[i]; 2363 da.rsize += RULEUSIZE1(rule) + sizeof(ipfw_obj_tlv); 2364 da.rcount++; 2365 /* Update bitmask of used objects for given range */ 2366 da.tcount += mark_object_kidx(chain, rule, bmask); 2367 } 2368 /* Add counters if requested */ 2369 if (hdr->flags & IPFW_CFG_GET_COUNTERS) { 2370 da.rsize += sizeof(struct ip_fw_bcounter) * da.rcount; 2371 da.rcounters = 1; 2372 } 2373 2374 if (da.tcount > 0) 2375 sz += da.tcount * sizeof(ipfw_obj_ntlv) + 2376 sizeof(ipfw_obj_ctlv); 2377 sz += da.rsize + sizeof(ipfw_obj_ctlv); 2378 } 2379 2380 if (hdr->flags & IPFW_CFG_GET_STATES) 2381 sz += ipfw_dyn_get_count() * sizeof(ipfw_obj_dyntlv) + 2382 sizeof(ipfw_obj_ctlv); 2383 2384 2385 /* 2386 * Fill header anyway. 2387 * Note we have to save header fields to stable storage 2388 * buffer inside @sd can be flushed after dumping rules 2389 */ 2390 hdr->size = sz; 2391 hdr->set_mask = ~V_set_disable; 2392 hdr_flags = hdr->flags; 2393 hdr = NULL; 2394 2395 if (sd->valsize < sz) { 2396 error = ENOMEM; 2397 goto cleanup; 2398 } 2399 2400 /* STAGE2: Store actual data */ 2401 if (hdr_flags & IPFW_CFG_GET_STATIC) { 2402 error = dump_static_rules(chain, &da, bmask, sd); 2403 if (error != 0) 2404 goto cleanup; 2405 } 2406 2407 if (hdr_flags & IPFW_CFG_GET_STATES) 2408 error = ipfw_dump_states(chain, sd); 2409 2410cleanup: 2411 IPFW_UH_RUNLOCK(chain); 2412 2413 if (bmask != NULL) 2414 free(bmask, M_TEMP); 2415 2416 return (error); 2417} 2418 2419int 2420ipfw_check_object_name_generic(const char *name) 2421{ 2422 int nsize; 2423 2424 nsize = sizeof(((ipfw_obj_ntlv *)0)->name); 2425 if (strnlen(name, nsize) == nsize) 2426 return (EINVAL); 2427 if (name[0] == '\0') 2428 return (EINVAL); 2429 return (0); 2430} 2431 2432/* 2433 * Creates non-existent objects referenced by rule. 2434 * 2435 * Return 0 on success. 2436 */ 2437int 2438create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd, 2439 struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti) 2440{ 2441 struct opcode_obj_rewrite *rw; 2442 struct obj_idx *p; 2443 uint16_t kidx; 2444 int error; 2445 2446 /* 2447 * Compatibility stuff: do actual creation for non-existing, 2448 * but referenced objects. 2449 */ 2450 for (p = oib; p < pidx; p++) { 2451 if (p->kidx != 0) 2452 continue; 2453 2454 ti->uidx = p->uidx; 2455 ti->type = p->type; 2456 ti->atype = 0; 2457 2458 rw = find_op_rw(cmd + p->off, NULL, NULL); 2459 KASSERT(rw != NULL, ("Unable to find handler for op %d", 2460 (cmd + p->off)->opcode)); 2461 2462 if (rw->create_object == NULL) 2463 error = EOPNOTSUPP; 2464 else 2465 error = rw->create_object(ch, ti, &kidx); 2466 if (error == 0) { 2467 p->kidx = kidx; 2468 continue; 2469 } 2470 2471 /* 2472 * Error happened. We have to rollback everything. 2473 * Drop all already acquired references. 2474 */ 2475 IPFW_UH_WLOCK(ch); 2476 unref_oib_objects(ch, cmd, oib, pidx); 2477 IPFW_UH_WUNLOCK(ch); 2478 2479 return (error); 2480 } 2481 2482 return (0); 2483} 2484 2485/* 2486 * Compatibility function for old ipfw(8) binaries. 2487 * Rewrites table/nat kernel indices with userland ones. 2488 * Convert tables matching '/^\d+$/' to their atoi() value. 2489 * Use number 65535 for other tables. 2490 * 2491 * Returns 0 on success. 2492 */ 2493static int 2494set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule) 2495{ 2496 struct opcode_obj_rewrite *rw; 2497 struct named_object *no; 2498 ipfw_insn *cmd; 2499 char *end; 2500 long val; 2501 int cmdlen, error, l; 2502 uint16_t kidx, uidx; 2503 uint8_t subtype; 2504 2505 error = 0; 2506 2507 l = rule->cmd_len; 2508 cmd = rule->cmd; 2509 cmdlen = 0; 2510 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2511 cmdlen = F_LEN(cmd); 2512 2513 /* Check if is index in given opcode */ 2514 rw = find_op_rw(cmd, &kidx, &subtype); 2515 if (rw == NULL) 2516 continue; 2517 2518 /* Try to find referenced kernel object */ 2519 no = rw->find_bykidx(ch, kidx); 2520 if (no == NULL) 2521 continue; 2522 2523 val = strtol(no->name, &end, 10); 2524 if (*end == '\0' && val < 65535) { 2525 uidx = val; 2526 } else { 2527 2528 /* 2529 * We are called via legacy opcode. 2530 * Save error and show table as fake number 2531 * not to make ipfw(8) hang. 2532 */ 2533 uidx = 65535; 2534 error = 2; 2535 } 2536 2537 rw->update(cmd, uidx); 2538 } 2539 2540 return (error); 2541} 2542 2543 2544/* 2545 * Unreferences all already-referenced objects in given @cmd rule, 2546 * using information in @oib. 2547 * 2548 * Used to rollback partially converted rule on error. 2549 */ 2550static void 2551unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib, 2552 struct obj_idx *end) 2553{ 2554 struct opcode_obj_rewrite *rw; 2555 struct named_object *no; 2556 struct obj_idx *p; 2557 2558 IPFW_UH_WLOCK_ASSERT(ch); 2559 2560 for (p = oib; p < end; p++) { 2561 if (p->kidx == 0) 2562 continue; 2563 2564 rw = find_op_rw(cmd + p->off, NULL, NULL); 2565 KASSERT(rw != NULL, ("Unable to find handler for op %d", 2566 (cmd + p->off)->opcode)); 2567 2568 /* Find & unref by existing idx */ 2569 no = rw->find_bykidx(ch, p->kidx); 2570 KASSERT(no != NULL, ("Ref'd object %d disappeared", p->kidx)); 2571 no->refcnt--; 2572 } 2573} 2574 2575/* 2576 * Remove references from every object used in @rule. 2577 * Used at rule removal code. 2578 */ 2579static void 2580unref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule) 2581{ 2582 struct opcode_obj_rewrite *rw; 2583 struct named_object *no; 2584 ipfw_insn *cmd; 2585 int cmdlen, l; 2586 uint16_t kidx; 2587 uint8_t subtype; 2588 2589 IPFW_UH_WLOCK_ASSERT(ch); 2590 2591 l = rule->cmd_len; 2592 cmd = rule->cmd; 2593 cmdlen = 0; 2594 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2595 cmdlen = F_LEN(cmd); 2596 2597 rw = find_op_rw(cmd, &kidx, &subtype); 2598 if (rw == NULL) 2599 continue; 2600 no = rw->find_bykidx(ch, kidx); 2601 2602 KASSERT(no != NULL, ("table id %d not found", kidx)); 2603 KASSERT(no->subtype == subtype, 2604 ("wrong type %d (%d) for table id %d", 2605 no->subtype, subtype, kidx)); 2606 KASSERT(no->refcnt > 0, ("refcount for table %d is %d", 2607 kidx, no->refcnt)); 2608 2609 if (no->refcnt == 1 && rw->destroy_object != NULL) 2610 rw->destroy_object(ch, no); 2611 else 2612 no->refcnt--; 2613 } 2614} 2615 2616 2617/* 2618 * Find and reference object (if any) stored in instruction @cmd. 2619 * 2620 * Saves object info in @pidx, sets 2621 * - @unresolved to 1 if object should exists but not found 2622 * 2623 * Returns non-zero value in case of error. 2624 */ 2625static int 2626ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, struct tid_info *ti, 2627 struct obj_idx *pidx, int *unresolved) 2628{ 2629 struct named_object *no; 2630 struct opcode_obj_rewrite *rw; 2631 int error; 2632 2633 /* Check if this opcode is candidate for rewrite */ 2634 rw = find_op_rw(cmd, &ti->uidx, &ti->type); 2635 if (rw == NULL) 2636 return (0); 2637 2638 /* Need to rewrite. Save necessary fields */ 2639 pidx->uidx = ti->uidx; 2640 pidx->type = ti->type; 2641 2642 /* Try to find referenced kernel object */ 2643 error = rw->find_byname(ch, ti, &no); 2644 if (error != 0) 2645 return (error); 2646 if (no == NULL) { 2647 /* 2648 * Report about unresolved object for automaic 2649 * creation. 2650 */ 2651 *unresolved = 1; 2652 return (0); 2653 } 2654 2655 /* Found. Bump refcount and update kidx. */ 2656 no->refcnt++; 2657 rw->update(cmd, no->kidx); 2658 return (0); 2659} 2660 2661/* 2662 * Finds and bumps refcount for objects referenced by given @rule. 2663 * Auto-creates non-existing tables. 2664 * Fills in @oib array with userland/kernel indexes. 2665 * 2666 * Returns 0 on success. 2667 */ 2668static int 2669ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule, 2670 struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti) 2671{ 2672 struct obj_idx *pidx; 2673 ipfw_insn *cmd; 2674 int cmdlen, error, l, unresolved; 2675 2676 pidx = oib; 2677 l = rule->cmd_len; 2678 cmd = rule->cmd; 2679 cmdlen = 0; 2680 error = 0; 2681 2682 IPFW_UH_WLOCK(ch); 2683 2684 /* Increase refcount on each existing referenced table. */ 2685 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2686 cmdlen = F_LEN(cmd); 2687 unresolved = 0; 2688 2689 error = ref_opcode_object(ch, cmd, ti, pidx, &unresolved); 2690 if (error != 0) 2691 break; 2692 /* 2693 * Compatibility stuff for old clients: 2694 * prepare to automaitcally create non-existing objects. 2695 */ 2696 if (unresolved != 0) { 2697 pidx->off = rule->cmd_len - l; 2698 pidx++; 2699 } 2700 } 2701 2702 if (error != 0) { 2703 /* Unref everything we have already done */ 2704 unref_oib_objects(ch, rule->cmd, oib, pidx); 2705 IPFW_UH_WUNLOCK(ch); 2706 return (error); 2707 } 2708 IPFW_UH_WUNLOCK(ch); 2709 2710 /* Perform auto-creation for non-existing objects */ 2711 if (pidx != oib) 2712 error = create_objects_compat(ch, rule->cmd, oib, pidx, ti); 2713 2714 /* Calculate real number of dynamic objects */ 2715 ci->object_opcodes = (uint16_t)(pidx - oib); 2716 2717 return (error); 2718} 2719 2720/* 2721 * Checks is opcode is referencing table of appropriate type. 2722 * Adds reference count for found table if true. 2723 * Rewrites user-supplied opcode values with kernel ones. 2724 * 2725 * Returns 0 on success and appropriate error code otherwise. 2726 */ 2727static int 2728rewrite_rule_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci) 2729{ 2730 int error; 2731 ipfw_insn *cmd; 2732 uint8_t type; 2733 struct obj_idx *p, *pidx_first, *pidx_last; 2734 struct tid_info ti; 2735 2736 /* 2737 * Prepare an array for storing opcode indices. 2738 * Use stack allocation by default. 2739 */ 2740 if (ci->object_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) { 2741 /* Stack */ 2742 pidx_first = ci->obuf; 2743 } else 2744 pidx_first = malloc( 2745 ci->object_opcodes * sizeof(struct obj_idx), 2746 M_IPFW, M_WAITOK | M_ZERO); 2747 2748 error = 0; 2749 type = 0; 2750 memset(&ti, 0, sizeof(ti)); 2751 2752 /* Use set rule is assigned to. */ 2753 ti.set = ci->krule->set; 2754 if (ci->ctlv != NULL) { 2755 ti.tlvs = (void *)(ci->ctlv + 1); 2756 ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv); 2757 } 2758 2759 /* Reference all used tables and other objects */ 2760 error = ref_rule_objects(chain, ci->krule, ci, pidx_first, &ti); 2761 if (error != 0) 2762 goto free; 2763 /* 2764 * Note that ref_rule_objects() might have updated ci->object_opcodes 2765 * to reflect actual number of object opcodes. 2766 */ 2767 2768 /* Perform rewrite of remaining opcodes */ 2769 p = pidx_first; 2770 pidx_last = pidx_first + ci->object_opcodes; 2771 for (p = pidx_first; p < pidx_last; p++) { 2772 cmd = ci->krule->cmd + p->off; 2773 update_opcode_kidx(cmd, p->kidx); 2774 } 2775 2776free: 2777 if (pidx_first != ci->obuf) 2778 free(pidx_first, M_IPFW); 2779 2780 return (error); 2781} 2782 2783/* 2784 * Adds one or more rules to ipfw @chain. 2785 * Data layout (version 0)(current): 2786 * Request: 2787 * [ 2788 * ip_fw3_opheader 2789 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1) 2790 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3) 2791 * ] 2792 * Reply: 2793 * [ 2794 * ip_fw3_opheader 2795 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional) 2796 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] 2797 * ] 2798 * 2799 * Rules in reply are modified to store their actual ruleset number. 2800 * 2801 * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending 2802 * according to their idx field and there has to be no duplicates. 2803 * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending. 2804 * (*3) Each ip_fw structure needs to be aligned to u64 boundary. 2805 * 2806 * Returns 0 on success. 2807 */ 2808static int 2809add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 2810 struct sockopt_data *sd) 2811{ 2812 ipfw_obj_ctlv *ctlv, *rtlv, *tstate; 2813 ipfw_obj_ntlv *ntlv; 2814 int clen, error, idx; 2815 uint32_t count, read; 2816 struct ip_fw_rule *r; 2817 struct rule_check_info rci, *ci, *cbuf; 2818 int i, rsize; 2819 2820 op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize); 2821 ctlv = (ipfw_obj_ctlv *)(op3 + 1); 2822 2823 read = sizeof(ip_fw3_opheader); 2824 rtlv = NULL; 2825 tstate = NULL; 2826 cbuf = NULL; 2827 memset(&rci, 0, sizeof(struct rule_check_info)); 2828 2829 if (read + sizeof(*ctlv) > sd->valsize) 2830 return (EINVAL); 2831 2832 if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { 2833 clen = ctlv->head.length; 2834 /* Check size and alignment */ 2835 if (clen > sd->valsize || clen < sizeof(*ctlv)) 2836 return (EINVAL); 2837 if ((clen % sizeof(uint64_t)) != 0) 2838 return (EINVAL); 2839 2840 /* 2841 * Some table names or other named objects. 2842 * Check for validness. 2843 */ 2844 count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv); 2845 if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv)) 2846 return (EINVAL); 2847 2848 /* 2849 * Check each TLV. 2850 * Ensure TLVs are sorted ascending and 2851 * there are no duplicates. 2852 */ 2853 idx = -1; 2854 ntlv = (ipfw_obj_ntlv *)(ctlv + 1); 2855 while (count > 0) { 2856 if (ntlv->head.length != sizeof(ipfw_obj_ntlv)) 2857 return (EINVAL); 2858 2859 error = ipfw_check_object_name_generic(ntlv->name); 2860 if (error != 0) 2861 return (error); 2862 2863 if (ntlv->idx <= idx) 2864 return (EINVAL); 2865 2866 idx = ntlv->idx; 2867 count--; 2868 ntlv++; 2869 } 2870 2871 tstate = ctlv; 2872 read += ctlv->head.length; 2873 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); 2874 } 2875 2876 if (read + sizeof(*ctlv) > sd->valsize) 2877 return (EINVAL); 2878 2879 if (ctlv->head.type == IPFW_TLV_RULE_LIST) { 2880 clen = ctlv->head.length; 2881 if (clen + read > sd->valsize || clen < sizeof(*ctlv)) 2882 return (EINVAL); 2883 if ((clen % sizeof(uint64_t)) != 0) 2884 return (EINVAL); 2885 2886 /* 2887 * TODO: Permit adding multiple rules at once 2888 */ 2889 if (ctlv->count != 1) 2890 return (ENOTSUP); 2891 2892 clen -= sizeof(*ctlv); 2893 2894 if (ctlv->count > clen / sizeof(struct ip_fw_rule)) 2895 return (EINVAL); 2896 2897 /* Allocate state for each rule or use stack */ 2898 if (ctlv->count == 1) { 2899 memset(&rci, 0, sizeof(struct rule_check_info)); 2900 cbuf = &rci; 2901 } else 2902 cbuf = malloc(ctlv->count * sizeof(*ci), M_TEMP, 2903 M_WAITOK | M_ZERO); 2904 ci = cbuf; 2905 2906 /* 2907 * Check each rule for validness. 2908 * Ensure numbered rules are sorted ascending 2909 * and properly aligned 2910 */ 2911 idx = 0; 2912 r = (struct ip_fw_rule *)(ctlv + 1); 2913 count = 0; 2914 error = 0; 2915 while (clen > 0) { 2916 rsize = roundup2(RULESIZE(r), sizeof(uint64_t)); 2917 if (rsize > clen || ctlv->count <= count) { 2918 error = EINVAL; 2919 break; 2920 } 2921 2922 ci->ctlv = tstate; 2923 error = check_ipfw_rule1(r, rsize, ci); 2924 if (error != 0) 2925 break; 2926 2927 /* Check sorting */ 2928 if (r->rulenum != 0 && r->rulenum < idx) { 2929 printf("rulenum %d idx %d\n", r->rulenum, idx); 2930 error = EINVAL; 2931 break; 2932 } 2933 idx = r->rulenum; 2934 2935 ci->urule = (caddr_t)r; 2936 2937 rsize = roundup2(rsize, sizeof(uint64_t)); 2938 clen -= rsize; 2939 r = (struct ip_fw_rule *)((caddr_t)r + rsize); 2940 count++; 2941 ci++; 2942 } 2943 2944 if (ctlv->count != count || error != 0) { 2945 if (cbuf != &rci) 2946 free(cbuf, M_TEMP); 2947 return (EINVAL); 2948 } 2949 2950 rtlv = ctlv; 2951 read += ctlv->head.length; 2952 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); 2953 } 2954 2955 if (read != sd->valsize || rtlv == NULL || rtlv->count == 0) { 2956 if (cbuf != NULL && cbuf != &rci) 2957 free(cbuf, M_TEMP); 2958 return (EINVAL); 2959 } 2960 2961 /* 2962 * Passed rules seems to be valid. 2963 * Allocate storage and try to add them to chain. 2964 */ 2965 for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) { 2966 clen = RULEKSIZE1((struct ip_fw_rule *)ci->urule); 2967 ci->krule = ipfw_alloc_rule(chain, clen); 2968 import_rule1(ci); 2969 } 2970 2971 if ((error = commit_rules(chain, cbuf, rtlv->count)) != 0) { 2972 /* Free allocate krules */ 2973 for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) 2974 free_rule(ci->krule); 2975 } 2976 2977 if (cbuf != NULL && cbuf != &rci) 2978 free(cbuf, M_TEMP); 2979 2980 return (error); 2981} 2982 2983/* 2984 * Lists all sopts currently registered. 2985 * Data layout (v0)(current): 2986 * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 2987 * Reply: [ ipfw_obj_lheader ipfw_sopt_info x N ] 2988 * 2989 * Returns 0 on success 2990 */ 2991static int 2992dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 2993 struct sockopt_data *sd) 2994{ 2995 struct _ipfw_obj_lheader *olh; 2996 ipfw_sopt_info *i; 2997 struct ipfw_sopt_handler *sh; 2998 uint32_t count, n, size; 2999 3000 olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 3001 if (olh == NULL) 3002 return (EINVAL); 3003 if (sd->valsize < olh->size) 3004 return (EINVAL); 3005 3006 CTL3_LOCK(); 3007 count = ctl3_hsize; 3008 size = count * sizeof(ipfw_sopt_info) + sizeof(ipfw_obj_lheader); 3009 3010 /* Fill in header regadless of buffer size */ 3011 olh->count = count; 3012 olh->objsize = sizeof(ipfw_sopt_info); 3013 3014 if (size > olh->size) { 3015 olh->size = size; 3016 CTL3_UNLOCK(); 3017 return (ENOMEM); 3018 } 3019 olh->size = size; 3020 3021 for (n = 1; n <= count; n++) { 3022 i = (ipfw_sopt_info *)ipfw_get_sopt_space(sd, sizeof(*i)); 3023 KASSERT(i != NULL, ("previously checked buffer is not enough")); 3024 sh = &ctl3_handlers[n]; 3025 i->opcode = sh->opcode; 3026 i->version = sh->version; 3027 i->refcnt = sh->refcnt; 3028 } 3029 CTL3_UNLOCK(); 3030 3031 return (0); 3032} 3033 3034/* 3035 * Compares two opcodes. 3036 * Used both in qsort() and bsearch(). 3037 * 3038 * Returns 0 if match is found. 3039 */ 3040static int 3041compare_opcodes(const void *_a, const void *_b) 3042{ 3043 const struct opcode_obj_rewrite *a, *b; 3044 3045 a = (const struct opcode_obj_rewrite *)_a; 3046 b = (const struct opcode_obj_rewrite *)_b; 3047 3048 if (a->opcode < b->opcode) 3049 return (-1); 3050 else if (a->opcode > b->opcode) 3051 return (1); 3052 3053 return (0); 3054} 3055 3056/* 3057 * XXX: Rewrite bsearch() 3058 */ 3059static int 3060find_op_rw_range(uint16_t op, struct opcode_obj_rewrite **plo, 3061 struct opcode_obj_rewrite **phi) 3062{ 3063 struct opcode_obj_rewrite *ctl3_max, *lo, *hi, h, *rw; 3064 3065 memset(&h, 0, sizeof(h)); 3066 h.opcode = op; 3067 3068 rw = (struct opcode_obj_rewrite *)bsearch(&h, ctl3_rewriters, 3069 ctl3_rsize, sizeof(h), compare_opcodes); 3070 if (rw == NULL) 3071 return (1); 3072 3073 /* Find the first element matching the same opcode */ 3074 lo = rw; 3075 for ( ; lo > ctl3_rewriters && (lo - 1)->opcode == op; lo--) 3076 ; 3077 3078 /* Find the last element matching the same opcode */ 3079 hi = rw; 3080 ctl3_max = ctl3_rewriters + ctl3_rsize; 3081 for ( ; (hi + 1) < ctl3_max && (hi + 1)->opcode == op; hi++) 3082 ; 3083 3084 *plo = lo; 3085 *phi = hi; 3086 3087 return (0); 3088} 3089 3090/* 3091 * Finds opcode object rewriter based on @code. 3092 * 3093 * Returns pointer to handler or NULL. 3094 */ 3095static struct opcode_obj_rewrite * 3096find_op_rw(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 3097{ 3098 struct opcode_obj_rewrite *rw, *lo, *hi; 3099 uint16_t uidx; 3100 uint8_t subtype; 3101 3102 if (find_op_rw_range(cmd->opcode, &lo, &hi) != 0) 3103 return (NULL); 3104 3105 for (rw = lo; rw <= hi; rw++) { 3106 if (rw->classifier(cmd, &uidx, &subtype) == 0) { 3107 if (puidx != NULL) 3108 *puidx = uidx; 3109 if (ptype != NULL) 3110 *ptype = subtype; 3111 return (rw); 3112 } 3113 } 3114 3115 return (NULL); 3116} 3117int 3118classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx) 3119{ 3120 3121 if (find_op_rw(cmd, puidx, NULL) == 0) 3122 return (1); 3123 return (0); 3124} 3125 3126void 3127update_opcode_kidx(ipfw_insn *cmd, uint16_t idx) 3128{ 3129 struct opcode_obj_rewrite *rw; 3130 3131 rw = find_op_rw(cmd, NULL, NULL); 3132 KASSERT(rw != NULL, ("No handler to update opcode %d", cmd->opcode)); 3133 rw->update(cmd, idx); 3134} 3135 3136void 3137ipfw_init_obj_rewriter() 3138{ 3139 3140 ctl3_rewriters = NULL; 3141 ctl3_rsize = 0; 3142} 3143 3144void 3145ipfw_destroy_obj_rewriter() 3146{ 3147 3148 if (ctl3_rewriters != NULL) 3149 free(ctl3_rewriters, M_IPFW); 3150 ctl3_rewriters = NULL; 3151 ctl3_rsize = 0; 3152} 3153 3154/* 3155 * Adds one or more opcode object rewrite handlers to the global array. 3156 * Function may sleep. 3157 */ 3158void 3159ipfw_add_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count) 3160{ 3161 size_t sz; 3162 struct opcode_obj_rewrite *tmp; 3163 3164 CTL3_LOCK(); 3165 3166 for (;;) { 3167 sz = ctl3_rsize + count; 3168 CTL3_UNLOCK(); 3169 tmp = malloc(sizeof(*rw) * sz, M_IPFW, M_WAITOK | M_ZERO); 3170 CTL3_LOCK(); 3171 if (ctl3_rsize + count <= sz) 3172 break; 3173 3174 /* Retry */ 3175 free(tmp, M_IPFW); 3176 } 3177 3178 /* Merge old & new arrays */ 3179 sz = ctl3_rsize + count; 3180 memcpy(tmp, ctl3_rewriters, ctl3_rsize * sizeof(*rw)); 3181 memcpy(&tmp[ctl3_rsize], rw, count * sizeof(*rw)); 3182 qsort(tmp, sz, sizeof(*rw), compare_opcodes); 3183 /* Switch new and free old */ 3184 if (ctl3_rewriters != NULL) 3185 free(ctl3_rewriters, M_IPFW); 3186 ctl3_rewriters = tmp; 3187 ctl3_rsize = sz; 3188 3189 CTL3_UNLOCK(); 3190} 3191 3192/* 3193 * Removes one or more object rewrite handlers from the global array. 3194 */ 3195int 3196ipfw_del_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count) 3197{ 3198 size_t sz; 3199 struct opcode_obj_rewrite *ctl3_max, *ktmp, *lo, *hi; 3200 int i; 3201 3202 CTL3_LOCK(); 3203 3204 for (i = 0; i < count; i++) { 3205 if (find_op_rw_range(rw[i].opcode, &lo, &hi) != 0) 3206 continue; 3207 3208 for (ktmp = lo; ktmp <= hi; ktmp++) { 3209 if (ktmp->classifier != rw[i].classifier) 3210 continue; 3211 3212 ctl3_max = ctl3_rewriters + ctl3_rsize; 3213 sz = (ctl3_max - (ktmp + 1)) * sizeof(*ktmp); 3214 memmove(ktmp, ktmp + 1, sz); 3215 ctl3_rsize--; 3216 break; 3217 } 3218 3219 } 3220 3221 if (ctl3_rsize == 0) { 3222 if (ctl3_rewriters != NULL) 3223 free(ctl3_rewriters, M_IPFW); 3224 ctl3_rewriters = NULL; 3225 } 3226 3227 CTL3_UNLOCK(); 3228 3229 return (0); 3230} 3231 3232static int 3233export_objhash_ntlv_internal(struct namedobj_instance *ni, 3234 struct named_object *no, void *arg) 3235{ 3236 struct sockopt_data *sd; 3237 ipfw_obj_ntlv *ntlv; 3238 3239 sd = (struct sockopt_data *)arg; 3240 ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); 3241 if (ntlv == NULL) 3242 return (ENOMEM); 3243 ipfw_export_obj_ntlv(no, ntlv); 3244 return (0); 3245} 3246 3247/* 3248 * Lists all service objects. 3249 * Data layout (v0)(current): 3250 * Request: [ ipfw_obj_lheader ] size = ipfw_obj_lheader.size 3251 * Reply: [ ipfw_obj_lheader [ ipfw_obj_ntlv x N ] (optional) ] 3252 * Returns 0 on success 3253 */ 3254static int 3255dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 3256 struct sockopt_data *sd) 3257{ 3258 ipfw_obj_lheader *hdr; 3259 int count; 3260 3261 hdr = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr)); 3262 if (hdr == NULL) 3263 return (EINVAL); 3264 3265 IPFW_UH_RLOCK(chain); 3266 count = ipfw_objhash_count(CHAIN_TO_SRV(chain)); 3267 hdr->size = sizeof(ipfw_obj_lheader) + count * sizeof(ipfw_obj_ntlv); 3268 if (sd->valsize < hdr->size) { 3269 IPFW_UH_RUNLOCK(chain); 3270 return (ENOMEM); 3271 } 3272 hdr->count = count; 3273 hdr->objsize = sizeof(ipfw_obj_ntlv); 3274 if (count > 0) 3275 ipfw_objhash_foreach(CHAIN_TO_SRV(chain), 3276 export_objhash_ntlv_internal, sd); 3277 IPFW_UH_RUNLOCK(chain); 3278 return (0); 3279} 3280 3281/* 3282 * Compares two sopt handlers (code, version and handler ptr). 3283 * Used both as qsort() and bsearch(). 3284 * Does not compare handler for latter case. 3285 * 3286 * Returns 0 if match is found. 3287 */ 3288static int 3289compare_sh(const void *_a, const void *_b) 3290{ 3291 const struct ipfw_sopt_handler *a, *b; 3292 3293 a = (const struct ipfw_sopt_handler *)_a; 3294 b = (const struct ipfw_sopt_handler *)_b; 3295 3296 if (a->opcode < b->opcode) 3297 return (-1); 3298 else if (a->opcode > b->opcode) 3299 return (1); 3300 3301 if (a->version < b->version) 3302 return (-1); 3303 else if (a->version > b->version) 3304 return (1); 3305 3306 /* bsearch helper */ 3307 if (a->handler == NULL) 3308 return (0); 3309 3310 if ((uintptr_t)a->handler < (uintptr_t)b->handler) 3311 return (-1); 3312 else if ((uintptr_t)a->handler > (uintptr_t)b->handler) 3313 return (1); 3314 3315 return (0); 3316} 3317 3318/* 3319 * Finds sopt handler based on @code and @version. 3320 * 3321 * Returns pointer to handler or NULL. 3322 */ 3323static struct ipfw_sopt_handler * 3324find_sh(uint16_t code, uint8_t version, sopt_handler_f *handler) 3325{ 3326 struct ipfw_sopt_handler *sh, h; 3327 3328 memset(&h, 0, sizeof(h)); 3329 h.opcode = code; 3330 h.version = version; 3331 h.handler = handler; 3332 3333 sh = (struct ipfw_sopt_handler *)bsearch(&h, ctl3_handlers, 3334 ctl3_hsize, sizeof(h), compare_sh); 3335 3336 return (sh); 3337} 3338 3339static int 3340find_ref_sh(uint16_t opcode, uint8_t version, struct ipfw_sopt_handler *psh) 3341{ 3342 struct ipfw_sopt_handler *sh; 3343 3344 CTL3_LOCK(); 3345 if ((sh = find_sh(opcode, version, NULL)) == NULL) { 3346 CTL3_UNLOCK(); 3347 printf("ipfw: ipfw_ctl3 invalid option %d""v""%d\n", 3348 opcode, version); 3349 return (EINVAL); 3350 } 3351 sh->refcnt++; 3352 ctl3_refct++; 3353 /* Copy handler data to requested buffer */ 3354 *psh = *sh; 3355 CTL3_UNLOCK(); 3356 3357 return (0); 3358} 3359 3360static void 3361find_unref_sh(struct ipfw_sopt_handler *psh) 3362{ 3363 struct ipfw_sopt_handler *sh; 3364 3365 CTL3_LOCK(); 3366 sh = find_sh(psh->opcode, psh->version, NULL); 3367 KASSERT(sh != NULL, ("ctl3 handler disappeared")); 3368 sh->refcnt--; 3369 ctl3_refct--; 3370 CTL3_UNLOCK(); 3371} 3372 3373void 3374ipfw_init_sopt_handler() 3375{ 3376 3377 CTL3_LOCK_INIT(); 3378 IPFW_ADD_SOPT_HANDLER(1, scodes); 3379} 3380 3381void 3382ipfw_destroy_sopt_handler() 3383{ 3384 3385 IPFW_DEL_SOPT_HANDLER(1, scodes); 3386 CTL3_LOCK_DESTROY(); 3387} 3388 3389/* 3390 * Adds one or more sockopt handlers to the global array. 3391 * Function may sleep. 3392 */ 3393void 3394ipfw_add_sopt_handler(struct ipfw_sopt_handler *sh, size_t count) 3395{ 3396 size_t sz; 3397 struct ipfw_sopt_handler *tmp; 3398 3399 CTL3_LOCK(); 3400 3401 for (;;) { 3402 sz = ctl3_hsize + count; 3403 CTL3_UNLOCK(); 3404 tmp = malloc(sizeof(*sh) * sz, M_IPFW, M_WAITOK | M_ZERO); 3405 CTL3_LOCK(); 3406 if (ctl3_hsize + count <= sz) 3407 break; 3408 3409 /* Retry */ 3410 free(tmp, M_IPFW); 3411 } 3412 3413 /* Merge old & new arrays */ 3414 sz = ctl3_hsize + count; 3415 memcpy(tmp, ctl3_handlers, ctl3_hsize * sizeof(*sh)); 3416 memcpy(&tmp[ctl3_hsize], sh, count * sizeof(*sh)); 3417 qsort(tmp, sz, sizeof(*sh), compare_sh); 3418 /* Switch new and free old */ 3419 if (ctl3_handlers != NULL) 3420 free(ctl3_handlers, M_IPFW); 3421 ctl3_handlers = tmp; 3422 ctl3_hsize = sz; 3423 ctl3_gencnt++; 3424 3425 CTL3_UNLOCK(); 3426} 3427 3428/* 3429 * Removes one or more sockopt handlers from the global array. 3430 */ 3431int 3432ipfw_del_sopt_handler(struct ipfw_sopt_handler *sh, size_t count) 3433{ 3434 size_t sz; 3435 struct ipfw_sopt_handler *tmp, *h; 3436 int i; 3437 3438 CTL3_LOCK(); 3439 3440 for (i = 0; i < count; i++) { 3441 tmp = &sh[i]; 3442 h = find_sh(tmp->opcode, tmp->version, tmp->handler); 3443 if (h == NULL) 3444 continue; 3445 3446 sz = (ctl3_handlers + ctl3_hsize - (h + 1)) * sizeof(*h); 3447 memmove(h, h + 1, sz); 3448 ctl3_hsize--; 3449 } 3450 3451 if (ctl3_hsize == 0) { 3452 if (ctl3_handlers != NULL) 3453 free(ctl3_handlers, M_IPFW); 3454 ctl3_handlers = NULL; 3455 } 3456 3457 ctl3_gencnt++; 3458 3459 CTL3_UNLOCK(); 3460 3461 return (0); 3462} 3463 3464/* 3465 * Writes data accumulated in @sd to sockopt buffer. 3466 * Zeroes internal @sd buffer. 3467 */ 3468static int 3469ipfw_flush_sopt_data(struct sockopt_data *sd) 3470{ 3471 struct sockopt *sopt; 3472 int error; 3473 size_t sz; 3474 3475 sz = sd->koff; 3476 if (sz == 0) 3477 return (0); 3478 3479 sopt = sd->sopt; 3480 3481 if (sopt->sopt_dir == SOPT_GET) { 3482 error = copyout(sd->kbuf, sopt->sopt_val, sz); 3483 if (error != 0) 3484 return (error); 3485 } 3486 3487 memset(sd->kbuf, 0, sd->ksize); 3488 sd->ktotal += sz; 3489 sd->koff = 0; 3490 if (sd->ktotal + sd->ksize < sd->valsize) 3491 sd->kavail = sd->ksize; 3492 else 3493 sd->kavail = sd->valsize - sd->ktotal; 3494 3495 /* Update sopt buffer data */ 3496 sopt->sopt_valsize = sd->ktotal; 3497 sopt->sopt_val = sd->sopt_val + sd->ktotal; 3498 3499 return (0); 3500} 3501 3502/* 3503 * Ensures that @sd buffer has contiguous @neeeded number of 3504 * bytes. 3505 * 3506 * Returns pointer to requested space or NULL. 3507 */ 3508caddr_t 3509ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed) 3510{ 3511 int error; 3512 caddr_t addr; 3513 3514 if (sd->kavail < needed) { 3515 /* 3516 * Flush data and try another time. 3517 */ 3518 error = ipfw_flush_sopt_data(sd); 3519 3520 if (sd->kavail < needed || error != 0) 3521 return (NULL); 3522 } 3523 3524 addr = sd->kbuf + sd->koff; 3525 sd->koff += needed; 3526 sd->kavail -= needed; 3527 return (addr); 3528} 3529 3530/* 3531 * Requests @needed contiguous bytes from @sd buffer. 3532 * Function is used to notify subsystem that we are 3533 * interesed in first @needed bytes (request header) 3534 * and the rest buffer can be safely zeroed. 3535 * 3536 * Returns pointer to requested space or NULL. 3537 */ 3538caddr_t 3539ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed) 3540{ 3541 caddr_t addr; 3542 3543 if ((addr = ipfw_get_sopt_space(sd, needed)) == NULL) 3544 return (NULL); 3545 3546 if (sd->kavail > 0) 3547 memset(sd->kbuf + sd->koff, 0, sd->kavail); 3548 3549 return (addr); 3550} 3551 3552/* 3553 * New sockopt handler. 3554 */ 3555int 3556ipfw_ctl3(struct sockopt *sopt) 3557{ 3558 int error, locked; 3559 size_t size, valsize; 3560 struct ip_fw_chain *chain; 3561 char xbuf[256]; 3562 struct sockopt_data sdata; 3563 struct ipfw_sopt_handler h; 3564 ip_fw3_opheader *op3 = NULL; 3565 3566 error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); 3567 if (error != 0) 3568 return (error); 3569 3570 if (sopt->sopt_name != IP_FW3) 3571 return (ipfw_ctl(sopt)); 3572 3573 chain = &V_layer3_chain; 3574 error = 0; 3575 3576 /* Save original valsize before it is altered via sooptcopyin() */ 3577 valsize = sopt->sopt_valsize; 3578 memset(&sdata, 0, sizeof(sdata)); 3579 /* Read op3 header first to determine actual operation */ 3580 op3 = (ip_fw3_opheader *)xbuf; 3581 error = sooptcopyin(sopt, op3, sizeof(*op3), sizeof(*op3)); 3582 if (error != 0) 3583 return (error); 3584 sopt->sopt_valsize = valsize; 3585 3586 /* 3587 * Find and reference command. 3588 */ 3589 error = find_ref_sh(op3->opcode, op3->version, &h); 3590 if (error != 0) 3591 return (error); 3592 3593 /* 3594 * Disallow modifications in really-really secure mode, but still allow 3595 * the logging counters to be reset. 3596 */ 3597 if ((h.dir & HDIR_SET) != 0 && h.opcode != IP_FW_XRESETLOG) { 3598 error = securelevel_ge(sopt->sopt_td->td_ucred, 3); 3599 if (error != 0) { 3600 find_unref_sh(&h); 3601 return (error); 3602 } 3603 } 3604 3605 /* 3606 * Fill in sockopt_data structure that may be useful for 3607 * IP_FW3 get requests. 3608 */ 3609 locked = 0; 3610 if (valsize <= sizeof(xbuf)) { 3611 /* use on-stack buffer */ 3612 sdata.kbuf = xbuf; 3613 sdata.ksize = sizeof(xbuf); 3614 sdata.kavail = valsize; 3615 } else { 3616 3617 /* 3618 * Determine opcode type/buffer size: 3619 * allocate sliding-window buf for data export or 3620 * contiguous buffer for special ops. 3621 */ 3622 if ((h.dir & HDIR_SET) != 0) { 3623 /* Set request. Allocate contigous buffer. */ 3624 if (valsize > CTL3_LARGEBUF) { 3625 find_unref_sh(&h); 3626 return (EFBIG); 3627 } 3628 3629 size = valsize; 3630 } else { 3631 /* Get request. Allocate sliding window buffer */ 3632 size = (valsize<CTL3_SMALLBUF) ? valsize:CTL3_SMALLBUF; 3633 3634 if (size < valsize) { 3635 /* We have to wire user buffer */ 3636 error = vslock(sopt->sopt_val, valsize); 3637 if (error != 0) 3638 return (error); 3639 locked = 1; 3640 } 3641 } 3642 3643 sdata.kbuf = malloc(size, M_TEMP, M_WAITOK | M_ZERO); 3644 sdata.ksize = size; 3645 sdata.kavail = size; 3646 } 3647 3648 sdata.sopt = sopt; 3649 sdata.sopt_val = sopt->sopt_val; 3650 sdata.valsize = valsize; 3651 3652 /* 3653 * Copy either all request (if valsize < bsize_max) 3654 * or first bsize_max bytes to guarantee most consumers 3655 * that all necessary data has been copied). 3656 * Anyway, copy not less than sizeof(ip_fw3_opheader). 3657 */ 3658 if ((error = sooptcopyin(sopt, sdata.kbuf, sdata.ksize, 3659 sizeof(ip_fw3_opheader))) != 0) 3660 return (error); 3661 op3 = (ip_fw3_opheader *)sdata.kbuf; 3662 3663 /* Finally, run handler */ 3664 error = h.handler(chain, op3, &sdata); 3665 find_unref_sh(&h); 3666 3667 /* Flush state and free buffers */ 3668 if (error == 0) 3669 error = ipfw_flush_sopt_data(&sdata); 3670 else 3671 ipfw_flush_sopt_data(&sdata); 3672 3673 if (locked != 0) 3674 vsunlock(sdata.sopt_val, valsize); 3675 3676 /* Restore original pointer and set number of bytes written */ 3677 sopt->sopt_val = sdata.sopt_val; 3678 sopt->sopt_valsize = sdata.ktotal; 3679 if (sdata.kbuf != xbuf) 3680 free(sdata.kbuf, M_TEMP); 3681 3682 return (error); 3683} 3684 3685/** 3686 * {set|get}sockopt parser. 3687 */ 3688int 3689ipfw_ctl(struct sockopt *sopt) 3690{ 3691#define RULE_MAXSIZE (512*sizeof(u_int32_t)) 3692 int error; 3693 size_t size, valsize; 3694 struct ip_fw *buf; 3695 struct ip_fw_rule0 *rule; 3696 struct ip_fw_chain *chain; 3697 u_int32_t rulenum[2]; 3698 uint32_t opt; 3699 struct rule_check_info ci; 3700 IPFW_RLOCK_TRACKER; 3701 3702 chain = &V_layer3_chain; 3703 error = 0; 3704 3705 /* Save original valsize before it is altered via sooptcopyin() */ 3706 valsize = sopt->sopt_valsize; 3707 opt = sopt->sopt_name; 3708 3709 /* 3710 * Disallow modifications in really-really secure mode, but still allow 3711 * the logging counters to be reset. 3712 */ 3713 if (opt == IP_FW_ADD || 3714 (sopt->sopt_dir == SOPT_SET && opt != IP_FW_RESETLOG)) { 3715 error = securelevel_ge(sopt->sopt_td->td_ucred, 3); 3716 if (error != 0) 3717 return (error); 3718 } 3719 3720 switch (opt) { 3721 case IP_FW_GET: 3722 /* 3723 * pass up a copy of the current rules. Static rules 3724 * come first (the last of which has number IPFW_DEFAULT_RULE), 3725 * followed by a possibly empty list of dynamic rule. 3726 * The last dynamic rule has NULL in the "next" field. 3727 * 3728 * Note that the calculated size is used to bound the 3729 * amount of data returned to the user. The rule set may 3730 * change between calculating the size and returning the 3731 * data in which case we'll just return what fits. 3732 */ 3733 for (;;) { 3734 int len = 0, want; 3735 3736 size = chain->static_len; 3737 size += ipfw_dyn_len(); 3738 if (size >= sopt->sopt_valsize) 3739 break; 3740 buf = malloc(size, M_TEMP, M_WAITOK | M_ZERO); 3741 IPFW_UH_RLOCK(chain); 3742 /* check again how much space we need */ 3743 want = chain->static_len + ipfw_dyn_len(); 3744 if (size >= want) 3745 len = ipfw_getrules(chain, buf, size); 3746 IPFW_UH_RUNLOCK(chain); 3747 if (size >= want) 3748 error = sooptcopyout(sopt, buf, len); 3749 free(buf, M_TEMP); 3750 if (size >= want) 3751 break; 3752 } 3753 break; 3754 3755 case IP_FW_FLUSH: 3756 /* locking is done within del_entry() */ 3757 error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */ 3758 break; 3759 3760 case IP_FW_ADD: 3761 rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK); 3762 error = sooptcopyin(sopt, rule, RULE_MAXSIZE, 3763 sizeof(struct ip_fw7) ); 3764 3765 memset(&ci, 0, sizeof(struct rule_check_info)); 3766 3767 /* 3768 * If the size of commands equals RULESIZE7 then we assume 3769 * a FreeBSD7.2 binary is talking to us (set is7=1). 3770 * is7 is persistent so the next 'ipfw list' command 3771 * will use this format. 3772 * NOTE: If wrong version is guessed (this can happen if 3773 * the first ipfw command is 'ipfw [pipe] list') 3774 * the ipfw binary may crash or loop infinitly... 3775 */ 3776 size = sopt->sopt_valsize; 3777 if (size == RULESIZE7(rule)) { 3778 is7 = 1; 3779 error = convert_rule_to_8(rule); 3780 if (error) { 3781 free(rule, M_TEMP); 3782 return error; 3783 } 3784 size = RULESIZE(rule); 3785 } else 3786 is7 = 0; 3787 if (error == 0) 3788 error = check_ipfw_rule0(rule, size, &ci); 3789 if (error == 0) { 3790 /* locking is done within add_rule() */ 3791 struct ip_fw *krule; 3792 krule = ipfw_alloc_rule(chain, RULEKSIZE0(rule)); 3793 ci.urule = (caddr_t)rule; 3794 ci.krule = krule; 3795 import_rule0(&ci); 3796 error = commit_rules(chain, &ci, 1); 3797 if (error != 0) 3798 free_rule(ci.krule); 3799 else if (sopt->sopt_dir == SOPT_GET) { 3800 if (is7) { 3801 error = convert_rule_to_7(rule); 3802 size = RULESIZE7(rule); 3803 if (error) { 3804 free(rule, M_TEMP); 3805 return error; 3806 } 3807 } 3808 error = sooptcopyout(sopt, rule, size); 3809 } 3810 } 3811 free(rule, M_TEMP); 3812 break; 3813 3814 case IP_FW_DEL: 3815 /* 3816 * IP_FW_DEL is used for deleting single rules or sets, 3817 * and (ab)used to atomically manipulate sets. Argument size 3818 * is used to distinguish between the two: 3819 * sizeof(u_int32_t) 3820 * delete single rule or set of rules, 3821 * or reassign rules (or sets) to a different set. 3822 * 2*sizeof(u_int32_t) 3823 * atomic disable/enable sets. 3824 * first u_int32_t contains sets to be disabled, 3825 * second u_int32_t contains sets to be enabled. 3826 */ 3827 error = sooptcopyin(sopt, rulenum, 3828 2*sizeof(u_int32_t), sizeof(u_int32_t)); 3829 if (error) 3830 break; 3831 size = sopt->sopt_valsize; 3832 if (size == sizeof(u_int32_t) && rulenum[0] != 0) { 3833 /* delete or reassign, locking done in del_entry() */ 3834 error = del_entry(chain, rulenum[0]); 3835 } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */ 3836 IPFW_UH_WLOCK(chain); 3837 V_set_disable = 3838 (V_set_disable | rulenum[0]) & ~rulenum[1] & 3839 ~(1<<RESVD_SET); /* set RESVD_SET always enabled */ 3840 IPFW_UH_WUNLOCK(chain); 3841 } else 3842 error = EINVAL; 3843 break; 3844 3845 case IP_FW_ZERO: 3846 case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */ 3847 rulenum[0] = 0; 3848 if (sopt->sopt_val != 0) { 3849 error = sooptcopyin(sopt, rulenum, 3850 sizeof(u_int32_t), sizeof(u_int32_t)); 3851 if (error) 3852 break; 3853 } 3854 error = zero_entry(chain, rulenum[0], 3855 sopt->sopt_name == IP_FW_RESETLOG); 3856 break; 3857 3858 /*--- TABLE opcodes ---*/ 3859 case IP_FW_TABLE_ADD: 3860 case IP_FW_TABLE_DEL: 3861 { 3862 ipfw_table_entry ent; 3863 struct tentry_info tei; 3864 struct tid_info ti; 3865 struct table_value v; 3866 3867 error = sooptcopyin(sopt, &ent, 3868 sizeof(ent), sizeof(ent)); 3869 if (error) 3870 break; 3871 3872 memset(&tei, 0, sizeof(tei)); 3873 tei.paddr = &ent.addr; 3874 tei.subtype = AF_INET; 3875 tei.masklen = ent.masklen; 3876 ipfw_import_table_value_legacy(ent.value, &v); 3877 tei.pvalue = &v; 3878 memset(&ti, 0, sizeof(ti)); 3879 ti.uidx = ent.tbl; 3880 ti.type = IPFW_TABLE_CIDR; 3881 3882 error = (opt == IP_FW_TABLE_ADD) ? 3883 add_table_entry(chain, &ti, &tei, 0, 1) : 3884 del_table_entry(chain, &ti, &tei, 0, 1); 3885 } 3886 break; 3887 3888 3889 case IP_FW_TABLE_FLUSH: 3890 { 3891 u_int16_t tbl; 3892 struct tid_info ti; 3893 3894 error = sooptcopyin(sopt, &tbl, 3895 sizeof(tbl), sizeof(tbl)); 3896 if (error) 3897 break; 3898 memset(&ti, 0, sizeof(ti)); 3899 ti.uidx = tbl; 3900 error = flush_table(chain, &ti); 3901 } 3902 break; 3903 3904 case IP_FW_TABLE_GETSIZE: 3905 { 3906 u_int32_t tbl, cnt; 3907 struct tid_info ti; 3908 3909 if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl), 3910 sizeof(tbl)))) 3911 break; 3912 memset(&ti, 0, sizeof(ti)); 3913 ti.uidx = tbl; 3914 IPFW_RLOCK(chain); 3915 error = ipfw_count_table(chain, &ti, &cnt); 3916 IPFW_RUNLOCK(chain); 3917 if (error) 3918 break; 3919 error = sooptcopyout(sopt, &cnt, sizeof(cnt)); 3920 } 3921 break; 3922 3923 case IP_FW_TABLE_LIST: 3924 { 3925 ipfw_table *tbl; 3926 struct tid_info ti; 3927 3928 if (sopt->sopt_valsize < sizeof(*tbl)) { 3929 error = EINVAL; 3930 break; 3931 } 3932 size = sopt->sopt_valsize; 3933 tbl = malloc(size, M_TEMP, M_WAITOK); 3934 error = sooptcopyin(sopt, tbl, size, sizeof(*tbl)); 3935 if (error) { 3936 free(tbl, M_TEMP); 3937 break; 3938 } 3939 tbl->size = (size - sizeof(*tbl)) / 3940 sizeof(ipfw_table_entry); 3941 memset(&ti, 0, sizeof(ti)); 3942 ti.uidx = tbl->tbl; 3943 IPFW_RLOCK(chain); 3944 error = ipfw_dump_table_legacy(chain, &ti, tbl); 3945 IPFW_RUNLOCK(chain); 3946 if (error) { 3947 free(tbl, M_TEMP); 3948 break; 3949 } 3950 error = sooptcopyout(sopt, tbl, size); 3951 free(tbl, M_TEMP); 3952 } 3953 break; 3954 3955 /*--- NAT operations are protected by the IPFW_LOCK ---*/ 3956 case IP_FW_NAT_CFG: 3957 if (IPFW_NAT_LOADED) 3958 error = ipfw_nat_cfg_ptr(sopt); 3959 else { 3960 printf("IP_FW_NAT_CFG: %s\n", 3961 "ipfw_nat not present, please load it"); 3962 error = EINVAL; 3963 } 3964 break; 3965 3966 case IP_FW_NAT_DEL: 3967 if (IPFW_NAT_LOADED) 3968 error = ipfw_nat_del_ptr(sopt); 3969 else { 3970 printf("IP_FW_NAT_DEL: %s\n", 3971 "ipfw_nat not present, please load it"); 3972 error = EINVAL; 3973 } 3974 break; 3975 3976 case IP_FW_NAT_GET_CONFIG: 3977 if (IPFW_NAT_LOADED) 3978 error = ipfw_nat_get_cfg_ptr(sopt); 3979 else { 3980 printf("IP_FW_NAT_GET_CFG: %s\n", 3981 "ipfw_nat not present, please load it"); 3982 error = EINVAL; 3983 } 3984 break; 3985 3986 case IP_FW_NAT_GET_LOG: 3987 if (IPFW_NAT_LOADED) 3988 error = ipfw_nat_get_log_ptr(sopt); 3989 else { 3990 printf("IP_FW_NAT_GET_LOG: %s\n", 3991 "ipfw_nat not present, please load it"); 3992 error = EINVAL; 3993 } 3994 break; 3995 3996 default: 3997 printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name); 3998 error = EINVAL; 3999 } 4000 4001 return (error); 4002#undef RULE_MAXSIZE 4003} 4004#define RULE_MAXSIZE (256*sizeof(u_int32_t)) 4005 4006/* Functions to convert rules 7.2 <==> 8.0 */ 4007static int 4008convert_rule_to_7(struct ip_fw_rule0 *rule) 4009{ 4010 /* Used to modify original rule */ 4011 struct ip_fw7 *rule7 = (struct ip_fw7 *)rule; 4012 /* copy of original rule, version 8 */ 4013 struct ip_fw_rule0 *tmp; 4014 4015 /* Used to copy commands */ 4016 ipfw_insn *ccmd, *dst; 4017 int ll = 0, ccmdlen = 0; 4018 4019 tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); 4020 if (tmp == NULL) { 4021 return 1; //XXX error 4022 } 4023 bcopy(rule, tmp, RULE_MAXSIZE); 4024 4025 /* Copy fields */ 4026 //rule7->_pad = tmp->_pad; 4027 rule7->set = tmp->set; 4028 rule7->rulenum = tmp->rulenum; 4029 rule7->cmd_len = tmp->cmd_len; 4030 rule7->act_ofs = tmp->act_ofs; 4031 rule7->next_rule = (struct ip_fw7 *)tmp->next_rule; 4032 rule7->cmd_len = tmp->cmd_len; 4033 rule7->pcnt = tmp->pcnt; 4034 rule7->bcnt = tmp->bcnt; 4035 rule7->timestamp = tmp->timestamp; 4036 4037 /* Copy commands */ 4038 for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ; 4039 ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { 4040 ccmdlen = F_LEN(ccmd); 4041 4042 bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); 4043 4044 if (dst->opcode > O_NAT) 4045 /* O_REASS doesn't exists in 7.2 version, so 4046 * decrement opcode if it is after O_REASS 4047 */ 4048 dst->opcode--; 4049 4050 if (ccmdlen > ll) { 4051 printf("ipfw: opcode %d size truncated\n", 4052 ccmd->opcode); 4053 return EINVAL; 4054 } 4055 } 4056 free(tmp, M_TEMP); 4057 4058 return 0; 4059} 4060 4061static int 4062convert_rule_to_8(struct ip_fw_rule0 *rule) 4063{ 4064 /* Used to modify original rule */ 4065 struct ip_fw7 *rule7 = (struct ip_fw7 *) rule; 4066 4067 /* Used to copy commands */ 4068 ipfw_insn *ccmd, *dst; 4069 int ll = 0, ccmdlen = 0; 4070 4071 /* Copy of original rule */ 4072 struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); 4073 if (tmp == NULL) { 4074 return 1; //XXX error 4075 } 4076 4077 bcopy(rule7, tmp, RULE_MAXSIZE); 4078 4079 for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ; 4080 ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { 4081 ccmdlen = F_LEN(ccmd); 4082 4083 bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); 4084 4085 if (dst->opcode > O_NAT) 4086 /* O_REASS doesn't exists in 7.2 version, so 4087 * increment opcode if it is after O_REASS 4088 */ 4089 dst->opcode++; 4090 4091 if (ccmdlen > ll) { 4092 printf("ipfw: opcode %d size truncated\n", 4093 ccmd->opcode); 4094 return EINVAL; 4095 } 4096 } 4097 4098 rule->_pad = tmp->_pad; 4099 rule->set = tmp->set; 4100 rule->rulenum = tmp->rulenum; 4101 rule->cmd_len = tmp->cmd_len; 4102 rule->act_ofs = tmp->act_ofs; 4103 rule->next_rule = (struct ip_fw *)tmp->next_rule; 4104 rule->cmd_len = tmp->cmd_len; 4105 rule->id = 0; /* XXX see if is ok = 0 */ 4106 rule->pcnt = tmp->pcnt; 4107 rule->bcnt = tmp->bcnt; 4108 rule->timestamp = tmp->timestamp; 4109 4110 free (tmp, M_TEMP); 4111 return 0; 4112} 4113 4114/* 4115 * Named object api 4116 * 4117 */ 4118 4119void 4120ipfw_init_srv(struct ip_fw_chain *ch) 4121{ 4122 4123 ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT); 4124 ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT, 4125 M_IPFW, M_WAITOK | M_ZERO); 4126} 4127 4128void 4129ipfw_destroy_srv(struct ip_fw_chain *ch) 4130{ 4131 4132 free(ch->srvstate, M_IPFW); 4133 ipfw_objhash_destroy(ch->srvmap); 4134} 4135 4136/* 4137 * Allocate new bitmask which can be used to enlarge/shrink 4138 * named instance index. 4139 */ 4140void 4141ipfw_objhash_bitmap_alloc(uint32_t items, void **idx, int *pblocks) 4142{ 4143 size_t size; 4144 int max_blocks; 4145 u_long *idx_mask; 4146 4147 KASSERT((items % BLOCK_ITEMS) == 0, 4148 ("bitmask size needs to power of 2 and greater or equal to %zu", 4149 BLOCK_ITEMS)); 4150 4151 max_blocks = items / BLOCK_ITEMS; 4152 size = items / 8; 4153 idx_mask = malloc(size * IPFW_MAX_SETS, M_IPFW, M_WAITOK); 4154 /* Mark all as free */ 4155 memset(idx_mask, 0xFF, size * IPFW_MAX_SETS); 4156 *idx_mask &= ~(u_long)1; /* Skip index 0 */ 4157 4158 *idx = idx_mask; 4159 *pblocks = max_blocks; 4160} 4161 4162/* 4163 * Copy current bitmask index to new one. 4164 */ 4165void 4166ipfw_objhash_bitmap_merge(struct namedobj_instance *ni, void **idx, int *blocks) 4167{ 4168 int old_blocks, new_blocks; 4169 u_long *old_idx, *new_idx; 4170 int i; 4171 4172 old_idx = ni->idx_mask; 4173 old_blocks = ni->max_blocks; 4174 new_idx = *idx; 4175 new_blocks = *blocks; 4176 4177 for (i = 0; i < IPFW_MAX_SETS; i++) { 4178 memcpy(&new_idx[new_blocks * i], &old_idx[old_blocks * i], 4179 old_blocks * sizeof(u_long)); 4180 } 4181} 4182 4183/* 4184 * Swaps current @ni index with new one. 4185 */ 4186void 4187ipfw_objhash_bitmap_swap(struct namedobj_instance *ni, void **idx, int *blocks) 4188{ 4189 int old_blocks; 4190 u_long *old_idx; 4191 4192 old_idx = ni->idx_mask; 4193 old_blocks = ni->max_blocks; 4194 4195 ni->idx_mask = *idx; 4196 ni->max_blocks = *blocks; 4197 4198 /* Save old values */ 4199 *idx = old_idx; 4200 *blocks = old_blocks; 4201} 4202 4203void 4204ipfw_objhash_bitmap_free(void *idx, int blocks) 4205{ 4206 4207 free(idx, M_IPFW); 4208} 4209 4210/* 4211 * Creates named hash instance. 4212 * Must be called without holding any locks. 4213 * Return pointer to new instance. 4214 */ 4215struct namedobj_instance * 4216ipfw_objhash_create(uint32_t items) 4217{ 4218 struct namedobj_instance *ni; 4219 int i; 4220 size_t size; 4221 4222 size = sizeof(struct namedobj_instance) + 4223 sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE + 4224 sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE; 4225 4226 ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO); 4227 ni->nn_size = NAMEDOBJ_HASH_SIZE; 4228 ni->nv_size = NAMEDOBJ_HASH_SIZE; 4229 4230 ni->names = (struct namedobjects_head *)(ni +1); 4231 ni->values = &ni->names[ni->nn_size]; 4232 4233 for (i = 0; i < ni->nn_size; i++) 4234 TAILQ_INIT(&ni->names[i]); 4235 4236 for (i = 0; i < ni->nv_size; i++) 4237 TAILQ_INIT(&ni->values[i]); 4238 4239 /* Set default hashing/comparison functions */ 4240 ni->hash_f = objhash_hash_name; 4241 ni->cmp_f = objhash_cmp_name; 4242 4243 /* Allocate bitmask separately due to possible resize */ 4244 ipfw_objhash_bitmap_alloc(items, (void*)&ni->idx_mask, &ni->max_blocks); 4245 4246 return (ni); 4247} 4248 4249void 4250ipfw_objhash_destroy(struct namedobj_instance *ni) 4251{ 4252 4253 free(ni->idx_mask, M_IPFW); 4254 free(ni, M_IPFW); 4255} 4256 4257void 4258ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f, 4259 objhash_cmp_f *cmp_f) 4260{ 4261 4262 ni->hash_f = hash_f; 4263 ni->cmp_f = cmp_f; 4264} 4265 4266static uint32_t 4267objhash_hash_name(struct namedobj_instance *ni, const void *name, uint32_t set) 4268{ 4269 4270 return (fnv_32_str((const char *)name, FNV1_32_INIT)); 4271} 4272 4273static int 4274objhash_cmp_name(struct named_object *no, const void *name, uint32_t set) 4275{ 4276 4277 if ((strcmp(no->name, (const char *)name) == 0) && (no->set == set)) 4278 return (0); 4279 4280 return (1); 4281} 4282 4283static uint32_t 4284objhash_hash_idx(struct namedobj_instance *ni, uint32_t val) 4285{ 4286 uint32_t v; 4287 4288 v = val % (ni->nv_size - 1); 4289 4290 return (v); 4291} 4292 4293struct named_object * 4294ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set, char *name) 4295{ 4296 struct named_object *no; 4297 uint32_t hash; 4298 4299 hash = ni->hash_f(ni, name, set) % ni->nn_size; 4300 4301 TAILQ_FOREACH(no, &ni->names[hash], nn_next) { 4302 if (ni->cmp_f(no, name, set) == 0) 4303 return (no); 4304 } 4305 4306 return (NULL); 4307} 4308 4309/* 4310 * Find named object by @uid. 4311 * Check @tlvs for valid data inside. 4312 * 4313 * Returns pointer to found TLV or NULL. 4314 */ 4315ipfw_obj_ntlv * 4316ipfw_find_name_tlv_type(void *tlvs, int len, uint16_t uidx, uint32_t etlv) 4317{ 4318 ipfw_obj_ntlv *ntlv; 4319 uintptr_t pa, pe; 4320 int l; 4321 4322 pa = (uintptr_t)tlvs; 4323 pe = pa + len; 4324 l = 0; 4325 for (; pa < pe; pa += l) { 4326 ntlv = (ipfw_obj_ntlv *)pa; 4327 l = ntlv->head.length; 4328 4329 if (l != sizeof(*ntlv)) 4330 return (NULL); 4331 4332 if (ntlv->idx != uidx) 4333 continue; 4334 /* 4335 * When userland has specified zero TLV type, do 4336 * not compare it with eltv. In some cases userland 4337 * doesn't know what type should it have. Use only 4338 * uidx and name for search named_object. 4339 */ 4340 if (ntlv->head.type != 0 && 4341 ntlv->head.type != (uint16_t)etlv) 4342 continue; 4343 4344 if (ipfw_check_object_name_generic(ntlv->name) != 0) 4345 return (NULL); 4346 4347 return (ntlv); 4348 } 4349 4350 return (NULL); 4351} 4352 4353/* 4354 * Finds object config based on either legacy index 4355 * or name in ntlv. 4356 * Note @ti structure contains unchecked data from userland. 4357 * 4358 * Returns 0 in success and fills in @pno with found config 4359 */ 4360int 4361ipfw_objhash_find_type(struct namedobj_instance *ni, struct tid_info *ti, 4362 uint32_t etlv, struct named_object **pno) 4363{ 4364 char *name; 4365 ipfw_obj_ntlv *ntlv; 4366 uint32_t set; 4367 4368 if (ti->tlvs == NULL) 4369 return (EINVAL); 4370 4371 ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, etlv); 4372 if (ntlv == NULL) 4373 return (EINVAL); 4374 name = ntlv->name; 4375 4376 /* 4377 * Use set provided by @ti instead of @ntlv one. 4378 * This is needed due to different sets behavior 4379 * controlled by V_fw_tables_sets. 4380 */ 4381 set = ti->set; 4382 *pno = ipfw_objhash_lookup_name(ni, set, name); 4383 if (*pno == NULL) 4384 return (ESRCH); 4385 return (0); 4386} 4387 4388/* 4389 * Find named object by name, considering also its TLV type. 4390 */ 4391struct named_object * 4392ipfw_objhash_lookup_name_type(struct namedobj_instance *ni, uint32_t set, 4393 uint32_t type, const char *name) 4394{ 4395 struct named_object *no; 4396 uint32_t hash; 4397 4398 hash = ni->hash_f(ni, name, set) % ni->nn_size; 4399 4400 TAILQ_FOREACH(no, &ni->names[hash], nn_next) { 4401 if (ni->cmp_f(no, name, set) == 0 && 4402 no->etlv == (uint16_t)type) 4403 return (no); 4404 } 4405 4406 return (NULL); 4407} 4408 4409struct named_object * 4410ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint16_t kidx) 4411{ 4412 struct named_object *no; 4413 uint32_t hash; 4414 4415 hash = objhash_hash_idx(ni, kidx); 4416 4417 TAILQ_FOREACH(no, &ni->values[hash], nv_next) { 4418 if (no->kidx == kidx) 4419 return (no); 4420 } 4421 4422 return (NULL); 4423} 4424 4425int 4426ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a, 4427 struct named_object *b) 4428{ 4429 4430 if ((strcmp(a->name, b->name) == 0) && a->set == b->set) 4431 return (1); 4432 4433 return (0); 4434} 4435 4436void 4437ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no) 4438{ 4439 uint32_t hash; 4440 4441 hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size; 4442 TAILQ_INSERT_HEAD(&ni->names[hash], no, nn_next); 4443 4444 hash = objhash_hash_idx(ni, no->kidx); 4445 TAILQ_INSERT_HEAD(&ni->values[hash], no, nv_next); 4446 4447 ni->count++; 4448} 4449 4450void 4451ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no) 4452{ 4453 uint32_t hash; 4454 4455 hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size; 4456 TAILQ_REMOVE(&ni->names[hash], no, nn_next); 4457 4458 hash = objhash_hash_idx(ni, no->kidx); 4459 TAILQ_REMOVE(&ni->values[hash], no, nv_next); 4460 4461 ni->count--; 4462} 4463 4464uint32_t 4465ipfw_objhash_count(struct namedobj_instance *ni) 4466{ 4467 4468 return (ni->count); 4469} 4470 4471uint32_t 4472ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type) 4473{ 4474 struct named_object *no; 4475 uint32_t count; 4476 int i; 4477 4478 count = 0; 4479 for (i = 0; i < ni->nn_size; i++) { 4480 TAILQ_FOREACH(no, &ni->names[i], nn_next) { 4481 if (no->etlv == type) 4482 count++; 4483 } 4484 } 4485 return (count); 4486} 4487 4488/* 4489 * Runs @func for each found named object. 4490 * It is safe to delete objects from callback 4491 */ 4492int 4493ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg) 4494{ 4495 struct named_object *no, *no_tmp; 4496 int i, ret; 4497 4498 for (i = 0; i < ni->nn_size; i++) { 4499 TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) { 4500 ret = f(ni, no, arg); 4501 if (ret != 0) 4502 return (ret); 4503 } 4504 } 4505 return (0); 4506} 4507 4508/* 4509 * Runs @f for each found named object with type @type. 4510 * It is safe to delete objects from callback 4511 */ 4512int 4513ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f, 4514 void *arg, uint16_t type) 4515{ 4516 struct named_object *no, *no_tmp; 4517 int i, ret; 4518 4519 for (i = 0; i < ni->nn_size; i++) { 4520 TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) { 4521 if (no->etlv != type) 4522 continue; 4523 ret = f(ni, no, arg); 4524 if (ret != 0) 4525 return (ret); 4526 } 4527 } 4528 return (0); 4529} 4530 4531/* 4532 * Removes index from given set. 4533 * Returns 0 on success. 4534 */ 4535int 4536ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx) 4537{ 4538 u_long *mask; 4539 int i, v; 4540 4541 i = idx / BLOCK_ITEMS; 4542 v = idx % BLOCK_ITEMS; 4543 4544 if (i >= ni->max_blocks) 4545 return (1); 4546 4547 mask = &ni->idx_mask[i]; 4548 4549 if ((*mask & ((u_long)1 << v)) != 0) 4550 return (1); 4551 4552 /* Mark as free */ 4553 *mask |= (u_long)1 << v; 4554 4555 /* Update free offset */ 4556 if (ni->free_off[0] > i) 4557 ni->free_off[0] = i; 4558 4559 return (0); 4560} 4561 4562/* 4563 * Allocate new index in given instance and stores in in @pidx. 4564 * Returns 0 on success. 4565 */ 4566int 4567ipfw_objhash_alloc_idx(void *n, uint16_t *pidx) 4568{ 4569 struct namedobj_instance *ni; 4570 u_long *mask; 4571 int i, off, v; 4572 4573 ni = (struct namedobj_instance *)n; 4574 4575 off = ni->free_off[0]; 4576 mask = &ni->idx_mask[off]; 4577 4578 for (i = off; i < ni->max_blocks; i++, mask++) { 4579 if ((v = ffsl(*mask)) == 0) 4580 continue; 4581 4582 /* Mark as busy */ 4583 *mask &= ~ ((u_long)1 << (v - 1)); 4584 4585 ni->free_off[0] = i; 4586 4587 v = BLOCK_ITEMS * i + v - 1; 4588 4589 *pidx = v; 4590 return (0); 4591 } 4592 4593 return (1); 4594} 4595 4596/* end of file */ 4597