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