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