1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2025 Yandex LLC 5 * Copyright (c) 2025 Andrey V. Elsukov <ae@FreeBSD.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 /* 31 * Example of compatibility layer for ipfw's rule management routines. 32 */ 33 34 #include "opt_inet.h" 35 #include "opt_inet6.h" 36 #include "opt_ipfw.h" 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/malloc.h> 41 #include <sys/mbuf.h> 42 #include <sys/module.h> 43 #include <sys/kernel.h> 44 #include <sys/lock.h> 45 #include <sys/priv.h> 46 #include <sys/proc.h> 47 #include <sys/rwlock.h> 48 #include <sys/rmlock.h> 49 #include <sys/socket.h> 50 #include <sys/socketvar.h> 51 #include <sys/sysctl.h> 52 #include <sys/syslog.h> 53 #include <sys/fnv_hash.h> 54 #include <net/if.h> 55 #include <net/pfil.h> 56 #include <net/route.h> 57 #include <net/vnet.h> 58 #include <vm/vm.h> 59 #include <vm/vm_extern.h> 60 61 #include <netinet/in.h> 62 #include <netinet/ip_var.h> /* hooks */ 63 #include <netinet/ip_fw.h> 64 65 #include <netpfil/ipfw/ip_fw_private.h> 66 #include <netpfil/ipfw/ip_fw_table.h> 67 68 #ifdef MAC 69 #include <security/mac/mac_framework.h> 70 #endif 71 72 /* 73 * These structures were used by IP_FW3 socket option with version 0. 74 */ 75 typedef struct _ipfw_dyn_rule_v0 { 76 ipfw_dyn_rule *next; /* linked list of rules. */ 77 struct ip_fw *rule; /* pointer to rule */ 78 /* 'rule' is used to pass up the rule number (from the parent) */ 79 80 ipfw_dyn_rule *parent; /* pointer to parent rule */ 81 u_int64_t pcnt; /* packet match counter */ 82 u_int64_t bcnt; /* byte match counter */ 83 struct ipfw_flow_id id; /* (masked) flow id */ 84 u_int32_t expire; /* expire time */ 85 u_int32_t bucket; /* which bucket in hash table */ 86 u_int32_t state; /* state of this rule (typically a 87 * combination of TCP flags) 88 */ 89 u_int32_t ack_fwd; /* most recent ACKs in forward */ 90 u_int32_t ack_rev; /* and reverse directions (used */ 91 /* to generate keepalives) */ 92 u_int16_t dyn_type; /* rule type */ 93 u_int16_t count; /* refcount */ 94 u_int16_t kidx; /* index of named object */ 95 } __packed __aligned(8) ipfw_dyn_rule_v0; 96 97 typedef struct _ipfw_obj_dyntlv_v0 { 98 ipfw_obj_tlv head; 99 ipfw_dyn_rule_v0 state; 100 } ipfw_obj_dyntlv_v0; 101 102 typedef struct _ipfw_obj_ntlv_v0 { 103 ipfw_obj_tlv head; /* TLV header */ 104 uint16_t idx; /* Name index */ 105 uint8_t set; /* set, if applicable */ 106 uint8_t type; /* object type, if applicable */ 107 uint32_t spare; /* unused */ 108 char name[64]; /* Null-terminated name */ 109 } ipfw_obj_ntlv_v0; 110 111 typedef struct _ipfw_range_tlv_v0 { 112 ipfw_obj_tlv head; /* TLV header */ 113 uint32_t flags; /* Range flags */ 114 uint16_t start_rule; /* Range start */ 115 uint16_t end_rule; /* Range end */ 116 uint32_t set; /* Range set to match */ 117 uint32_t new_set; /* New set to move/swap to */ 118 } ipfw_range_tlv_v0; 119 120 typedef struct _ipfw_range_header_v0 { 121 ip_fw3_opheader opheader; /* IP_FW3 opcode */ 122 ipfw_range_tlv_v0 range; 123 } ipfw_range_header_v0; 124 125 typedef struct _ipfw_insn_limit_v0 { 126 ipfw_insn o; 127 uint8_t _pad; 128 uint8_t limit_mask; 129 uint16_t conn_limit; 130 } ipfw_insn_limit_v0; 131 132 typedef struct _ipfw_obj_tentry_v0 { 133 ipfw_obj_tlv head; /* TLV header */ 134 uint8_t subtype; /* subtype (IPv4,IPv6) */ 135 uint8_t masklen; /* mask length */ 136 uint8_t result; /* request result */ 137 uint8_t spare0; 138 uint16_t idx; /* Table name index */ 139 uint16_t spare1; 140 union { 141 /* Longest field needs to be aligned by 8-byte boundary */ 142 struct in_addr addr; /* IPv4 address */ 143 uint32_t key; /* uid/gid/port */ 144 struct in6_addr addr6; /* IPv6 address */ 145 char iface[IF_NAMESIZE]; /* interface name */ 146 struct tflow_entry flow; 147 } k; 148 union { 149 ipfw_table_value value; /* value data */ 150 uint32_t kidx; /* value kernel index */ 151 } v; 152 } ipfw_obj_tentry_v0; 153 154 static sopt_handler_f dump_config_v0, add_rules_v0, del_rules_v0, 155 clear_rules_v0, move_rules_v0, manage_sets_v0, dump_soptcodes_v0, 156 dump_srvobjects_v0; 157 158 static struct ipfw_sopt_handler scodes[] = { 159 { IP_FW_XGET, IP_FW3_OPVER_0, HDIR_GET, dump_config_v0 }, 160 { IP_FW_XADD, IP_FW3_OPVER_0, HDIR_BOTH, add_rules_v0 }, 161 { IP_FW_XDEL, IP_FW3_OPVER_0, HDIR_BOTH, del_rules_v0 }, 162 { IP_FW_XZERO, IP_FW3_OPVER_0, HDIR_SET, clear_rules_v0 }, 163 { IP_FW_XRESETLOG, IP_FW3_OPVER_0, HDIR_SET, clear_rules_v0 }, 164 { IP_FW_XMOVE, IP_FW3_OPVER_0, HDIR_SET, move_rules_v0 }, 165 { IP_FW_SET_SWAP, IP_FW3_OPVER_0, HDIR_SET, manage_sets_v0 }, 166 { IP_FW_SET_MOVE, IP_FW3_OPVER_0, HDIR_SET, manage_sets_v0 }, 167 { IP_FW_SET_ENABLE, IP_FW3_OPVER_0, HDIR_SET, manage_sets_v0 }, 168 { IP_FW_DUMP_SOPTCODES, IP_FW3_OPVER_0, HDIR_GET, dump_soptcodes_v0 }, 169 { IP_FW_DUMP_SRVOBJECTS, IP_FW3_OPVER_0, HDIR_GET, dump_srvobjects_v0 }, 170 }; 171 172 static int 173 dump_config_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 174 struct sockopt_data *sd) 175 { 176 return (EOPNOTSUPP); 177 } 178 179 /* 180 * Calculate the size adjust needed to store opcodes converted from v0 181 * to v1. 182 */ 183 static int 184 adjust_size_v0(ipfw_insn *cmd) 185 { 186 int cmdlen, adjust; 187 188 cmdlen = F_LEN(cmd); 189 switch (cmd->opcode) { 190 case O_CHECK_STATE: 191 case O_KEEP_STATE: 192 case O_PROBE_STATE: 193 case O_EXTERNAL_ACTION: 194 case O_EXTERNAL_INSTANCE: 195 adjust = F_INSN_SIZE(ipfw_insn_kidx) - cmdlen; 196 break; 197 case O_LIMIT: 198 adjust = F_INSN_SIZE(ipfw_insn_limit) - cmdlen; 199 break; 200 case O_IP_SRC_LOOKUP: 201 case O_IP_DST_LOOKUP: 202 case O_IP_FLOW_LOOKUP: 203 case O_MAC_SRC_LOOKUP: 204 case O_MAC_DST_LOOKUP: 205 if (cmdlen == F_INSN_SIZE(ipfw_insn)) 206 adjust = F_INSN_SIZE(ipfw_insn_kidx) - cmdlen; 207 else 208 adjust = F_INSN_SIZE(ipfw_insn_table) - cmdlen; 209 break; 210 case O_SKIPTO: 211 case O_CALLRETURN: 212 adjust = F_INSN_SIZE(ipfw_insn_u32) - cmdlen; 213 break; 214 default: 215 adjust = 0; 216 } 217 return (adjust); 218 } 219 220 static int 221 parse_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 222 struct sockopt_data *sd, ipfw_obj_ctlv **prtlv, 223 struct rule_check_info **pci) 224 { 225 ipfw_obj_ctlv *ctlv, *rtlv, *tstate; 226 ipfw_obj_ntlv_v0 *ntlv; 227 struct rule_check_info *ci, *cbuf; 228 struct ip_fw_rule *r; 229 size_t count, clen, read, rsize; 230 uint32_t rulenum; 231 int idx, error; 232 233 op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize); 234 ctlv = (ipfw_obj_ctlv *)(op3 + 1); 235 read = sizeof(ip_fw3_opheader); 236 if (read + sizeof(*ctlv) > sd->valsize) 237 return (EINVAL); 238 239 rtlv = NULL; 240 tstate = NULL; 241 cbuf = NULL; 242 /* Table names or other named objects. */ 243 if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { 244 /* Check size and alignment. */ 245 clen = ctlv->head.length; 246 if (read + clen > sd->valsize || clen < sizeof(*ctlv) || 247 (clen % sizeof(uint64_t)) != 0) 248 return (EINVAL); 249 /* Check for validness. */ 250 count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv); 251 if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv)) 252 return (EINVAL); 253 /* 254 * Check each TLV. 255 * Ensure TLVs are sorted ascending and 256 * there are no duplicates. 257 */ 258 idx = -1; 259 ntlv = (ipfw_obj_ntlv_v0 *)(ctlv + 1); 260 while (count > 0) { 261 if (ntlv->head.length != sizeof(ipfw_obj_ntlv_v0)) 262 return (EINVAL); 263 264 error = ipfw_check_object_name_generic(ntlv->name); 265 if (error != 0) 266 return (error); 267 268 if (ntlv->idx <= idx) 269 return (EINVAL); 270 271 idx = ntlv->idx; 272 count--; 273 ntlv++; 274 } 275 276 tstate = ctlv; 277 read += ctlv->head.length; 278 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); 279 280 if (read + sizeof(*ctlv) > sd->valsize) 281 return (EINVAL); 282 } 283 284 /* List of rules. */ 285 if (ctlv->head.type == IPFW_TLV_RULE_LIST) { 286 clen = ctlv->head.length; 287 if (read + clen > sd->valsize || clen < sizeof(*ctlv) || 288 (clen % sizeof(uint64_t)) != 0) 289 return (EINVAL); 290 291 clen -= sizeof(*ctlv); 292 if (ctlv->count == 0 || 293 ctlv->count > clen / sizeof(struct ip_fw_rule)) 294 return (EINVAL); 295 296 /* Allocate state for each rule */ 297 cbuf = malloc(ctlv->count * sizeof(struct rule_check_info), 298 M_TEMP, M_WAITOK | M_ZERO); 299 300 /* 301 * Check each rule for validness. 302 * Ensure numbered rules are sorted ascending 303 * and properly aligned 304 */ 305 rulenum = 0; 306 count = 0; 307 error = 0; 308 ci = cbuf; 309 r = (struct ip_fw_rule *)(ctlv + 1); 310 while (clen > 0) { 311 rsize = RULEUSIZE1(r); 312 if (rsize > clen || count > ctlv->count) { 313 error = EINVAL; 314 break; 315 } 316 ci->ctlv = tstate; 317 ci->version = IP_FW3_OPVER_0; 318 error = ipfw_check_rule(r, rsize, ci); 319 if (error != 0) 320 break; 321 322 /* Check sorting */ 323 if (r->rulenum != 0 && r->rulenum < rulenum) { 324 printf("ipfw: wrong order: rulenum %u" 325 " vs %u\n", r->rulenum, rulenum); 326 error = EINVAL; 327 break; 328 } 329 rulenum = r->rulenum; 330 ci->urule = (caddr_t)r; 331 clen -= rsize; 332 r = (struct ip_fw_rule *)((caddr_t)r + rsize); 333 count++; 334 ci++; 335 } 336 337 if (ctlv->count != count || error != 0) { 338 free(cbuf, M_TEMP); 339 return (EINVAL); 340 } 341 342 rtlv = ctlv; 343 read += ctlv->head.length; 344 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); 345 } 346 347 if (read != sd->valsize || rtlv == NULL) { 348 free(cbuf, M_TEMP); 349 return (EINVAL); 350 } 351 352 *prtlv = rtlv; 353 *pci = cbuf; 354 return (0); 355 } 356 357 static void 358 convert_v0_to_v1(struct rule_check_info *ci, int rule_len) 359 { 360 struct ip_fw_rule *urule; 361 struct ip_fw *krule; 362 ipfw_insn *src, *dst; 363 int l, cmdlen, newlen; 364 365 urule = (struct ip_fw_rule *)ci->urule; 366 krule = ci->krule; 367 for (l = urule->cmd_len, src = urule->cmd, dst = krule->cmd; 368 l > 0 && rule_len > 0; 369 l -= cmdlen, src += cmdlen, 370 rule_len -= newlen, dst += newlen) { 371 cmdlen = F_LEN(src); 372 switch (src->opcode) { 373 case O_CHECK_STATE: 374 case O_KEEP_STATE: 375 case O_PROBE_STATE: 376 case O_EXTERNAL_ACTION: 377 case O_EXTERNAL_INSTANCE: 378 newlen = F_INSN_SIZE(ipfw_insn_kidx); 379 insntod(dst, kidx)->kidx = src->arg1; 380 break; 381 case O_LIMIT: 382 newlen = F_INSN_SIZE(ipfw_insn_limit); 383 insntod(dst, limit)->kidx = src->arg1; 384 insntod(dst, limit)->limit_mask = 385 insntoc(src, limit)->limit_mask; 386 insntod(dst, limit)->conn_limit = 387 insntoc(src, limit)->conn_limit; 388 break; 389 case O_IP_DST_LOOKUP: 390 if (cmdlen == F_INSN_SIZE(ipfw_insn) + 2) { 391 /* lookup type stored in d[1] */ 392 dst->arg1 = insntoc(src, table)->value; 393 } 394 case O_IP_SRC_LOOKUP: 395 case O_IP_FLOW_LOOKUP: 396 case O_MAC_SRC_LOOKUP: 397 case O_MAC_DST_LOOKUP: 398 if (cmdlen == F_INSN_SIZE(ipfw_insn)) { 399 newlen = F_INSN_SIZE(ipfw_insn_kidx); 400 insntod(dst, kidx)->kidx = src->arg1; 401 } else { 402 newlen = F_INSN_SIZE(ipfw_insn_table); 403 insntod(dst, table)->kidx = src->arg1; 404 insntod(dst, table)->value = 405 insntoc(src, u32)->d[0]; 406 } 407 break; 408 case O_CALLRETURN: 409 case O_SKIPTO: 410 newlen = F_INSN_SIZE(ipfw_insn_u32); 411 insntod(dst, u32)->d[0] = src->arg1; 412 break; 413 default: 414 newlen = cmdlen; 415 memcpy(dst, src, sizeof(uint32_t) * newlen); 416 continue; 417 } 418 dst->opcode = src->opcode; 419 dst->len = (src->len & (F_NOT | F_OR)) | newlen; 420 } 421 } 422 423 /* 424 * Copy rule @urule from v0 userland format to kernel @krule. 425 */ 426 static void 427 import_rule_v0(struct ip_fw_chain *chain, struct rule_check_info *ci) 428 { 429 struct ip_fw_rule *urule; 430 struct ip_fw *krule; 431 ipfw_insn *cmd; 432 int l, cmdlen, adjust, aadjust; 433 434 urule = (struct ip_fw_rule *)ci->urule; 435 l = urule->cmd_len; 436 cmd = urule->cmd; 437 adjust = aadjust = 0; 438 439 /* Scan all opcodes and determine the needed size */ 440 while (l > 0) { 441 adjust += adjust_size_v0(cmd); 442 if (ACTION_PTR(urule) < cmd) 443 aadjust = adjust; 444 cmdlen = F_LEN(cmd); 445 l -= cmdlen; 446 cmd += cmdlen; 447 } 448 449 cmdlen = urule->cmd_len + adjust; 450 krule = ci->krule = ipfw_alloc_rule(chain, /* RULEKSIZE1(cmdlen) */ 451 roundup2(sizeof(struct ip_fw) + cmdlen * 4 - 4, 8)); 452 453 krule->act_ofs = urule->act_ofs + aadjust; 454 krule->cmd_len = urule->cmd_len + adjust; 455 456 if (adjust != 0) 457 printf("%s: converted rule %u: cmd_len %u -> %u, " 458 "act_ofs %u -> %u\n", __func__, urule->rulenum, 459 urule->cmd_len, krule->cmd_len, urule->act_ofs, 460 krule->act_ofs); 461 462 krule->rulenum = urule->rulenum; 463 krule->set = urule->set; 464 krule->flags = urule->flags; 465 466 /* Save rulenum offset */ 467 ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum); 468 convert_v0_to_v1(ci, cmdlen); 469 } 470 471 static int 472 add_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 473 struct sockopt_data *sd) 474 { 475 ipfw_obj_ctlv *rtlv; 476 struct rule_check_info *ci, *nci; 477 int i, ret; 478 479 /* 480 * Check rules buffer for validness. 481 */ 482 ret = parse_rules_v0(chain, op3, sd, &rtlv, &nci); 483 if (ret != 0) 484 return (ret); 485 /* 486 * Allocate storage for the kernel representation of rules. 487 */ 488 for (i = 0, ci = nci; i < rtlv->count; i++, ci++) 489 import_rule_v0(chain, ci); 490 /* 491 * Try to add new rules to the chain. 492 */ 493 if ((ret = ipfw_commit_rules(chain, nci, rtlv->count)) != 0) { 494 for (i = 0, ci = nci; i < rtlv->count; i++, ci++) 495 ipfw_free_rule(ci->krule); 496 } 497 /* Cleanup after ipfw_parse_rules() */ 498 free(nci, M_TEMP); 499 return (ret); 500 } 501 502 static int 503 check_range_tlv_v0(const ipfw_range_tlv_v0 *rt, ipfw_range_tlv *crt) 504 { 505 if (rt->head.length != sizeof(*rt)) 506 return (1); 507 if (rt->start_rule > rt->end_rule) 508 return (1); 509 if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS) 510 return (1); 511 if ((rt->flags & IPFW_RCFLAG_USER) != rt->flags) 512 return (1); 513 514 crt->head = rt->head; 515 crt->head.length = sizeof(*crt); 516 crt->flags = rt->flags; 517 crt->start_rule = rt->start_rule; 518 crt->end_rule = rt->end_rule; 519 crt->set = rt->set; 520 crt->new_set = rt->new_set; 521 return (0); 522 } 523 524 static int 525 del_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 526 struct sockopt_data *sd) 527 { 528 ipfw_range_tlv rv; 529 ipfw_range_header_v0 *rh; 530 int error, ndel; 531 532 if (sd->valsize != sizeof(*rh)) 533 return (EINVAL); 534 535 rh = (ipfw_range_header_v0 *)ipfw_get_sopt_space(sd, sd->valsize); 536 if (check_range_tlv_v0(&rh->range, &rv) != 0) 537 return (EINVAL); 538 539 ndel = 0; 540 if ((error = delete_range(chain, &rv, &ndel)) != 0) 541 return (error); 542 543 /* Save number of rules deleted */ 544 rh->range.new_set = ndel; 545 return (0); 546 } 547 548 static int 549 clear_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 550 struct sockopt_data *sd) 551 { 552 return (EOPNOTSUPP); 553 } 554 555 static int 556 move_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 557 struct sockopt_data *sd) 558 { 559 return (EOPNOTSUPP); 560 } 561 562 static int 563 manage_sets_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 564 struct sockopt_data *sd) 565 { 566 return (EOPNOTSUPP); 567 } 568 569 static int 570 dump_soptcodes_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 571 struct sockopt_data *sd) 572 { 573 return (EOPNOTSUPP); 574 } 575 576 static int 577 dump_srvobjects_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 578 struct sockopt_data *sd) 579 { 580 return (EOPNOTSUPP); 581 } 582 583 static enum ipfw_opcheck_result 584 check_opcode_compat(ipfw_insn **pcmd, int *plen, struct rule_check_info *ci) 585 { 586 ipfw_insn *cmd; 587 size_t cmdlen; 588 589 if (ci->version != IP_FW3_OPVER_0) 590 return (FAILED); 591 592 cmd = *pcmd; 593 cmdlen = F_LEN(cmd); 594 switch (cmd->opcode) { 595 case O_PROBE_STATE: 596 case O_KEEP_STATE: 597 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 598 return (BAD_SIZE); 599 ci->object_opcodes++; 600 break; 601 case O_LIMIT: 602 if (cmdlen != F_INSN_SIZE(ipfw_insn_limit_v0)) 603 return (BAD_SIZE); 604 ci->object_opcodes++; 605 break; 606 case O_IP_SRC_LOOKUP: 607 if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) 608 return (BAD_SIZE); 609 /* FALLTHROUGH */ 610 case O_IP_DST_LOOKUP: 611 if (cmdlen != F_INSN_SIZE(ipfw_insn) && 612 cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 && 613 cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 614 return (BAD_SIZE); 615 if (cmd->arg1 >= V_fw_tables_max) { 616 printf("ipfw: invalid table number %u\n", 617 cmd->arg1); 618 return (FAILED); 619 } 620 ci->object_opcodes++; 621 break; 622 case O_IP_FLOW_LOOKUP: 623 if (cmdlen != F_INSN_SIZE(ipfw_insn) && 624 cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 625 return (BAD_SIZE); 626 if (cmd->arg1 >= V_fw_tables_max) { 627 printf("ipfw: invalid table number %u\n", 628 cmd->arg1); 629 return (FAILED); 630 } 631 ci->object_opcodes++; 632 break; 633 case O_CHECK_STATE: 634 ci->object_opcodes++; 635 /* FALLTHROUGH */ 636 case O_SKIPTO: 637 case O_CALLRETURN: 638 if (cmdlen != F_INSN_SIZE(ipfw_insn)) 639 return (BAD_SIZE); 640 return (CHECK_ACTION); 641 642 case O_EXTERNAL_ACTION: 643 if (cmd->arg1 == 0 || 644 cmdlen != F_INSN_SIZE(ipfw_insn)) { 645 printf("ipfw: invalid external " 646 "action opcode\n"); 647 return (FAILED); 648 } 649 ci->object_opcodes++; 650 /* 651 * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA 652 * opcode? 653 */ 654 if (*plen != cmdlen) { 655 *plen -= cmdlen; 656 *pcmd = cmd += cmdlen; 657 cmdlen = F_LEN(cmd); 658 if (cmd->opcode == O_EXTERNAL_DATA) 659 return (CHECK_ACTION); 660 if (cmd->opcode != O_EXTERNAL_INSTANCE) { 661 printf("ipfw: invalid opcode " 662 "next to external action %u\n", 663 cmd->opcode); 664 return (FAILED); 665 } 666 if (cmd->arg1 == 0 || 667 cmdlen != F_INSN_SIZE(ipfw_insn)) { 668 printf("ipfw: invalid external " 669 "action instance opcode\n"); 670 return (FAILED); 671 } 672 ci->object_opcodes++; 673 } 674 return (CHECK_ACTION); 675 676 default: 677 return (ipfw_check_opcode(pcmd, plen, ci)); 678 } 679 return (SUCCESS); 680 } 681 682 static int 683 ipfw_compat_modevent(module_t mod, int type, void *unused) 684 { 685 switch (type) { 686 case MOD_LOAD: 687 IPFW_ADD_SOPT_HANDLER(1, scodes); 688 ipfw_register_compat(check_opcode_compat); 689 break; 690 case MOD_UNLOAD: 691 ipfw_unregister_compat(); 692 IPFW_DEL_SOPT_HANDLER(1, scodes); 693 break; 694 default: 695 return (EOPNOTSUPP); 696 } 697 return (0); 698 } 699 700 static moduledata_t ipfw_compat_mod = { 701 "ipfw_compat", 702 ipfw_compat_modevent, 703 0 704 }; 705 706 /* Define startup order. */ 707 #define IPFW_COMPAT_SI_SUB_FIREWALL SI_SUB_PROTO_FIREWALL 708 #define IPFW_COMPAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */ 709 #define IPFW_COMPAT_MODULE_ORDER (IPFW_COMPAT_MODEVENT_ORDER + 1) 710 711 DECLARE_MODULE(ipfw_compat, ipfw_compat_mod, IPFW_COMPAT_SI_SUB_FIREWALL, 712 IPFW_COMPAT_MODULE_ORDER); 713 MODULE_DEPEND(ipfw_compat, ipfw, 3, 3, 3); 714 MODULE_VERSION(ipfw_compat, 1); 715