ip_fw_sockopt.c revision 204713
1/*- 2 * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa 3 * 4 * Supported by: Valeria Paoli 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_sockopt.c 204713 2010-03-04 16:52:26Z luigi $"); 30 31/* 32 * Sockopt support for ipfw. The routines here implement 33 * the upper half of the ipfw code. 34 */ 35 36#if !defined(KLD_MODULE) 37#include "opt_ipfw.h" 38#include "opt_ipdivert.h" 39#include "opt_ipdn.h" 40#include "opt_inet.h" 41#ifndef INET 42#error IPFIREWALL requires INET. 43#endif /* INET */ 44#endif 45#include "opt_inet6.h" 46#include "opt_ipsec.h" 47 48#include <sys/param.h> 49#include <sys/systm.h> 50#include <sys/malloc.h> 51#include <sys/mbuf.h> /* struct m_tag used by nested headers */ 52#include <sys/kernel.h> 53#include <sys/lock.h> 54#include <sys/priv.h> 55#include <sys/proc.h> 56#include <sys/rwlock.h> 57#include <sys/socket.h> 58#include <sys/socketvar.h> 59#include <sys/sysctl.h> 60#include <sys/syslog.h> 61#include <net/if.h> 62#include <net/route.h> 63#include <net/vnet.h> 64 65#include <netinet/in.h> 66#include <netinet/ip_var.h> /* hooks */ 67#include <netinet/ip_fw.h> 68#include <netinet/ipfw/ip_fw_private.h> 69 70#ifdef MAC 71#include <security/mac/mac_framework.h> 72#endif 73 74MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); 75 76/* 77 * static variables followed by global ones (none in this file) 78 */ 79 80/* 81 * Find the smallest rule >= key, id. 82 * We could use bsearch but it is so simple that we code it directly 83 */ 84int 85ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id) 86{ 87 int i, lo, hi; 88 struct ip_fw *r; 89 90 for (lo = 0, hi = chain->n_rules - 1; lo < hi;) { 91 i = (lo + hi) / 2; 92 r = chain->map[i]; 93 if (r->rulenum < key) 94 lo = i + 1; /* continue from the next one */ 95 else if (r->rulenum > key) 96 hi = i; /* this might be good */ 97 else if (r->id < id) 98 lo = i + 1; /* continue from the next one */ 99 else /* r->id >= id */ 100 hi = i; /* this might be good */ 101 }; 102 return hi; 103} 104 105/* 106 * allocate a new map, returns the chain locked. extra is the number 107 * of entries to add or delete. 108 */ 109static struct ip_fw ** 110get_map(struct ip_fw_chain *chain, int extra, int locked) 111{ 112 113 for (;;) { 114 struct ip_fw **map; 115 int i; 116 117 i = chain->n_rules + extra; 118 map = malloc(i * sizeof(struct ip_fw *), M_IPFW, 119 locked ? M_NOWAIT : M_WAITOK); 120 if (map == NULL) { 121 printf("%s: cannot allocate map\n", __FUNCTION__); 122 return NULL; 123 } 124 if (!locked) 125 IPFW_UH_WLOCK(chain); 126 if (i >= chain->n_rules + extra) /* good */ 127 return map; 128 /* otherwise we lost the race, free and retry */ 129 if (!locked) 130 IPFW_UH_WUNLOCK(chain); 131 free(map, M_IPFW); 132 } 133} 134 135/* 136 * swap the maps. It is supposed to be called with IPFW_UH_WLOCK 137 */ 138static struct ip_fw ** 139swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len) 140{ 141 struct ip_fw **old_map; 142 143 IPFW_WLOCK(chain); 144 chain->id++; 145 chain->n_rules = new_len; 146 old_map = chain->map; 147 chain->map = new_map; 148 IPFW_WUNLOCK(chain); 149 return old_map; 150} 151 152/* 153 * Add a new rule to the list. Copy the rule into a malloc'ed area, then 154 * possibly create a rule number and add the rule to the list. 155 * Update the rule_number in the input struct so the caller knows it as well. 156 * XXX DO NOT USE FOR THE DEFAULT RULE. 157 * Must be called without IPFW_UH held 158 */ 159int 160ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule) 161{ 162 struct ip_fw *rule; 163 int i, l, insert_before; 164 struct ip_fw **map; /* the new array of pointers */ 165 166 if (chain->rules == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE-1) 167 return (EINVAL); 168 169 l = RULESIZE(input_rule); 170 rule = malloc(l, M_IPFW, M_WAITOK | M_ZERO); 171 if (rule == NULL) 172 return (ENOSPC); 173 /* get_map returns with IPFW_UH_WLOCK if successful */ 174 map = get_map(chain, 1, 0 /* not locked */); 175 if (map == NULL) { 176 free(rule, M_IPFW); 177 return ENOSPC; 178 } 179 180 bcopy(input_rule, rule, l); 181 /* clear fields not settable from userland */ 182 rule->x_next = NULL; 183 rule->next_rule = NULL; 184 rule->pcnt = 0; 185 rule->bcnt = 0; 186 rule->timestamp = 0; 187 188 if (V_autoinc_step < 1) 189 V_autoinc_step = 1; 190 else if (V_autoinc_step > 1000) 191 V_autoinc_step = 1000; 192 /* find the insertion point, we will insert before */ 193 insert_before = rule->rulenum ? rule->rulenum + 1 : IPFW_DEFAULT_RULE; 194 i = ipfw_find_rule(chain, insert_before, 0); 195 /* duplicate first part */ 196 if (i > 0) 197 bcopy(chain->map, map, i * sizeof(struct ip_fw *)); 198 map[i] = rule; 199 /* duplicate remaining part, we always have the default rule */ 200 bcopy(chain->map + i, map + i + 1, 201 sizeof(struct ip_fw *) *(chain->n_rules - i)); 202 if (rule->rulenum == 0) { 203 /* write back the number */ 204 rule->rulenum = i > 0 ? map[i-1]->rulenum : 0; 205 if (rule->rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) 206 rule->rulenum += V_autoinc_step; 207 input_rule->rulenum = rule->rulenum; 208 } 209 210 rule->id = chain->id + 1; 211 map = swap_map(chain, map, chain->n_rules + 1); 212 chain->static_len += l; 213 IPFW_UH_WUNLOCK(chain); 214 if (map) 215 free(map, M_IPFW); 216 return (0); 217} 218 219/* 220 * Reclaim storage associated with a list of rules. This is 221 * typically the list created using remove_rule. 222 * A NULL pointer on input is handled correctly. 223 */ 224void 225ipfw_reap_rules(struct ip_fw *head) 226{ 227 struct ip_fw *rule; 228 229 while ((rule = head) != NULL) { 230 head = head->x_next; 231 free(rule, M_IPFW); 232 } 233} 234 235/** 236 * Remove all rules with given number, and also do set manipulation. 237 * Assumes chain != NULL && *chain != NULL. 238 * 239 * The argument is an u_int32_t. The low 16 bit are the rule or set number, 240 * the next 8 bits are the new set, the top 8 bits are the command: 241 * 242 * 0 delete rules with given number 243 * 1 delete rules with given set number 244 * 2 move rules with given number to new set 245 * 3 move rules with given set number to new set 246 * 4 swap sets with given numbers 247 * 5 delete rules with given number and with given set number 248 */ 249static int 250del_entry(struct ip_fw_chain *chain, u_int32_t arg) 251{ 252 struct ip_fw *rule; 253 uint32_t rulenum; /* rule or old_set */ 254 uint8_t cmd, new_set; 255 int start, end = 0, i, ofs, n; 256 struct ip_fw **map = NULL; 257 int error = 0; 258 259 rulenum = arg & 0xffff; 260 cmd = (arg >> 24) & 0xff; 261 new_set = (arg >> 16) & 0xff; 262 263 if (cmd > 5 || new_set > RESVD_SET) 264 return EINVAL; 265 if (cmd == 0 || cmd == 2 || cmd == 5) { 266 if (rulenum >= IPFW_DEFAULT_RULE) 267 return EINVAL; 268 } else { 269 if (rulenum > RESVD_SET) /* old_set */ 270 return EINVAL; 271 } 272 273 IPFW_UH_WLOCK(chain); /* prevent conflicts among the writers */ 274 chain->reap = NULL; /* prepare for deletions */ 275 276 switch (cmd) { 277 case 0: /* delete rules with given number (0 is special means all) */ 278 case 1: /* delete all rules with given set number, rule->set == rulenum */ 279 case 5: /* delete rules with given number and with given set number. 280 * rulenum - given rule number; 281 * new_set - given set number. 282 */ 283 /* locate first rule to delete (start), the one after the 284 * last one (end), and count how many rules to delete (n) 285 */ 286 n = 0; 287 if (cmd == 1) { /* look for a specific set, must scan all */ 288 for (start = -1, i = 0; i < chain->n_rules; i++) { 289 if (chain->map[start]->set != rulenum) 290 continue; 291 if (start < 0) 292 start = i; 293 end = i; 294 n++; 295 } 296 end++; /* first non-matching */ 297 } else { 298 start = ipfw_find_rule(chain, rulenum, 0); 299 for (end = start; end < chain->n_rules; end++) { 300 rule = chain->map[end]; 301 if (rulenum > 0 && rule->rulenum != rulenum) 302 break; 303 if (rule->set != RESVD_SET && 304 (cmd == 0 || rule->set == new_set) ) 305 n++; 306 } 307 } 308 if (n == 0 && arg == 0) 309 break; /* special case, flush on empty ruleset */ 310 /* allocate the map, if needed */ 311 if (n > 0) 312 map = get_map(chain, -n, 1 /* locked */); 313 if (n == 0 || map == NULL) { 314 error = EINVAL; 315 break; 316 } 317 /* copy the initial part of the map */ 318 if (start > 0) 319 bcopy(chain->map, map, start * sizeof(struct ip_fw *)); 320 /* copy active rules between start and end */ 321 for (i = ofs = start; i < end; i++) { 322 rule = chain->map[i]; 323 if (!(rule->set != RESVD_SET && 324 (cmd == 0 || rule->set == new_set) )) 325 map[ofs++] = chain->map[i]; 326 } 327 /* finally the tail */ 328 bcopy(chain->map + end, map + ofs, 329 (chain->n_rules - end) * sizeof(struct ip_fw *)); 330 map = swap_map(chain, map, chain->n_rules - n); 331 /* now remove the rules deleted */ 332 for (i = start; i < end; i++) { 333 rule = map[i]; 334 if (rule->set != RESVD_SET && 335 (cmd == 0 || rule->set == new_set) ) { 336 int l = RULESIZE(rule); 337 338 chain->static_len -= l; 339 ipfw_remove_dyn_children(rule); 340 rule->x_next = chain->reap; 341 chain->reap = rule; 342 } 343 } 344 break; 345 346 case 2: /* move rules with given number to new set */ 347 for (i = 0; i < chain->n_rules; i++) { 348 rule = chain->map[i]; 349 if (rule->rulenum == rulenum) 350 rule->set = new_set; 351 } 352 break; 353 354 case 3: /* move rules with given set number to new set */ 355 for (i = 0; i < chain->n_rules; i++) { 356 rule = chain->map[i]; 357 if (rule->set == rulenum) 358 rule->set = new_set; 359 } 360 break; 361 362 case 4: /* swap two sets */ 363 for (i = 0; i < chain->n_rules; i++) { 364 rule = chain->map[i]; 365 if (rule->set == rulenum) 366 rule->set = new_set; 367 else if (rule->set == new_set) 368 rule->set = rulenum; 369 } 370 break; 371 } 372 rule = chain->reap; 373 chain->reap = NULL; 374 IPFW_UH_WUNLOCK(chain); 375 ipfw_reap_rules(rule); 376 if (map) 377 free(map, M_IPFW); 378 return error; 379} 380 381/* 382 * Clear counters for a specific rule. 383 * Normally run under IPFW_UH_RLOCK, but these are idempotent ops 384 * so we only care that rules do not disappear. 385 */ 386static void 387clear_counters(struct ip_fw *rule, int log_only) 388{ 389 ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule); 390 391 if (log_only == 0) { 392 rule->bcnt = rule->pcnt = 0; 393 rule->timestamp = 0; 394 } 395 if (l->o.opcode == O_LOG) 396 l->log_left = l->max_log; 397} 398 399/** 400 * Reset some or all counters on firewall rules. 401 * The argument `arg' is an u_int32_t. The low 16 bit are the rule number, 402 * the next 8 bits are the set number, the top 8 bits are the command: 403 * 0 work with rules from all set's; 404 * 1 work with rules only from specified set. 405 * Specified rule number is zero if we want to clear all entries. 406 * log_only is 1 if we only want to reset logs, zero otherwise. 407 */ 408static int 409zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only) 410{ 411 struct ip_fw *rule; 412 char *msg; 413 int i; 414 415 uint16_t rulenum = arg & 0xffff; 416 uint8_t set = (arg >> 16) & 0xff; 417 uint8_t cmd = (arg >> 24) & 0xff; 418 419 if (cmd > 1) 420 return (EINVAL); 421 if (cmd == 1 && set > RESVD_SET) 422 return (EINVAL); 423 424 IPFW_UH_RLOCK(chain); 425 if (rulenum == 0) { 426 V_norule_counter = 0; 427 for (i = 0; i < chain->n_rules; i++) { 428 rule = chain->map[i]; 429 /* Skip rules not in our set. */ 430 if (cmd == 1 && rule->set != set) 431 continue; 432 clear_counters(rule, log_only); 433 } 434 msg = log_only ? "All logging counts reset" : 435 "Accounting cleared"; 436 } else { 437 int cleared = 0; 438 for (i = 0; i < chain->n_rules; i++) { 439 rule = chain->map[i]; 440 if (rule->rulenum == rulenum) { 441 if (cmd == 0 || rule->set == set) 442 clear_counters(rule, log_only); 443 cleared = 1; 444 } 445 if (rule->rulenum > rulenum) 446 break; 447 } 448 if (!cleared) { /* we did not find any matching rules */ 449 IPFW_WUNLOCK(chain); 450 return (EINVAL); 451 } 452 msg = log_only ? "logging count reset" : "cleared"; 453 } 454 IPFW_UH_RUNLOCK(chain); 455 456 if (V_fw_verbose) { 457 int lev = LOG_SECURITY | LOG_NOTICE; 458 459 if (rulenum) 460 log(lev, "ipfw: Entry %d %s.\n", rulenum, msg); 461 else 462 log(lev, "ipfw: %s.\n", msg); 463 } 464 return (0); 465} 466 467/* 468 * Check validity of the structure before insert. 469 * Rules are simple, so this mostly need to check rule sizes. 470 */ 471static int 472check_ipfw_struct(struct ip_fw *rule, int size) 473{ 474 int l, cmdlen = 0; 475 int have_action=0; 476 ipfw_insn *cmd; 477 478 if (size < sizeof(*rule)) { 479 printf("ipfw: rule too short\n"); 480 return (EINVAL); 481 } 482 /* first, check for valid size */ 483 l = RULESIZE(rule); 484 if (l != size) { 485 printf("ipfw: size mismatch (have %d want %d)\n", size, l); 486 return (EINVAL); 487 } 488 if (rule->act_ofs >= rule->cmd_len) { 489 printf("ipfw: bogus action offset (%u > %u)\n", 490 rule->act_ofs, rule->cmd_len - 1); 491 return (EINVAL); 492 } 493 /* 494 * Now go for the individual checks. Very simple ones, basically only 495 * instruction sizes. 496 */ 497 for (l = rule->cmd_len, cmd = rule->cmd ; 498 l > 0 ; l -= cmdlen, cmd += cmdlen) { 499 cmdlen = F_LEN(cmd); 500 if (cmdlen > l) { 501 printf("ipfw: opcode %d size truncated\n", 502 cmd->opcode); 503 return EINVAL; 504 } 505 switch (cmd->opcode) { 506 case O_PROBE_STATE: 507 case O_KEEP_STATE: 508 case O_PROTO: 509 case O_IP_SRC_ME: 510 case O_IP_DST_ME: 511 case O_LAYER2: 512 case O_IN: 513 case O_FRAG: 514 case O_DIVERTED: 515 case O_IPOPT: 516 case O_IPTOS: 517 case O_IPPRECEDENCE: 518 case O_IPVER: 519 case O_TCPWIN: 520 case O_TCPFLAGS: 521 case O_TCPOPTS: 522 case O_ESTAB: 523 case O_VERREVPATH: 524 case O_VERSRCREACH: 525 case O_ANTISPOOF: 526 case O_IPSEC: 527#ifdef INET6 528 case O_IP6_SRC_ME: 529 case O_IP6_DST_ME: 530 case O_EXT_HDR: 531 case O_IP6: 532#endif 533 case O_IP4: 534 case O_TAG: 535 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 536 goto bad_size; 537 break; 538 539 case O_FIB: 540 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 541 goto bad_size; 542 if (cmd->arg1 >= rt_numfibs) { 543 printf("ipfw: invalid fib number %d\n", 544 cmd->arg1); 545 return EINVAL; 546 } 547 break; 548 549 case O_SETFIB: 550 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 551 goto bad_size; 552 if (cmd->arg1 >= rt_numfibs) { 553 printf("ipfw: invalid fib number %d\n", 554 cmd->arg1); 555 return EINVAL; 556 } 557 goto check_action; 558 559 case O_UID: 560 case O_GID: 561 case O_JAIL: 562 case O_IP_SRC: 563 case O_IP_DST: 564 case O_TCPSEQ: 565 case O_TCPACK: 566 case O_PROB: 567 case O_ICMPTYPE: 568 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 569 goto bad_size; 570 break; 571 572 case O_LIMIT: 573 if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) 574 goto bad_size; 575 break; 576 577 case O_LOG: 578 if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) 579 goto bad_size; 580 581 ((ipfw_insn_log *)cmd)->log_left = 582 ((ipfw_insn_log *)cmd)->max_log; 583 584 break; 585 586 case O_IP_SRC_MASK: 587 case O_IP_DST_MASK: 588 /* only odd command lengths */ 589 if ( !(cmdlen & 1) || cmdlen > 31) 590 goto bad_size; 591 break; 592 593 case O_IP_SRC_SET: 594 case O_IP_DST_SET: 595 if (cmd->arg1 == 0 || cmd->arg1 > 256) { 596 printf("ipfw: invalid set size %d\n", 597 cmd->arg1); 598 return EINVAL; 599 } 600 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 601 (cmd->arg1+31)/32 ) 602 goto bad_size; 603 break; 604 605 case O_IP_SRC_LOOKUP: 606 case O_IP_DST_LOOKUP: 607 if (cmd->arg1 >= IPFW_TABLES_MAX) { 608 printf("ipfw: invalid table number %d\n", 609 cmd->arg1); 610 return (EINVAL); 611 } 612 if (cmdlen != F_INSN_SIZE(ipfw_insn) && 613 cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 && 614 cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 615 goto bad_size; 616 break; 617 618 case O_MACADDR2: 619 if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) 620 goto bad_size; 621 break; 622 623 case O_NOP: 624 case O_IPID: 625 case O_IPTTL: 626 case O_IPLEN: 627 case O_TCPDATALEN: 628 case O_TAGGED: 629 if (cmdlen < 1 || cmdlen > 31) 630 goto bad_size; 631 break; 632 633 case O_MAC_TYPE: 634 case O_IP_SRCPORT: 635 case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ 636 if (cmdlen < 2 || cmdlen > 31) 637 goto bad_size; 638 break; 639 640 case O_RECV: 641 case O_XMIT: 642 case O_VIA: 643 if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) 644 goto bad_size; 645 break; 646 647 case O_ALTQ: 648 if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) 649 goto bad_size; 650 break; 651 652 case O_PIPE: 653 case O_QUEUE: 654 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 655 goto bad_size; 656 goto check_action; 657 658 case O_FORWARD_IP: 659#ifdef IPFIREWALL_FORWARD 660 if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) 661 goto bad_size; 662 goto check_action; 663#else 664 return EINVAL; 665#endif 666 667 case O_DIVERT: 668 case O_TEE: 669 if (ip_divert_ptr == NULL) 670 return EINVAL; 671 else 672 goto check_size; 673 case O_NETGRAPH: 674 case O_NGTEE: 675 if (ng_ipfw_input_p == NULL) 676 return EINVAL; 677 else 678 goto check_size; 679 case O_NAT: 680 if (!IPFW_NAT_LOADED) 681 return EINVAL; 682 if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) 683 goto bad_size; 684 goto check_action; 685 case O_FORWARD_MAC: /* XXX not implemented yet */ 686 case O_CHECK_STATE: 687 case O_COUNT: 688 case O_ACCEPT: 689 case O_DENY: 690 case O_REJECT: 691#ifdef INET6 692 case O_UNREACH6: 693#endif 694 case O_SKIPTO: 695 case O_REASS: 696check_size: 697 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 698 goto bad_size; 699check_action: 700 if (have_action) { 701 printf("ipfw: opcode %d, multiple actions" 702 " not allowed\n", 703 cmd->opcode); 704 return EINVAL; 705 } 706 have_action = 1; 707 if (l != cmdlen) { 708 printf("ipfw: opcode %d, action must be" 709 " last opcode\n", 710 cmd->opcode); 711 return EINVAL; 712 } 713 break; 714#ifdef INET6 715 case O_IP6_SRC: 716 case O_IP6_DST: 717 if (cmdlen != F_INSN_SIZE(struct in6_addr) + 718 F_INSN_SIZE(ipfw_insn)) 719 goto bad_size; 720 break; 721 722 case O_FLOW6ID: 723 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 724 ((ipfw_insn_u32 *)cmd)->o.arg1) 725 goto bad_size; 726 break; 727 728 case O_IP6_SRC_MASK: 729 case O_IP6_DST_MASK: 730 if ( !(cmdlen & 1) || cmdlen > 127) 731 goto bad_size; 732 break; 733 case O_ICMP6TYPE: 734 if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) 735 goto bad_size; 736 break; 737#endif 738 739 default: 740 switch (cmd->opcode) { 741#ifndef INET6 742 case O_IP6_SRC_ME: 743 case O_IP6_DST_ME: 744 case O_EXT_HDR: 745 case O_IP6: 746 case O_UNREACH6: 747 case O_IP6_SRC: 748 case O_IP6_DST: 749 case O_FLOW6ID: 750 case O_IP6_SRC_MASK: 751 case O_IP6_DST_MASK: 752 case O_ICMP6TYPE: 753 printf("ipfw: no IPv6 support in kernel\n"); 754 return EPROTONOSUPPORT; 755#endif 756 default: 757 printf("ipfw: opcode %d, unknown opcode\n", 758 cmd->opcode); 759 return EINVAL; 760 } 761 } 762 } 763 if (have_action == 0) { 764 printf("ipfw: missing action\n"); 765 return EINVAL; 766 } 767 return 0; 768 769bad_size: 770 printf("ipfw: opcode %d size %d wrong\n", 771 cmd->opcode, cmdlen); 772 return EINVAL; 773} 774 775 776/* 777 * Translation of requests for compatibility with FreeBSD 7.2/8. 778 * a static variable tells us if we have an old client from userland, 779 * and if necessary we translate requests and responses between the 780 * two formats. 781 */ 782static int is7 = 0; 783 784struct ip_fw7 { 785 struct ip_fw7 *next; /* linked list of rules */ 786 struct ip_fw7 *next_rule; /* ptr to next [skipto] rule */ 787 /* 'next_rule' is used to pass up 'set_disable' status */ 788 789 uint16_t act_ofs; /* offset of action in 32-bit units */ 790 uint16_t cmd_len; /* # of 32-bit words in cmd */ 791 uint16_t rulenum; /* rule number */ 792 uint8_t set; /* rule set (0..31) */ 793 // #define RESVD_SET 31 /* set for default and persistent rules */ 794 uint8_t _pad; /* padding */ 795 // uint32_t id; /* rule id, only in v.8 */ 796 /* These fields are present in all rules. */ 797 uint64_t pcnt; /* Packet counter */ 798 uint64_t bcnt; /* Byte counter */ 799 uint32_t timestamp; /* tv_sec of last match */ 800 801 ipfw_insn cmd[1]; /* storage for commands */ 802}; 803 804 int convert_rule_to_7(struct ip_fw *rule); 805int convert_rule_to_8(struct ip_fw *rule); 806 807#ifndef RULESIZE7 808#define RULESIZE7(rule) (sizeof(struct ip_fw7) + \ 809 ((struct ip_fw7 *)(rule))->cmd_len * 4 - 4) 810#endif 811 812 813/* 814 * Copy the static and dynamic rules to the supplied buffer 815 * and return the amount of space actually used. 816 * Must be run under IPFW_UH_RLOCK 817 */ 818static size_t 819ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) 820{ 821 char *bp = buf; 822 char *ep = bp + space; 823 struct ip_fw *rule, *dst; 824 int l, i; 825 time_t boot_seconds; 826 827 boot_seconds = boottime.tv_sec; 828 for (i = 0; i < chain->n_rules; i++) { 829 rule = chain->map[i]; 830 831 if (is7) { 832 /* Convert rule to FreeBSd 7.2 format */ 833 l = RULESIZE7(rule); 834 if (bp + l + sizeof(uint32_t) <= ep) { 835 int error; 836 bcopy(rule, bp, l + sizeof(uint32_t)); 837 error = convert_rule_to_7((struct ip_fw *) bp); 838 if (error) 839 return 0; /*XXX correct? */ 840 /* 841 * XXX HACK. Store the disable mask in the "next" 842 * pointer in a wild attempt to keep the ABI the same. 843 * Why do we do this on EVERY rule? 844 */ 845 bcopy(&V_set_disable, 846 &(((struct ip_fw7 *)bp)->next_rule), 847 sizeof(V_set_disable)); 848 if (((struct ip_fw7 *)bp)->timestamp) 849 ((struct ip_fw7 *)bp)->timestamp += boot_seconds; 850 bp += l; 851 } 852 continue; /* go to next rule */ 853 } 854 855 /* normal mode, don't touch rules */ 856 l = RULESIZE(rule); 857 if (bp + l > ep) { /* should not happen */ 858 printf("overflow dumping static rules\n"); 859 break; 860 } 861 dst = (struct ip_fw *)bp; 862 bcopy(rule, dst, l); 863 /* 864 * XXX HACK. Store the disable mask in the "next" 865 * pointer in a wild attempt to keep the ABI the same. 866 * Why do we do this on EVERY rule? 867 */ 868 bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable)); 869 if (dst->timestamp) 870 dst->timestamp += boot_seconds; 871 bp += l; 872 } 873 ipfw_get_dynamic(&bp, ep); /* protected by the dynamic lock */ 874 return (bp - (char *)buf); 875} 876 877 878/** 879 * {set|get}sockopt parser. 880 */ 881int 882ipfw_ctl(struct sockopt *sopt) 883{ 884#define RULE_MAXSIZE (256*sizeof(u_int32_t)) 885 int error; 886 size_t size; 887 struct ip_fw *buf, *rule; 888 struct ip_fw_chain *chain; 889 u_int32_t rulenum[2]; 890 891 error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); 892 if (error) 893 return (error); 894 895 /* 896 * Disallow modifications in really-really secure mode, but still allow 897 * the logging counters to be reset. 898 */ 899 if (sopt->sopt_name == IP_FW_ADD || 900 (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) { 901 error = securelevel_ge(sopt->sopt_td->td_ucred, 3); 902 if (error) 903 return (error); 904 } 905 906 chain = &V_layer3_chain; 907 error = 0; 908 909 switch (sopt->sopt_name) { 910 case IP_FW_GET: 911 /* 912 * pass up a copy of the current rules. Static rules 913 * come first (the last of which has number IPFW_DEFAULT_RULE), 914 * followed by a possibly empty list of dynamic rule. 915 * The last dynamic rule has NULL in the "next" field. 916 * 917 * Note that the calculated size is used to bound the 918 * amount of data returned to the user. The rule set may 919 * change between calculating the size and returning the 920 * data in which case we'll just return what fits. 921 */ 922 for (;;) { 923 int len = 0, want; 924 925 size = chain->static_len; 926 size += ipfw_dyn_len(); 927 if (size >= sopt->sopt_valsize) 928 break; 929 buf = malloc(size, M_TEMP, M_WAITOK); 930 if (buf == NULL) 931 break; 932 IPFW_UH_RLOCK(chain); 933 /* check again how much space we need */ 934 want = chain->static_len + ipfw_dyn_len(); 935 if (size >= want) 936 len = ipfw_getrules(chain, buf, size); 937 IPFW_UH_RUNLOCK(chain); 938 if (size >= want) 939 error = sooptcopyout(sopt, buf, len); 940 free(buf, M_TEMP); 941 if (size >= want) 942 break; 943 } 944 break; 945 946 case IP_FW_FLUSH: 947 /* locking is done within del_entry() */ 948 error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */ 949 break; 950 951 case IP_FW_ADD: 952 rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK); 953 error = sooptcopyin(sopt, rule, RULE_MAXSIZE, 954 sizeof(struct ip_fw) ); 955 956 /* 957 * If the size of commands equals RULESIZE7 then we assume 958 * a FreeBSD7.2 binary is talking to us (set is7=1). 959 * is7 is persistent so the next 'ipfw list' command 960 * will use this format. 961 * NOTE: If wrong version is guessed (this can happen if 962 * the first ipfw command is 'ipfw [pipe] list') 963 * the ipfw binary may crash or loop infinitly... 964 */ 965 if (sopt->sopt_valsize == RULESIZE7(rule)) { 966 is7 = 1; 967 error = convert_rule_to_8(rule); 968 if (error) 969 return error; 970 if (error == 0) 971 error = check_ipfw_struct(rule, RULESIZE(rule)); 972 } else { 973 is7 = 0; 974 if (error == 0) 975 error = check_ipfw_struct(rule, sopt->sopt_valsize); 976 } 977 if (error == 0) { 978 /* locking is done within ipfw_add_rule() */ 979 error = ipfw_add_rule(chain, rule); 980 size = RULESIZE(rule); 981 if (!error && sopt->sopt_dir == SOPT_GET) { 982 if (is7) { 983 error = convert_rule_to_7(rule); 984 size = RULESIZE7(rule); 985 if (error) 986 return error; 987 } 988 error = sooptcopyout(sopt, rule, size); 989 } 990 } 991 free(rule, M_TEMP); 992 break; 993 994 case IP_FW_DEL: 995 /* 996 * IP_FW_DEL is used for deleting single rules or sets, 997 * and (ab)used to atomically manipulate sets. Argument size 998 * is used to distinguish between the two: 999 * sizeof(u_int32_t) 1000 * delete single rule or set of rules, 1001 * or reassign rules (or sets) to a different set. 1002 * 2*sizeof(u_int32_t) 1003 * atomic disable/enable sets. 1004 * first u_int32_t contains sets to be disabled, 1005 * second u_int32_t contains sets to be enabled. 1006 */ 1007 error = sooptcopyin(sopt, rulenum, 1008 2*sizeof(u_int32_t), sizeof(u_int32_t)); 1009 if (error) 1010 break; 1011 size = sopt->sopt_valsize; 1012 if (size == sizeof(u_int32_t) && rulenum[0] != 0) { 1013 /* delete or reassign, locking done in del_entry() */ 1014 error = del_entry(chain, rulenum[0]); 1015 } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */ 1016 IPFW_UH_WLOCK(chain); 1017 V_set_disable = 1018 (V_set_disable | rulenum[0]) & ~rulenum[1] & 1019 ~(1<<RESVD_SET); /* set RESVD_SET always enabled */ 1020 IPFW_UH_WUNLOCK(chain); 1021 } else 1022 error = EINVAL; 1023 break; 1024 1025 case IP_FW_ZERO: 1026 case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */ 1027 rulenum[0] = 0; 1028 if (sopt->sopt_val != 0) { 1029 error = sooptcopyin(sopt, rulenum, 1030 sizeof(u_int32_t), sizeof(u_int32_t)); 1031 if (error) 1032 break; 1033 } 1034 error = zero_entry(chain, rulenum[0], 1035 sopt->sopt_name == IP_FW_RESETLOG); 1036 break; 1037 1038 /*--- TABLE manipulations are protected by the IPFW_LOCK ---*/ 1039 case IP_FW_TABLE_ADD: 1040 { 1041 ipfw_table_entry ent; 1042 1043 error = sooptcopyin(sopt, &ent, 1044 sizeof(ent), sizeof(ent)); 1045 if (error) 1046 break; 1047 error = ipfw_add_table_entry(chain, ent.tbl, 1048 ent.addr, ent.masklen, ent.value); 1049 } 1050 break; 1051 1052 case IP_FW_TABLE_DEL: 1053 { 1054 ipfw_table_entry ent; 1055 1056 error = sooptcopyin(sopt, &ent, 1057 sizeof(ent), sizeof(ent)); 1058 if (error) 1059 break; 1060 error = ipfw_del_table_entry(chain, ent.tbl, 1061 ent.addr, ent.masklen); 1062 } 1063 break; 1064 1065 case IP_FW_TABLE_FLUSH: 1066 { 1067 u_int16_t tbl; 1068 1069 error = sooptcopyin(sopt, &tbl, 1070 sizeof(tbl), sizeof(tbl)); 1071 if (error) 1072 break; 1073 IPFW_WLOCK(chain); 1074 error = ipfw_flush_table(chain, tbl); 1075 IPFW_WUNLOCK(chain); 1076 } 1077 break; 1078 1079 case IP_FW_TABLE_GETSIZE: 1080 { 1081 u_int32_t tbl, cnt; 1082 1083 if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl), 1084 sizeof(tbl)))) 1085 break; 1086 IPFW_RLOCK(chain); 1087 error = ipfw_count_table(chain, tbl, &cnt); 1088 IPFW_RUNLOCK(chain); 1089 if (error) 1090 break; 1091 error = sooptcopyout(sopt, &cnt, sizeof(cnt)); 1092 } 1093 break; 1094 1095 case IP_FW_TABLE_LIST: 1096 { 1097 ipfw_table *tbl; 1098 1099 if (sopt->sopt_valsize < sizeof(*tbl)) { 1100 error = EINVAL; 1101 break; 1102 } 1103 size = sopt->sopt_valsize; 1104 tbl = malloc(size, M_TEMP, M_WAITOK); 1105 error = sooptcopyin(sopt, tbl, size, sizeof(*tbl)); 1106 if (error) { 1107 free(tbl, M_TEMP); 1108 break; 1109 } 1110 tbl->size = (size - sizeof(*tbl)) / 1111 sizeof(ipfw_table_entry); 1112 IPFW_RLOCK(chain); 1113 error = ipfw_dump_table(chain, tbl); 1114 IPFW_RUNLOCK(chain); 1115 if (error) { 1116 free(tbl, M_TEMP); 1117 break; 1118 } 1119 error = sooptcopyout(sopt, tbl, size); 1120 free(tbl, M_TEMP); 1121 } 1122 break; 1123 1124 /*--- NAT operations are protected by the IPFW_LOCK ---*/ 1125 case IP_FW_NAT_CFG: 1126 if (IPFW_NAT_LOADED) 1127 error = ipfw_nat_cfg_ptr(sopt); 1128 else { 1129 printf("IP_FW_NAT_CFG: %s\n", 1130 "ipfw_nat not present, please load it"); 1131 error = EINVAL; 1132 } 1133 break; 1134 1135 case IP_FW_NAT_DEL: 1136 if (IPFW_NAT_LOADED) 1137 error = ipfw_nat_del_ptr(sopt); 1138 else { 1139 printf("IP_FW_NAT_DEL: %s\n", 1140 "ipfw_nat not present, please load it"); 1141 error = EINVAL; 1142 } 1143 break; 1144 1145 case IP_FW_NAT_GET_CONFIG: 1146 if (IPFW_NAT_LOADED) 1147 error = ipfw_nat_get_cfg_ptr(sopt); 1148 else { 1149 printf("IP_FW_NAT_GET_CFG: %s\n", 1150 "ipfw_nat not present, please load it"); 1151 error = EINVAL; 1152 } 1153 break; 1154 1155 case IP_FW_NAT_GET_LOG: 1156 if (IPFW_NAT_LOADED) 1157 error = ipfw_nat_get_log_ptr(sopt); 1158 else { 1159 printf("IP_FW_NAT_GET_LOG: %s\n", 1160 "ipfw_nat not present, please load it"); 1161 error = EINVAL; 1162 } 1163 break; 1164 1165 default: 1166 printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name); 1167 error = EINVAL; 1168 } 1169 1170 return (error); 1171#undef RULE_MAXSIZE 1172} 1173 1174 1175#define RULE_MAXSIZE (256*sizeof(u_int32_t)) 1176 1177/* Functions to convert rules 7.2 <==> 8.0 */ 1178int 1179convert_rule_to_7(struct ip_fw *rule) 1180{ 1181 /* Used to modify original rule */ 1182 struct ip_fw7 *rule7 = (struct ip_fw7 *)rule; 1183 /* copy of original rule, version 8 */ 1184 struct ip_fw *tmp; 1185 1186 /* Used to copy commands */ 1187 ipfw_insn *ccmd, *dst; 1188 int ll = 0, ccmdlen = 0; 1189 1190 tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); 1191 if (tmp == NULL) { 1192 return 1; //XXX error 1193 } 1194 bcopy(rule, tmp, RULE_MAXSIZE); 1195 1196 /* Copy fields */ 1197 rule7->_pad = tmp->_pad; 1198 rule7->set = tmp->set; 1199 rule7->rulenum = tmp->rulenum; 1200 rule7->cmd_len = tmp->cmd_len; 1201 rule7->act_ofs = tmp->act_ofs; 1202 rule7->next_rule = (struct ip_fw7 *)tmp->next_rule; 1203 rule7->next = (struct ip_fw7 *)tmp->x_next; 1204 rule7->cmd_len = tmp->cmd_len; 1205 rule7->pcnt = tmp->pcnt; 1206 rule7->bcnt = tmp->bcnt; 1207 rule7->timestamp = tmp->timestamp; 1208 1209 /* Copy commands */ 1210 for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ; 1211 ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { 1212 ccmdlen = F_LEN(ccmd); 1213 1214 bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); 1215 1216 if (dst->opcode > O_NAT) 1217 /* O_REASS doesn't exists in 7.2 version, so 1218 * decrement opcode if it is after O_REASS 1219 */ 1220 dst->opcode--; 1221 1222 if (ccmdlen > ll) { 1223 printf("ipfw: opcode %d size truncated\n", 1224 ccmd->opcode); 1225 return EINVAL; 1226 } 1227 } 1228 free(tmp, M_TEMP); 1229 1230 return 0; 1231} 1232 1233int 1234convert_rule_to_8(struct ip_fw *rule) 1235{ 1236 /* Used to modify original rule */ 1237 struct ip_fw7 *rule7 = (struct ip_fw7 *) rule; 1238 1239 /* Used to copy commands */ 1240 ipfw_insn *ccmd, *dst; 1241 int ll = 0, ccmdlen = 0; 1242 1243 /* Copy of original rule */ 1244 struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); 1245 if (tmp == NULL) { 1246 return 1; //XXX error 1247 } 1248 1249 bcopy(rule7, tmp, RULE_MAXSIZE); 1250 1251 for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ; 1252 ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { 1253 ccmdlen = F_LEN(ccmd); 1254 1255 bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); 1256 1257 if (dst->opcode > O_NAT) 1258 /* O_REASS doesn't exists in 7.2 version, so 1259 * increment opcode if it is after O_REASS 1260 */ 1261 dst->opcode++; 1262 1263 if (ccmdlen > ll) { 1264 printf("ipfw: opcode %d size truncated\n", 1265 ccmd->opcode); 1266 return EINVAL; 1267 } 1268 } 1269 1270 rule->_pad = tmp->_pad; 1271 rule->set = tmp->set; 1272 rule->rulenum = tmp->rulenum; 1273 rule->cmd_len = tmp->cmd_len; 1274 rule->act_ofs = tmp->act_ofs; 1275 rule->next_rule = (struct ip_fw *)tmp->next_rule; 1276 rule->x_next = (struct ip_fw *)tmp->next; 1277 rule->cmd_len = tmp->cmd_len; 1278 rule->id = 0; /* XXX see if is ok = 0 */ 1279 rule->pcnt = tmp->pcnt; 1280 rule->bcnt = tmp->bcnt; 1281 rule->timestamp = tmp->timestamp; 1282 1283 free (tmp, M_TEMP); 1284 return 0; 1285} 1286 1287/* end of file */ 1288