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