diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -40,10 +40,10 @@ .Ss SYSCTL SHORTCUTS .Nm .Cm enable -.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive +.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive | skipto_cache .Nm .Cm disable -.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive +.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive | skipto_cache .Ss LOOKUP TABLES .Nm .Oo Cm set Ar N Oc Cm table Ar name Cm create Ar create-options @@ -1395,6 +1395,16 @@ If an optional 32-bit unsigned .Ar value is also specified, an entry will match only if it has this value. +If +.Ar value +is specified in form +.Ar valtype=value , +then specified value type field will be checked. +It can be +.Ar skipto, pipe, fib, nat, dscp, tag, divert, netgraph, limit, nh4 +and +.Ar mark. + See the .Sx LOOKUP TABLES section below for more information on lookup tables. @@ -1865,7 +1875,7 @@ of source and destination addresses and ports can be specified. .It Cm lookup Bro Cm dst-ip | dst-port | dst-mac | src-ip | src-port | src-mac | uid | -.Cm jail | dscp | mark Brc Ar name +.Cm jail | dscp | mark | rulenum Brc Ar name Search an entry in lookup table .Ar name that matches the field specified as argument. diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -305,6 +305,8 @@ TOK_LOGOFF, TOK_PRIVATE, TOK_PRIVATEOFF, + TOK_SWAPCONF, + TOK_SWAPCONFOFF, /* NAT64 CLAT tokens */ TOK_NAT64CLAT, @@ -462,5 +464,5 @@ void ipfw_list_ta(int ac, char *av[]); void ipfw_list_values(int ac, char *av[]); void table_fill_ntlv(struct _ipfw_obj_ntlv *ntlv, const char *name, - uint8_t set, uint16_t uidx); + uint8_t set, uint32_t uidx); diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -290,6 +290,12 @@ { NULL, 0 } /* terminator */ }; +static struct _s_x return_types[] = { + { "next-rulenum", RETURN_NEXT_RULENUM }, + { "next-rule", RETURN_NEXT_RULE }, + { NULL, 0 } /* terminator */ +}; + static struct _s_x rule_action_params[] = { { "altq", TOK_ALTQ }, { "log", TOK_LOG }, @@ -300,7 +306,7 @@ /* * The 'lookup' instruction accepts one of the following arguments. - * Arguments are passed as v[1] in O_DST_LOOKUP options. + * Arguments are passed as o.arg1 and o->value in O_DST_LOOKUP option. */ static struct _s_x lookup_keys[] = { { "dst-ip", LOOKUP_DST_IP }, @@ -313,9 +319,29 @@ { "jail", LOOKUP_JAIL }, { "dscp", LOOKUP_DSCP }, { "mark", LOOKUP_MARK }, + { "rulenum", LOOKUP_RULENUM }, { NULL, 0 }, }; +/* + * table(name,valuename=value) instruction accepts one of the + * following arguments as valuename. + */ +static struct _s_x tvalue_names[] = { + { "tag", TVALUE_TAG }, + { "pipe", TVALUE_PIPE }, + { "divert", TVALUE_DIVERT }, + { "skipto", TVALUE_SKIPTO }, + { "netgraph", TVALUE_NETGRAPH }, + { "fib", TVALUE_FIB }, + { "nat", TVALUE_NAT }, + { "nh4", TVALUE_NH4 }, + { "dscp", TVALUE_DSCP }, + { "limit", TVALUE_LIMIT }, + { "mark", TVALUE_MARK }, + { NULL, 0 } +}; + static struct _s_x rule_options[] = { { "tagged", TOK_TAGGED }, { "uid", TOK_UID }, @@ -415,12 +441,12 @@ static void ipfw_list_tifaces(void); struct tidx; -static uint16_t pack_object(struct tidx *tstate, const char *name, int otype); -static uint16_t pack_table(struct tidx *tstate, const char *name); +static uint32_t pack_object(struct tidx *tstate, const char *name, int otype); +static uint32_t pack_table(struct tidx *tstate, const char *name); -static char *table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx); +static char *table_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx); static void object_sort_ctlv(ipfw_obj_ctlv *ctlv); -static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, +static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx, uint16_t type); int @@ -650,6 +676,7 @@ { op3->opcode = optname; + op3->version = IP_FW3_OPVER; /* use last version */ if (g_co.debug_only) { struct debug_header dbg = { @@ -690,6 +717,7 @@ socklen_t len; op3->opcode = optname; + op3->version = IP_FW3_OPVER; /* use last version */ if (g_co.debug_only) { struct debug_header dbg = { @@ -1284,6 +1312,15 @@ } } +static void +print_tvalue(struct buf_pr *bp, const ipfw_insn_table *cmd) +{ + const char *name; + + name = match_value(tvalue_names, IPFW_TVALUE_TYPE(&cmd->o)); + bprintf(bp, ",%s=%u", name != NULL ? name: "", cmd->value); +} + /* * Print the ip address contained in a command. @@ -1295,33 +1332,46 @@ struct hostent *he = NULL; const struct in_addr *ia; const uint32_t *a = ((const ipfw_insn_u32 *)cmd)->d; - uint32_t len = F_LEN((const ipfw_insn *)cmd); + uint32_t len = F_LEN(&cmd->o); char *t; bprintf(bp, " "); - if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) { - const char *arg; - - arg = match_value(lookup_keys, a[1]); - t = table_search_ctlv(fo->tstate, - ((const ipfw_insn *)cmd)->arg1); - bprintf(bp, "lookup %s %s", arg, t); - return; - } - if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { + switch (cmd->o.opcode) { + case O_IP_SRC_ME: + case O_IP_DST_ME: bprintf(bp, "me"); return; - } - if (cmd->o.opcode == O_IP_SRC_LOOKUP || - cmd->o.opcode == O_IP_DST_LOOKUP) { + + case O_IP_DST_LOOKUP: + if ((len == F_INSN_SIZE(ipfw_insn_kidx) || + len == F_INSN_SIZE(ipfw_insn_table)) && + IPFW_LOOKUP_TYPE(&cmd->o) != LOOKUP_NONE) { + const char *key; + + key = match_value(lookup_keys, + IPFW_LOOKUP_TYPE(&cmd->o)); + t = table_search_ctlv(fo->tstate, + insntoc(&cmd->o, kidx)->kidx); + if (len == F_INSN_SIZE(ipfw_insn_table)) { + bprintf(bp, "lookup %s:%#x %s", + (key != NULL ? key : ""), + insntoc(&cmd->o, table)->value, t); + } else + bprintf(bp, "lookup %s %s", key != NULL ? key: + "", t); + return; + } + /* FALLTHROUGH */ + case O_IP_SRC_LOOKUP: t = table_search_ctlv(fo->tstate, - ((const ipfw_insn *)cmd)->arg1); + insntoc(&cmd->o, kidx)->kidx); bprintf(bp, "table(%s", t); - if (len == F_INSN_SIZE(ipfw_insn_u32)) - bprintf(bp, ",%u", *a); + if (len == F_INSN_SIZE(ipfw_insn_table)) + print_tvalue(bp, insntoc(&cmd->o, table)); bprintf(bp, ")"); return; } + if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { const uint32_t *map = (const uint32_t *)&cmd->mask; struct in_addr addr; @@ -1427,10 +1477,10 @@ bprintf(bp, " "); - t = table_search_ctlv(fo->tstate, cmd->arg1); + t = table_search_ctlv(fo->tstate, insntoc(cmd, kidx)->kidx); bprintf(bp, "table(%s", t); - if (len == F_INSN_SIZE(ipfw_insn_u32)) - bprintf(bp, ",%u", ((const ipfw_insn_u32 *)cmd)->d[0]); + if (len == F_INSN_SIZE(ipfw_insn_table)) + print_tvalue(bp, insntoc(cmd, table)); bprintf(bp, ")"); } @@ -1497,10 +1547,9 @@ } } -#define insntod(cmd, type) ((const ipfw_insn_ ## type *)(cmd)) struct show_state { struct ip_fw_rule *rule; - const ipfw_insn *eaction; + const ipfw_insn_kidx *eaction; uint8_t *printed; int flags; #define HAVE_PROTO 0x0001 @@ -1583,7 +1632,7 @@ switch (cmd->opcode) { case O_PROB: - d = 1.0 * insntod(cmd, u32)->d[0] / 0x7fffffff; + d = 1.0 * insntoc(cmd, u32)->d[0] / 0x7fffffff; bprintf(bp, "prob %f ", d); break; case O_PROBE_STATE: /* no need to print anything here */ @@ -1596,30 +1645,36 @@ case O_IP_SRC_SET: if (state->flags & HAVE_SRCIP) bprintf(bp, " src-ip"); - print_ip(bp, fo, insntod(cmd, ip)); + print_ip(bp, fo, insntoc(cmd, ip)); break; case O_IP_DST: - case O_IP_DST_LOOKUP: case O_IP_DST_MASK: case O_IP_DST_ME: case O_IP_DST_SET: - if (state->flags & HAVE_DSTIP) + case O_IP_DST_LOOKUP: + /* + * Special handling for O_IP_DST_LOOKUP when + * lookup type is not LOOKUP_NONE. + */ + if ((state->flags & HAVE_DSTIP) != 0 && ( + cmd->opcode != O_IP_DST_LOOKUP || + IPFW_LOOKUP_TYPE(cmd) == LOOKUP_NONE)) bprintf(bp, " dst-ip"); - print_ip(bp, fo, insntod(cmd, ip)); + print_ip(bp, fo, insntoc(cmd, ip)); break; case O_IP6_SRC: case O_IP6_SRC_MASK: case O_IP6_SRC_ME: if (state->flags & HAVE_SRCIP) bprintf(bp, " src-ip6"); - print_ip6(bp, insntod(cmd, ip6)); + print_ip6(bp, insntoc(cmd, ip6)); break; case O_IP6_DST: case O_IP6_DST_MASK: case O_IP6_DST_ME: if (state->flags & HAVE_DSTIP) bprintf(bp, " dst-ip6"); - print_ip6(bp, insntod(cmd, ip6)); + print_ip6(bp, insntoc(cmd, ip6)); break; case O_MAC_SRC_LOOKUP: bprintf(bp, " src-mac"); @@ -1630,11 +1685,11 @@ print_mac_lookup(bp, fo, cmd); break; case O_FLOW6ID: - print_flow6id(bp, insntod(cmd, u32)); + print_flow6id(bp, insntoc(cmd, u32)); break; case O_IP_DSTPORT: case O_IP_SRCPORT: - print_newports(bp, insntod(cmd, u16), state->proto, + print_newports(bp, insntoc(cmd, u16), state->proto, (state->flags & (HAVE_SRCIP | HAVE_DSTIP)) == (HAVE_SRCIP | HAVE_DSTIP) ? cmd->opcode: 0); break; @@ -1649,10 +1704,10 @@ state->proto = cmd->arg1; break; case O_MACADDR2: - print_mac(bp, insntod(cmd, mac)); + print_mac(bp, insntoc(cmd, mac)); break; case O_MAC_TYPE: - print_newports(bp, insntod(cmd, u16), + print_newports(bp, insntoc(cmd, u16), IPPROTO_ETHERTYPE, cmd->opcode); break; case O_FRAG: @@ -1695,26 +1750,27 @@ s = "recv"; else /* if (cmd->opcode == O_VIA) */ s = "via"; - switch (insntod(cmd, if)->name[0]) { + switch (insntoc(cmd, if)->name[0]) { case '\0': bprintf(bp, " %s %s", s, - inet_ntoa(insntod(cmd, if)->p.ip)); + inet_ntoa(insntoc(cmd, if)->p.ip)); break; case '\1': bprintf(bp, " %s table(%s)", s, table_search_ctlv(fo->tstate, - insntod(cmd, if)->p.kidx)); + insntoc(cmd, if)->p.kidx)); break; default: bprintf(bp, " %s %s", s, - insntod(cmd, if)->name); + insntoc(cmd, if)->name); } break; case O_IP_FLOW_LOOKUP: - s = table_search_ctlv(fo->tstate, cmd->arg1); + s = table_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx); bprintf(bp, " flow table(%s", s); - if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) - bprintf(bp, ",%u", insntod(cmd, u32)->d[0]); + if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_table)) + print_tvalue(bp, insntoc(cmd, table)); bprintf(bp, ")"); break; case O_IPID: @@ -1749,7 +1805,7 @@ } bprintf(bp, " %s %u", s, cmd->arg1); } else - print_newports(bp, insntod(cmd, u16), 0, + print_newports(bp, insntoc(cmd, u16), 0, cmd->opcode); break; case O_IPVER: @@ -1759,7 +1815,7 @@ bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5); break; case O_DSCP: - print_dscp(bp, insntod(cmd, u32)); + print_dscp(bp, insntoc(cmd, u32)); break; case O_IPOPT: print_flags(bp, "ipoptions", cmd, f_ipopts); @@ -1768,7 +1824,7 @@ print_flags(bp, "iptos", cmd, f_iptos); break; case O_ICMPTYPE: - print_icmptypes(bp, insntod(cmd, u32)); + print_icmptypes(bp, insntoc(cmd, u32)); break; case O_ESTAB: bprintf(bp, " established"); @@ -1781,30 +1837,30 @@ break; case O_TCPACK: bprintf(bp, " tcpack %d", - ntohl(insntod(cmd, u32)->d[0])); + ntohl(insntoc(cmd, u32)->d[0])); break; case O_TCPSEQ: bprintf(bp, " tcpseq %d", - ntohl(insntod(cmd, u32)->d[0])); + ntohl(insntoc(cmd, u32)->d[0])); break; case O_UID: - pwd = getpwuid(insntod(cmd, u32)->d[0]); + pwd = getpwuid(insntoc(cmd, u32)->d[0]); if (pwd != NULL) bprintf(bp, " uid %s", pwd->pw_name); else bprintf(bp, " uid %u", - insntod(cmd, u32)->d[0]); + insntoc(cmd, u32)->d[0]); break; case O_GID: - grp = getgrgid(insntod(cmd, u32)->d[0]); + grp = getgrgid(insntoc(cmd, u32)->d[0]); if (grp != NULL) bprintf(bp, " gid %s", grp->gr_name); else bprintf(bp, " gid %u", - insntod(cmd, u32)->d[0]); + insntoc(cmd, u32)->d[0]); break; case O_JAIL: - bprintf(bp, " jail %d", insntod(cmd, u32)->d[0]); + bprintf(bp, " jail %d", insntoc(cmd, u32)->d[0]); break; case O_VERREVPATH: bprintf(bp, " verrevpath"); @@ -1827,7 +1883,8 @@ else bprintf(bp, " record-state"); bprintf(bp, " :%s", - object_search_ctlv(fo->tstate, cmd->arg1, + object_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx, IPFW_TLV_STATE_NAME)); break; case O_LIMIT: @@ -1835,9 +1892,10 @@ bprintf(bp, " limit"); else bprintf(bp, " set-limit"); - print_limit_mask(bp, insntod(cmd, limit)); + print_limit_mask(bp, insntoc(cmd, limit)); bprintf(bp, " :%s", - object_search_ctlv(fo->tstate, cmd->arg1, + object_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx, IPFW_TLV_STATE_NAME)); break; case O_IP6: @@ -1851,7 +1909,7 @@ bprintf(bp, " ip4"); break; case O_ICMP6TYPE: - print_icmp6types(bp, insntod(cmd, u32)); + print_icmp6types(bp, insntoc(cmd, u32)); break; case O_EXT_HDR: print_ext6hdr(bp, cmd); @@ -1860,7 +1918,7 @@ if (F_LEN(cmd) == 1) bprint_uint_arg(bp, " tagged ", cmd->arg1); else - print_newports(bp, insntod(cmd, u16), + print_newports(bp, insntoc(cmd, u16), 0, O_TAGGED); break; case O_SKIP_ACTION: @@ -1871,12 +1929,10 @@ if (cmd->arg1 == IP_FW_TARG) bprintf(bp, " tablearg"); else - bprintf(bp, " %#x", - ((const ipfw_insn_u32 *)cmd)->d[0]); + bprintf(bp, " %#x", insntoc(cmd, u32)->d[0]); - if (((const ipfw_insn_u32 *)cmd)->d[1] != 0xFFFFFFFF) - bprintf(bp, ":%#x", - ((const ipfw_insn_u32 *)cmd)->d[1]); + if (insntoc(cmd, u32)->d[1] != 0xFFFFFFFF) + bprintf(bp, ":%#x", insntoc(cmd, u32)->d[1]); break; default: @@ -1929,14 +1985,14 @@ uint16_t port; if (cmd->opcode == O_FORWARD_IP) { - sa = insntod(cmd, sa); + sa = insntoc(cmd, sa); port = sa->sa.sin_port; if (sa->sa.sin_addr.s_addr == INADDR_ANY) bprintf(bp, "fwd tablearg"); else bprintf(bp, "fwd %s", inet_ntoa(sa->sa.sin_addr)); } else { - sa6 = insntod(cmd, sa6); + sa6 = insntoc(cmd, sa6); port = sa6->sa.sin6_port; bprintf(bp, "fwd "); if (getnameinfo((const struct sockaddr *)&sa6->sa, @@ -1948,6 +2004,26 @@ bprintf(bp, ",%u", port); } +static void +print_logdst(struct buf_pr *bp, uint16_t arg1) +{ + char const *comma = ""; + + bprintf(bp, " logdst ", arg1); + if (arg1 & IPFW_LOG_SYSLOG) { + bprintf(bp, "%ssyslog", comma); + comma = ","; + } + if (arg1 & IPFW_LOG_IPFW0) { + bprintf(bp, "%sipfw0", comma); + comma = ","; + } + if (arg1 & IPFW_LOG_RTSOCK) { + bprintf(bp, "%srtsock", comma); + comma = ","; + } +} + static int print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, struct show_state *state, const ipfw_insn *cmd) @@ -1959,8 +2035,9 @@ switch (cmd->opcode) { case O_CHECK_STATE: bprintf(bp, "check-state"); - if (cmd->arg1 != 0) - s = object_search_ctlv(fo->tstate, cmd->arg1, + if (insntoc(cmd, kidx)->kidx != 0) + s = object_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx, IPFW_TLV_STATE_NAME); else s = NULL; @@ -1985,7 +2062,7 @@ else if (cmd->arg1 == ICMP_UNREACH_NEEDFRAG && cmd->len == F_INSN_SIZE(ipfw_insn_u16)) bprintf(bp, "needfrag %u", - ((const ipfw_insn_u16 *)cmd)->ports[0]); + insntoc(cmd, u16)->ports[0]); else print_reject_code(bp, cmd->arg1); break; @@ -1998,7 +2075,7 @@ print_unreach6_code(bp, cmd->arg1); break; case O_SKIPTO: - bprint_uint_arg(bp, "skipto ", cmd->arg1); + bprint_uint_arg(bp, "skipto ", insntoc(cmd, u32)->d[0]); break; case O_PIPE: bprint_uint_arg(bp, "pipe ", cmd->arg1); @@ -2023,15 +2100,16 @@ print_fwd(bp, cmd); break; case O_LOG: - if (insntod(cmd, log)->max_log > 0) - bprintf(bp, " log logamount %d", - insntod(cmd, log)->max_log); - else - bprintf(bp, " log"); + bprintf(bp, " log"); + if (insntoc(cmd, log)->max_log > 0) + bprintf(bp, " logamount %d", + insntoc(cmd, log)->max_log); + if (cmd->arg1 != IPFW_LOG_DEFAULT) + print_logdst(bp, cmd->arg1); break; case O_ALTQ: #ifndef NO_ALTQ - print_altq_cmd(bp, insntod(cmd, altq)); + print_altq_cmd(bp, insntoc(cmd, altq)); #endif break; case O_TAG: @@ -2060,8 +2138,9 @@ * NOTE: in case when external action has no named * instances support, the second opcode isn't needed. */ - state->eaction = cmd; - s = object_search_ctlv(fo->tstate, cmd->arg1, + state->eaction = insntoc(cmd, kidx); + s = object_search_ctlv(fo->tstate, + state->eaction->kidx, IPFW_TLV_EACTION); if (match_token(rule_eactions, s) != -1) bprintf(bp, "%s", s); @@ -2081,11 +2160,12 @@ * we calculate TLV type using IPFW_TLV_EACTION_NAME() * macro. */ - s = object_search_ctlv(fo->tstate, cmd->arg1, 0); + s = object_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx, 0); if (s == NULL) s = object_search_ctlv(fo->tstate, - cmd->arg1, IPFW_TLV_EACTION_NAME( - state->eaction->arg1)); + insntoc(cmd, kidx)->kidx, IPFW_TLV_EACTION_NAME( + state->eaction->kidx)); bprintf(bp, " %s", s); break; case O_EXTERNAL_DATA: @@ -2117,17 +2197,18 @@ bprintf(bp, "reass"); break; case O_CALLRETURN: - if (cmd->len & F_NOT) - bprintf(bp, "return"); - else - bprint_uint_arg(bp, "call ", cmd->arg1); + if (cmd->len & F_NOT) { + s = match_value(return_types, cmd->arg1); + bprintf(bp, "return %s", s ? s: ""); + } else + bprint_uint_arg(bp, "call ", insntoc(cmd, u32)->d[0]); break; case O_SETMARK: if (cmd->arg1 == IP_FW_TARG) { bprintf(bp, "setmark tablearg"); break; } - bprintf(bp, "setmark %#x", ((const ipfw_insn_u32 *)cmd)->d[0]); + bprintf(bp, "setmark %#x", insntoc(cmd, u32)->d[0]); break; default: bprintf(bp, "** unrecognized action %d len %d ", @@ -2233,9 +2314,16 @@ count = portcnt = 0; for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { - if (match_opcode(cmd->opcode, opcodes, nops)) + if (match_opcode(cmd->opcode, opcodes, nops)) { + /* + * Special handling for O_IP_DST_LOOKUP when + * lookup type is not LOOKUP_NONE. + */ + if (cmd->opcode == O_IP_DST_LOOKUP && + IPFW_LOOKUP_TYPE(cmd) != LOOKUP_NONE) + continue; count++; - else if (cmd->opcode == portop) + } else if (cmd->opcode == portop) portcnt++; } if (count == 0) @@ -2292,6 +2380,12 @@ O_IP_DST_SET, O_IP6_DST, O_IP6_DST_MASK, O_IP6_DST_ME }; +#if IPFW_DEFAULT_RULE > 65535 +#define RULENUM_FORMAT "%06d" +#else +#define RULENUM_FORMAT "%05d" +#endif + static void show_static_rule(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr) @@ -2313,7 +2407,8 @@ warn("init_show_state() failed"); return; } - bprintf(bp, "%05u ", rule->rulenum); + + bprintf(bp, RULENUM_FORMAT " ", rule->rulenum); /* only if counters are available */ if (cntr != NULL) { @@ -2416,23 +2511,21 @@ { struct protoent *pe; struct in_addr a; - uint16_t rulenum; char buf[INET6_ADDRSTRLEN]; - if (d->expire == 0 && d->dyn_type != O_LIMIT_PARENT) + if (!d->expire && !(d->type == O_LIMIT_PARENT)) return; - bcopy(&d->rule, &rulenum, sizeof(rulenum)); - bprintf(bp, "%05d", rulenum); + bprintf(bp, RULENUM_FORMAT, d->rulenum); if (fo->pcwidth > 0 || fo->bcwidth > 0) { bprintf(bp, " "); pr_u64(bp, &d->pcnt, fo->pcwidth); pr_u64(bp, &d->bcnt, fo->bcwidth); bprintf(bp, "(%ds)", d->expire); } - switch (d->dyn_type) { + switch (d->type) { case O_LIMIT_PARENT: - bprintf(bp, " PARENT %d", d->count); + bprintf(bp, " PARENT %u", d->count); break; case O_LIMIT: bprintf(bp, " LIMIT"); @@ -2524,9 +2617,8 @@ ipfw_range_tlv rt; const char *msg; size_t size; - uint32_t masks[2]; + uint32_t masks[2], rulenum; int i; - uint16_t rulenum; uint8_t cmd; av++; @@ -2577,7 +2669,7 @@ if (av[0] == NULL || av[1] == NULL || av[2] == NULL || av[3] != NULL || _substrcmp(av[1], "to") != 0) errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); - rulenum = atoi(av[0]); + rulenum = (uint32_t)strtoul(av[0], NULL, 10); rt.new_set = atoi(av[2]); if (cmd == IP_FW_XMOVE) { rt.start_rule = rulenum; @@ -2630,6 +2722,18 @@ errx(EX_USAGE, "invalid set command %s\n", *av); } +static void +manage_skipto_cache(int op) +{ + ipfw_cmd_header req; + + memset(&req, 0, sizeof(req)); + req.size = sizeof(req); + req.cmd = op ? SKIPTO_CACHE_ENABLE : SKIPTO_CACHE_DISABLE; + + do_set3(IP_FW_SKIPTO_CACHE, &req.opheader, sizeof(req)); +} + void ipfw_sysctl_handler(char *av[], int which) { @@ -2654,6 +2758,8 @@ } else if (_substrcmp(*av, "dyn_keepalive") == 0) { sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0, &which, sizeof(which)); + } else if (_substrcmp(*av, "skipto_cache") == 0) { + manage_skipto_cache(which); #ifndef NO_ALTQ } else if (_substrcmp(*av, "altq") == 0) { altq_set_enabled(which); @@ -2672,7 +2778,6 @@ { ipfw_dyn_rule *d; int width; - uint8_t set; d = (ipfw_dyn_rule *)_state; /* Count _ALL_ states */ @@ -2681,13 +2786,9 @@ if (fo->show_counters == 0) return; - if (co->use_set) { - /* skip states from another set */ - bcopy((char *)&d->rule + sizeof(uint16_t), &set, - sizeof(uint8_t)); - if (set != co->use_set - 1) - return; - } + /* skip states from another set */ + if (co->use_set != 0 && d->set != co->use_set - 1) + return; width = pr_u64(NULL, &d->pcnt, 0); if (width > fo->pcwidth) @@ -2815,24 +2916,17 @@ list_dyn_state(struct cmdline_opts *co, struct format_opts *fo, void *_arg, void *_state) { - uint16_t rulenum; - uint8_t set; ipfw_dyn_rule *d; struct buf_pr *bp; d = (ipfw_dyn_rule *)_state; bp = (struct buf_pr *)_arg; - bcopy(&d->rule, &rulenum, sizeof(rulenum)); - if (rulenum > fo->last) + if (d->rulenum > fo->last) return; - if (co->use_set) { - bcopy((char *)&d->rule + sizeof(uint16_t), - &set, sizeof(uint8_t)); - if (set != co->use_set - 1) - return; - } - if (rulenum >= fo->first) { + if (co->use_set != 0 && d->set != co->use_set - 1) + return; + if (d->rulenum >= fo->first) { show_dyn_state(co, fo, bp, d); printf("%s\n", bp->buf); bp_flush(bp); @@ -3158,7 +3252,7 @@ return (0); } -static uint16_t +static uint32_t pack_object(struct tidx *tstate, const char *name, int otype) { ipfw_obj_ntlv *ntlv; @@ -3195,7 +3289,7 @@ return (ntlv->idx); } -static uint16_t +static uint32_t pack_table(struct tidx *tstate, const char *name) { @@ -3205,12 +3299,32 @@ return (pack_object(tstate, name, IPFW_TLV_TBL_NAME)); } +static void +fill_table_value(ipfw_insn *cmd, char *s) +{ + char *p; + int i; + + p = strchr(s, '='); + if (p != NULL) { + *p++ = '\0'; + i = match_token(tvalue_names, s); + if (i == -1) + errx(EX_USAGE, + "format: unknown table value name %s", s); + } else { + i = TVALUE_TAG; + p = s; + } + + IPFW_SET_TVALUE_TYPE(cmd, i); + insntod(cmd, table)->value = strtoul(p, NULL, 0); +} + void -fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode, - struct tidx *tstate) +fill_table(ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate) { - uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; - uint16_t uidx; + ipfw_insn_kidx *c = insntod(cmd, kidx); char *p; if ((p = strchr(av + 6, ')')) == NULL) @@ -3220,19 +3334,19 @@ if (p) *p++ = '\0'; - if ((uidx = pack_table(tstate, av + 6)) == 0) + if ((c->kidx = pack_table(tstate, av + 6)) == 0) errx(EX_DATAERR, "Invalid table name: %s", av + 6); cmd->opcode = opcode; - cmd->arg1 = uidx; if (p) { - cmd->len |= F_INSN_SIZE(ipfw_insn_u32); - d[0] = strtoul(p, NULL, 0); - } else - cmd->len |= F_INSN_SIZE(ipfw_insn); + cmd->len |= F_INSN_SIZE(ipfw_insn_table); + fill_table_value(cmd, p); + } else { + IPFW_SET_LOOKUP_TYPE(cmd, LOOKUP_NONE); + cmd->len |= F_INSN_SIZE(ipfw_insn_kidx); + } } - /* * fills the addr and mask fields in the instruction as appropriate from av. * Update length as appropriate. @@ -3463,7 +3577,7 @@ { ipfw_range_tlv rt; char *sep; - int i, j; + uint32_t i, j; int exitval = EX_OK; int do_set = 0; @@ -3496,8 +3610,8 @@ rt.set = i & 31; rt.flags = IPFW_RCFLAG_SET; } else { - rt.start_rule = i & 0xffff; - rt.end_rule = j & 0xffff; + rt.start_rule = i; + rt.end_rule = j; if (rt.start_rule == 0 && rt.end_rule == 0) rt.flags |= IPFW_RCFLAG_ALL; else @@ -3534,7 +3648,6 @@ exit(exitval); } - /* * fill the interface structure. We do not check the name as we can * create interfaces dynamically, so checking them at insert time @@ -3546,7 +3659,7 @@ fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate) { char *p; - uint16_t uidx; + uint32_t uidx; cmd->name[0] = '\0'; cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); @@ -3854,6 +3967,8 @@ { "to", TOK_OR }, { "via", TOK_OR }, { "{", TOK_OR }, + { "lookup", TOK_OR }, + { "tagged", TOK_OR }, { NULL, 0 } /* terminator */ }; @@ -3934,7 +4049,34 @@ return ret; } -static inline int +static uint16_t +parse_logdst(char *logdst_iter) +{ + char *token; + uint16_t ret; + + ret = IPFW_LOG_DEFAULT; + while ((token = strsep(&logdst_iter, ",")) != NULL) { + if (_substrcmp(token, "syslog") == 0) { + ret |= IPFW_LOG_SYSLOG; + continue; + } + if (_substrcmp(token, "ipfw0") == 0) { + /* XXX add multiple ipfw* */ + ret |= IPFW_LOG_IPFW0; + continue; + } + if (_substrcmp(token, "rtsock") == 0) { + ret |= IPFW_LOG_RTSOCK; + continue; + } + errx(EX_DATAERR, + "unsupported logdst token"); + } + return (ret); +} + +static inline uint32_t arg_or_targ_relaxed(const char *arg, const char *action) { uint32_t arg1 = (uint32_t)(-1); @@ -3951,7 +4093,7 @@ return (arg1); } -static inline uint16_t +static inline uint32_t arg_or_targ(const char *arg, const char *action) { uint32_t arg1 = arg_or_targ_relaxed(arg, action); @@ -4079,18 +4221,20 @@ case TOK_CHECKSTATE: have_state = action; action->opcode = O_CHECK_STATE; + action->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_ACTLEN; if (*av == NULL || match_token(rule_options, *av) == TOK_COMMENT) { - action->arg1 = pack_object(tstate, + insntod(have_state, kidx)->kidx = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); break; } if (*av[0] == ':') { if (strcmp(*av + 1, "any") == 0) - action->arg1 = 0; + insntod(have_state, kidx)->kidx = 0; else if (state_check_name(*av + 1) == 0) - action->arg1 = pack_object(tstate, *av + 1, - IPFW_TLV_STATE_NAME); + insntod(have_state, kidx)->kidx = pack_object( + tstate, *av + 1, IPFW_TLV_STATE_NAME); else errx(EX_DATAERR, "Invalid state name %s", *av); @@ -4147,7 +4291,7 @@ errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); action->len = F_INSN_SIZE(ipfw_insn_u16); - ((ipfw_insn_u16 *)action)->ports[0] = mtu; + insntod(action, u16)->ports[0] = mtu; av++; } break; @@ -4185,7 +4329,12 @@ break; case TOK_SKIPTO: action->opcode = O_SKIPTO; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + action->len = F_INSN_SIZE(ipfw_insn_u32); + CHECK_ACTLEN; + insntod(action, u32)->d[0] = arg_or_targ(av[0], *(av - 1)); + if (insntod(action, u32)->d[0] < IPFW_ARG_MIN || + insntod(action, u32)->d[0] > IPFW_DEFAULT_RULE) + errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); av++; break; case TOK_NETGRAPH: @@ -4210,7 +4359,11 @@ break; case TOK_CALL: action->opcode = O_CALLRETURN; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + action->len = F_INSN_SIZE(ipfw_insn_u32); + insntod(action, u32)->d[0] = arg_or_targ(av[0], *(av - 1)); + if (insntod(action, u32)->d[0] < IPFW_ARG_MIN || + insntod(action, u32)->d[0] > IPFW_DEFAULT_RULE) + errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); av++; break; @@ -4374,7 +4527,21 @@ } case TOK_REASS: - action->opcode = O_REASS; + action->opcode = O_CALLRETURN; + action->len = F_INSN_SIZE(ipfw_insn_u32) | F_NOT; + if (*av != NULL) { + /* + * Return type is optional. + * By default we use RETURN_NEXT_RULENUM. + */ + i = match_token(return_types, *av); + if (i >= 0) { + action->arg1 = i; + av++; + } else + action->arg1 = RETURN_NEXT_RULENUM; + } + CHECK_ACTLEN; break; case TOK_RETURN: @@ -4388,8 +4555,7 @@ if (strcmp(*av, "tablearg") == 0) { action->arg1 = IP_FW_TARG; } else { - ((ipfw_insn_u32 *)action)->d[0] = - strtoul(*av, NULL, 0); + insntod(action, u32)->d[0] = strtoul(*av, NULL, 0); /* This is not a tablearg */ action->arg1 |= 0x8000; } @@ -4400,12 +4566,16 @@ case TOK_TCPSETMSS: { u_long mss; - uint16_t idx; + uint32_t idx; idx = pack_object(tstate, "tcp-setmss", IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); - fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); + action->opcode = O_EXTERNAL_ACTION; + action->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_ACTLEN; + insntod(action, kidx)->kidx = idx; + NEED1("Missing MSS value"); action = next_cmd(action, &ablen); action->len = 1; @@ -4439,12 +4609,13 @@ idx = pack_object(tstate, *av, IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); - fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); + action->opcode = O_EXTERNAL_ACTION; + action->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_ACTLEN; + insntod(action, kidx)->kidx = idx; + av++; NEED1("Missing eaction instance name"); - action = next_cmd(action, &ablen); - action->len = 1; - CHECK_ACTLEN; if (eaction_check_name(*av) != 0) errx(EX_DATAERR, "Invalid eaction instance name %s", *av); @@ -4456,7 +4627,11 @@ idx = pack_object(tstate, *av, 0); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); - fill_cmd(action, O_EXTERNAL_INSTANCE, 0, idx); + action = next_cmd(action, &ablen); + action->opcode = O_EXTERNAL_INSTANCE; + action->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_ACTLEN; + insntod(action, kidx)->kidx = idx; av++; } } @@ -4484,6 +4659,15 @@ cmd->len = F_INSN_SIZE(ipfw_insn_log); CHECK_CMDLEN; cmd->opcode = O_LOG; + cmd->arg1 = IPFW_LOG_DEFAULT; + /* logdst before logamount */ + if (av[0] && _substrcmp(*av, "logdst") == 0) { + av++; + NEED1("logdst requires argument"); + cmd->arg1 = parse_logdst(*av); + av++; + } + if (av[0] && _substrcmp(*av, "logamount") == 0) { av++; NEED1("logamount requires argument"); @@ -4505,6 +4689,14 @@ "net.inet.ip.fw.verbose_limit"); } } + + /* logdst after logamount */ + if (av[0] && _substrcmp(*av, "logdst") == 0) { + av++; + NEED1("logdst requires argument"); + cmd->arg1 = parse_logdst(*av); + av++; + } } break; @@ -4817,7 +5009,7 @@ case TOK_VIA: NEED1("recv, xmit, via require interface name" " or address"); - fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate); + fill_iface(insntod(cmd, if), av[0], cblen, tstate); av++; if (F_LEN(cmd) == 0) /* not a valid address */ break; @@ -5027,7 +5219,7 @@ case TOK_KEEPSTATE: case TOK_RECORDSTATE: { - uint16_t uidx; + uint32_t uidx; if (open_par) errx(EX_USAGE, "keep-state or record-state cannot be part " @@ -5047,13 +5239,16 @@ IPFW_TLV_STATE_NAME); have_state = cmd; have_rstate = i == TOK_RECORDSTATE; - fill_cmd(cmd, O_KEEP_STATE, 0, uidx); + cmd->opcode = O_KEEP_STATE; + cmd->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_CMDLEN; + insntod(have_state, kidx)->kidx = uidx; break; } case TOK_LIMIT: case TOK_SETLIMIT: { - ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; + ipfw_insn_limit *c = insntod(cmd, limit); int val; if (open_par) @@ -5065,10 +5260,10 @@ have_state = cmd; have_rstate = i == TOK_SETLIMIT; + cmd->opcode = O_LIMIT; cmd->len = F_INSN_SIZE(ipfw_insn_limit); CHECK_CMDLEN; - cmd->opcode = O_LIMIT; - c->limit_mask = c->conn_limit = 0; + c->limit_mask = c->conn_limit = c->kidx = 0; while ( av[0] != NULL ) { if ((val = match_token(limit_masks, *av)) <= 0) @@ -5088,11 +5283,11 @@ if (state_check_name(*av + 1) != 0) errx(EX_DATAERR, "Invalid state name %s", *av); - cmd->arg1 = pack_object(tstate, *av + 1, + c->kidx = pack_object(tstate, *av + 1, IPFW_TLV_STATE_NAME); av++; } else - cmd->arg1 = pack_object(tstate, + c->kidx = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); break; } @@ -5249,24 +5444,55 @@ break; case TOK_LOOKUP: { - ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd; + /* optional mask for some LOOKUP types */ + ipfw_insn_table *c = insntod(cmd, table); + char *lkey; if (!av[0] || !av[1]) - errx(EX_USAGE, "format: lookup argument tablenum"); + errx(EX_USAGE, + "format: lookup argument tablenum"); cmd->opcode = O_IP_DST_LOOKUP; - cmd->len |= F_INSN_SIZE(ipfw_insn) + 2; - i = match_token(lookup_keys, *av); - if (i == -1) - errx(EX_USAGE, "format: cannot lookup on %s", *av); - __PAST_END(c->d, 1) = i; - av++; - if ((i = pack_table(tstate, *av)) == 0) - errx(EX_DATAERR, "Invalid table name: %s", *av); + lkey = strsep(av, ":"); + i = match_token(lookup_keys, lkey); + if (i == -1) + errx(EX_USAGE, + "format: cannot lookup on %s", lkey); + /* masked lookup key */ + if (*av != NULL) { + switch (i) { + case LOOKUP_DST_PORT: + case LOOKUP_SRC_PORT: + case LOOKUP_UID: + case LOOKUP_JAIL: + case LOOKUP_DSCP: + case LOOKUP_MARK: + case LOOKUP_RULENUM: + break; + default: + errx(EX_USAGE, + "masked lookup is not supported " + "for %s", lkey); + } + cmd->len |= F_INSN_SIZE(ipfw_insn_table); + c->value = strtoul(*av, NULL, 0); + if (c->value == 0) + errx(EX_USAGE, + "all-zeroes bitmask for lookup " + "is meaningless"); + } else { + cmd->len |= F_INSN_SIZE(ipfw_insn_kidx); + } + CHECK_CMDLEN; - cmd->arg1 = i; + IPFW_SET_LOOKUP_TYPE(cmd, i); av++; - } + c->kidx = pack_table(tstate, *av); + if (c->kidx == 0) + errx(EX_DATAERR, + "Invalid table name: %s", *av); + av++; + } break; case TOK_FLOW: NEED1("missing table name"); @@ -5329,7 +5555,9 @@ * generate O_PROBE_STATE if necessary */ if (have_state && have_state->opcode != O_CHECK_STATE && !have_rstate) { - fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1); + dst->opcode = O_PROBE_STATE; + dst->len = F_INSN_SIZE(ipfw_insn_kidx); + insntod(dst, kidx)->kidx = insntod(have_state, kidx)->kidx; dst = next_cmd(dst, &rblen); } @@ -5451,7 +5679,7 @@ } struct object_kt { - uint16_t uidx; + uint32_t uidx; uint16_t type; }; static int @@ -5485,7 +5713,7 @@ * Returns table name or NULL. */ static char * -object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type) +object_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx, uint16_t type) { ipfw_obj_ntlv *ntlv; struct object_kt key; @@ -5503,7 +5731,7 @@ } static char * -table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx) +table_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx) { return (object_search_ctlv(ctlv, idx, IPFW_TLV_TBL_NAME)); diff --git a/sbin/ipfw/nat64lsn.c b/sbin/ipfw/nat64lsn.c --- a/sbin/ipfw/nat64lsn.c +++ b/sbin/ipfw/nat64lsn.c @@ -380,6 +380,8 @@ { "-log", TOK_LOGOFF }, { "allow_private", TOK_PRIVATE }, { "-allow_private", TOK_PRIVATEOFF }, + { "swap_conf", TOK_SWAPCONF }, + { "-swap_conf", TOK_SWAPCONFOFF }, /* for compatibility with old configurations */ { "max_ports", TOK_MAX_PORTS }, /* unused */ { NULL, 0 } @@ -514,6 +516,12 @@ case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; + case TOK_SWAPCONF: + cfg->flags |= NAT64LSN_ALLOW_SWAPCONF; + break; + case TOK_SWAPCONFOFF: + cfg->flags &= ~NAT64LSN_ALLOW_SWAPCONF; + break; } } @@ -631,6 +639,12 @@ case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; + case TOK_SWAPCONF: + cfg->flags |= NAT64LSN_ALLOW_SWAPCONF; + break; + case TOK_SWAPCONFOFF: + cfg->flags &= ~NAT64LSN_ALLOW_SWAPCONF; + break; default: errx(EX_USAGE, "Can't change %s option", opt); } @@ -796,6 +810,8 @@ printf(" icmp_age %u", cfg->st_icmp_ttl); if (g_co.verbose || cfg->jmaxlen != NAT64LSN_JMAXLEN) printf(" jmaxlen %u", cfg->jmaxlen); + if (cfg->flags & NAT64LSN_ALLOW_SWAPCONF) + printf(" swap_conf"); if (cfg->flags & NAT64_LOG) printf(" log"); if (cfg->flags & NAT64_ALLOW_PRIVATE) diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c --- a/sbin/ipfw/tables.c +++ b/sbin/ipfw/tables.c @@ -312,7 +312,7 @@ void table_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set, - uint16_t uidx) + uint32_t uidx) { ntlv->head.type = IPFW_TLV_TBL_NAME; diff --git a/sbin/route/route.c b/sbin/route/route.c --- a/sbin/route/route.c +++ b/sbin/route/route.c @@ -1661,6 +1661,7 @@ "RTM_DELMADDR: multicast group membership removed from iface", "RTM_IFANNOUNCE: interface arrival/departure", "RTM_IEEE80211: IEEE 802.11 wireless event", + "RTM_IPFWLOG: IPFW log", }; static const char metricnames[] = diff --git a/sys/net/route.h b/sys/net/route.h --- a/sys/net/route.h +++ b/sys/net/route.h @@ -294,6 +294,7 @@ #define RTM_DELMADDR 0x10 /* (4) mcast group membership being deleted */ #define RTM_IFANNOUNCE 0x11 /* (5) iface arrival/departure */ #define RTM_IEEE80211 0x12 /* (5) IEEE80211 wireless event */ +#define RTM_IPFWLOG 0x13 /* (1) IPFW rule match log event */ #endif /* NETLINK_COMPAT*/ diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -75,6 +75,10 @@ uint16_t reserved[2]; /* Align to 64-bit boundary */ } ip_fw3_opheader; +#define IP_FW3_OPVER_0 0 +#define IP_FW3_OPVER_1 1 /* 32bit rulenum */ +#define IP_FW3_OPVER IP_FW3_OPVER_1 + /* IP_FW3 opcodes */ #define IP_FW_TABLE_XADD 86 /* add entry */ #define IP_FW_TABLE_XDEL 87 /* delete entry */ @@ -109,6 +113,7 @@ #define IP_FW_DUMP_SOPTCODES 116 /* Dump available sopts/versions */ #define IP_FW_DUMP_SRVOBJECTS 117 /* Dump existing named objects */ +#define IP_FW_SKIPTO_CACHE 118 /* Manage skipto cache */ #define IP_FW_NAT64STL_CREATE 130 /* Create stateless NAT64 instance */ #define IP_FW_NAT64STL_DESTROY 131 /* Destroy stateless NAT64 instance */ @@ -211,8 +216,8 @@ O_VERREVPATH, /* none */ O_VERSRCREACH, /* none */ - O_PROBE_STATE, /* none */ - O_KEEP_STATE, /* none */ + O_PROBE_STATE, /* v0:arg1=kidx, v1:kidx=kidx */ + O_KEEP_STATE, /* v0:arg1=kidx, v1:kidx=kidx */ O_LIMIT, /* ipfw_insn_limit */ O_LIMIT_PARENT, /* dyn_type, not an opcode. */ @@ -223,12 +228,13 @@ O_LOG, /* ipfw_insn_log */ O_PROB, /* u32 = match probability */ - O_CHECK_STATE, /* none */ + O_CHECK_STATE, /* v0:arg1=kidx, v1:kidx=kidx */ O_ACCEPT, /* none */ O_DENY, /* none */ O_REJECT, /* arg1=icmp arg (same as deny) */ O_COUNT, /* none */ - O_SKIPTO, /* arg1=next rule number */ + O_SKIPTO, /* v0:arg1=next rule number */ + /* v1:kidx= next rule number */ O_PIPE, /* arg1=pipe number */ O_QUEUE, /* arg1=queue number */ O_DIVERT, /* arg1=port number */ @@ -242,8 +248,10 @@ * More opcodes. */ O_IPSEC, /* has ipsec history */ - O_IP_SRC_LOOKUP, /* arg1=table number, u32=value */ + O_IP_SRC_LOOKUP, /* v0:arg1=table number, u32=value */ + /* v1:kidx=name, u32=value, arg1=key */ O_IP_DST_LOOKUP, /* arg1=table number, u32=value */ + /* v1:kidx=name, u32=value, arg1=key */ O_ANTISPOOF, /* none */ O_JAIL, /* u32 = id */ O_ALTQ, /* u32 = altq classif. qid */ @@ -278,23 +286,27 @@ O_SOCKARG, /* socket argument */ - O_CALLRETURN, /* arg1=called rule number */ + O_CALLRETURN, /* v0:arg1=called rule number */ + /* v1:kidx=called rule number */ O_FORWARD_IP6, /* fwd sockaddr_in6 */ O_DSCP, /* 2 u32 = DSCP mask */ O_SETDSCP, /* arg1=DSCP value */ - O_IP_FLOW_LOOKUP, /* arg1=table number, u32=value */ + O_IP_FLOW_LOOKUP, /* v0:arg1=table number, u32=value */ + /* v1:kidx=name, u32=value */ - O_EXTERNAL_ACTION, /* arg1=id of external action handler */ - O_EXTERNAL_INSTANCE, /* arg1=id of eaction handler instance */ + O_EXTERNAL_ACTION, /* v0:arg1=id of external action handler */ + /* v1:kidx=id of external action handler */ + O_EXTERNAL_INSTANCE, /* v0:arg1=id of eaction handler instance */ + /* v1:kidx=id of eaction handler instance */ O_EXTERNAL_DATA, /* variable length data */ O_SKIP_ACTION, /* none */ O_TCPMSS, /* arg1=MSS value */ - O_MAC_SRC_LOOKUP, /* arg1=table number, u32=value */ - O_MAC_DST_LOOKUP, /* arg1=table number, u32=value */ + O_MAC_SRC_LOOKUP, /* kidx=name, u32=value, arg1=key */ + O_MAC_DST_LOOKUP, /* kidx=name, u32=value, arg1=key */ O_SETMARK, /* u32 = value */ O_MARK, /* 2 u32 = value, bitmask */ @@ -302,22 +314,6 @@ O_LAST_OPCODE /* not an opcode! */ }; -/* - * Defines key types used by lookup instruction - */ -enum ipfw_table_lookup_type { - LOOKUP_DST_IP, - LOOKUP_SRC_IP, - LOOKUP_DST_PORT, - LOOKUP_SRC_PORT, - LOOKUP_UID, - LOOKUP_JAIL, - LOOKUP_DSCP, - LOOKUP_DST_MAC, - LOOKUP_SRC_MAC, - LOOKUP_MARK, -}; - /* * The extension header are filtered only for presence using a bit * vector with a flag for each header. @@ -392,6 +388,11 @@ u_int32_t d[1]; /* one or more */ } ipfw_insn_u32; +typedef struct _ipfw_insn_kidx { + ipfw_insn o; + uint32_t kidx; +} ipfw_insn_kidx; + /* * This is used to store IP addr-mask pairs. */ @@ -401,6 +402,47 @@ struct in_addr mask; } ipfw_insn_ip; +typedef struct _ipfw_insn_table { + ipfw_insn o; /* arg1 is optional lookup key */ + uint32_t kidx; /* table name index */ + uint32_t value; /* table value */ +} ipfw_insn_table; + +#define IPFW_LOOKUP_TYPE_MASK 0x00FF +#define IPFW_LOOKUP_TYPE(insn) ((insn)->arg1 & IPFW_LOOKUP_TYPE_MASK) +#define IPFW_SET_LOOKUP_TYPE(insn, type) do { \ + (insn)->arg1 &= ~IPFW_LOOKUP_TYPE_MASK; \ + (insn)->arg1 |= (type) & IPFW_LOOKUP_TYPE_MASK; \ +} while (0) + +/* + * Defines key types used by lookup instruction + */ +enum ipfw_table_lookup_type { + LOOKUP_NONE = 0, + LOOKUP_DST_IP, + LOOKUP_SRC_IP, + LOOKUP_DST_PORT, + LOOKUP_SRC_PORT, + LOOKUP_UID, + LOOKUP_JAIL, + LOOKUP_DSCP, + LOOKUP_DST_MAC, + LOOKUP_SRC_MAC, + LOOKUP_MARK, + LOOKUP_RULENUM, +}; + +enum ipfw_return_type { + RETURN_NEXT_RULENUM = 0, + RETURN_NEXT_RULE, +}; + +enum ipfw_skipto_cache_op { + SKIPTO_CACHE_DISABLE = 0, + SKIPTO_CACHE_ENABLE, +}; + /* * This is used to forward to a given address (ip). */ @@ -434,7 +476,8 @@ union { struct in_addr ip; int glob; - uint16_t kidx; + uint16_t kidx_v0; + uint32_t kidx; } p; char name[IFNAMSIZ]; } ipfw_insn_if; @@ -452,6 +495,7 @@ */ typedef struct _ipfw_insn_limit { ipfw_insn o; + u_int32_t kidx; u_int8_t _pad; u_int8_t limit_mask; /* combination of DYN_* below */ #define DYN_SRC_ADDR 0x1 @@ -462,6 +506,9 @@ u_int16_t conn_limit; } ipfw_insn_limit; +/* MAC/InfiniBand/etc address length */ +#define IPFW_MAX_L2_ADDR_LEN 20 + /* * This is used for log instructions. */ @@ -471,6 +518,22 @@ u_int32_t log_left; /* how many left to log */ } ipfw_insn_log; +/* ipfw_insn_log->o.arg1 bitmasks */ +#define IPFW_LOG_DEFAULT 0x0000 +#define IPFW_LOG_SYSLOG (1 << 15) +#define IPFW_LOG_IPFW0 (1 << 14) +#define IPFW_LOG_RTSOCK (1 << 13) + +typedef struct _ipfwlog_rtsock_hdr_v2 { + uint32_t rulenum; + uint32_t tablearg; + ipfw_insn cmd; + u_char ether_shost[IPFW_MAX_L2_ADDR_LEN]; + u_char ether_dhost[IPFW_MAX_L2_ADDR_LEN]; + uint32_t mark; + char comment[0]; +} ipfwlog_rtsock_hdr_v2; + /* Legacy NAT structures, compat only */ #ifndef _KERNEL /* @@ -604,6 +667,10 @@ */ } ipfw_insn_icmp6; +/* Convert pointer to instruction with specified type */ +#define insntod(p, type) ((ipfw_insn_ ## type *)(p)) +#define insntoc(p, type) ((const ipfw_insn_ ## type *)(p)) + /* * Here we have the structure representing an ipfw rule. * @@ -719,30 +786,29 @@ /* * Dynamic ipfw rule. */ -typedef struct _ipfw_dyn_rule ipfw_dyn_rule; - -struct _ipfw_dyn_rule { - ipfw_dyn_rule *next; /* linked list of rules. */ - struct ip_fw *rule; /* pointer to rule */ - /* 'rule' is used to pass up the rule number (from the parent) */ +#define IPFW_DYN_ORPHANED 0x40000 /* state's parent rule was deleted */ - ipfw_dyn_rule *parent; /* pointer to parent rule */ - u_int64_t pcnt; /* packet match counter */ - u_int64_t bcnt; /* byte match counter */ +typedef struct _ipfw_dyn_rule { struct ipfw_flow_id id; /* (masked) flow id */ - u_int32_t expire; /* expire time */ - u_int32_t bucket; /* which bucket in hash table */ - u_int32_t state; /* state of this rule (typically a + uint8_t set; + uint8_t type; /* rule type */ + uint16_t pad; + uint32_t expire; /* expire time */ + uint32_t rulenum; /* parent's rule number */ + uint32_t kidx; /* index of named object */ + uint64_t pcnt; /* packet match counter */ + uint64_t bcnt; /* byte match counter */ + uint32_t hashval; /* hash value */ + union { + uint32_t state; /* state of this rule (typically a * combination of TCP flags) */ -#define IPFW_DYN_ORPHANED 0x40000 /* state's parent rule was deleted */ - u_int32_t ack_fwd; /* most recent ACKs in forward */ - u_int32_t ack_rev; /* and reverse directions (used */ + uint32_t count; /* number of linked states */ + }; + uint32_t ack_fwd; /* most recent ACKs in forward */ + uint32_t ack_rev; /* and reverse directions (used */ /* to generate keepalives) */ - u_int16_t dyn_type; /* rule type */ - u_int16_t count; /* refcount */ - u_int16_t kidx; /* index of named object */ -} __packed __aligned(8); +} __packed __aligned(8) ipfw_dyn_rule; /* * Definitions for IP option names. @@ -794,16 +860,6 @@ #define IPFW_VTYPE_NH6 0x00000400 /* IPv6 nexthop */ #define IPFW_VTYPE_MARK 0x00000800 /* [fw]mark */ -/* MAC/InfiniBand/etc address length */ -#define IPFW_MAX_L2_ADDR_LEN 20 - -typedef struct _ipfw_table_entry { - in_addr_t addr; /* network address */ - u_int32_t value; /* value */ - u_int16_t tbl; /* table number */ - u_int8_t masklen; /* mask length */ -} ipfw_table_entry; - typedef struct _ipfw_table_xentry { uint16_t len; /* Total entry length */ uint8_t type; /* entry type */ @@ -819,13 +875,6 @@ } ipfw_table_xentry; #define IPFW_TCF_INET 0x01 /* CIDR flags: IPv4 record */ -typedef struct _ipfw_table { - u_int32_t size; /* size of entries in bytes */ - u_int32_t cnt; /* # of entries */ - u_int16_t tbl; /* table number */ - ipfw_table_entry ent[0]; /* entries */ -} ipfw_table; - typedef struct _ipfw_xtable { ip_fw3_opheader opheader; /* IP_FW3 opcode */ uint32_t size; /* size of entries in bytes */ @@ -865,10 +914,10 @@ /* Object name TLV */ typedef struct _ipfw_obj_ntlv { ipfw_obj_tlv head; /* TLV header */ - uint16_t idx; /* Name index */ + uint32_t idx; /* Name index */ uint8_t set; /* set, if applicable */ uint8_t type; /* object type, if applicable */ - uint32_t spare; /* unused */ + uint16_t spare; /* unused */ char name[64]; /* Null-terminated name */ } ipfw_obj_ntlv; @@ -891,19 +940,40 @@ } a; }; +#define IPFW_TVALUE_TYPE_MASK 0xFF00 +#define IPFW_TVALUE_TYPE(insn) (((insn)->arg1 & IPFW_TVALUE_TYPE_MASK) >> 8) +#define IPFW_SET_TVALUE_TYPE(insn, type) do { \ + (insn)->arg1 &= ~IPFW_TVALUE_TYPE_MASK; \ + (insn)->arg1 |= ((type) << 8) & IPFW_TVALUE_TYPE_MASK; \ +} while (0) + +enum ipfw_table_value_type { + TVALUE_TAG = 0, + TVALUE_PIPE, + TVALUE_DIVERT, + TVALUE_SKIPTO, + TVALUE_NETGRAPH, + TVALUE_FIB, + TVALUE_NAT, + TVALUE_NH4, + TVALUE_DSCP, + TVALUE_LIMIT, + TVALUE_MARK, +}; + /* 64-byte structure representing multi-field table value */ typedef struct _ipfw_table_value { uint32_t tag; /* O_TAG/O_TAGGED */ - uint32_t pipe; /* O_PIPE/O_QUEUE */ + uint16_t pipe; /* O_PIPE/O_QUEUE */ uint16_t divert; /* O_DIVERT/O_TEE */ - uint16_t skipto; /* skipto, CALLRET */ + uint32_t skipto; /* skipto, CALLRET */ uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */ - uint32_t fib; /* O_SETFIB */ uint32_t nat; /* O_NAT */ uint32_t nh4; + uint16_t fib; /* O_SETFIB */ uint8_t dscp; uint8_t spare0; - uint16_t kidx; /* value kernel index */ + uint32_t kidx; /* value kernel index */ struct in6_addr nh6; uint32_t limit; /* O_LIMIT */ uint32_t zoneid; /* scope zone id for nh6 */ @@ -918,8 +988,7 @@ uint8_t masklen; /* mask length */ uint8_t result; /* request result */ uint8_t spare0; - uint16_t idx; /* Table name index */ - uint16_t spare1; + uint32_t idx; /* Table name index */ union { /* Longest field needs to be aligned by 8-byte boundary */ struct in_addr addr; /* IPv4 address */ @@ -966,8 +1035,8 @@ typedef struct _ipfw_range_tlv { ipfw_obj_tlv head; /* TLV header */ uint32_t flags; /* Range flags */ - uint16_t start_rule; /* Range start */ - uint16_t end_rule; /* Range end */ + uint32_t start_rule; /* Range start */ + uint32_t end_rule; /* Range end */ uint32_t set; /* Range set to match */ uint32_t new_set; /* New set to move/swap to */ } ipfw_range_tlv; @@ -1051,10 +1120,16 @@ uint64_t spare1; } ipfw_ta_info; +typedef struct _ipfw_cmd_header { /* control command header */ + ip_fw3_opheader opheader; /* IP_FW3 opcode */ + uint32_t size; /* Total size (incl. header) */ + uint32_t cmd; /* command */ +} ipfw_cmd_header; + typedef struct _ipfw_obj_header { ip_fw3_opheader opheader; /* IP_FW3 opcode */ - uint32_t spare; - uint16_t idx; /* object name index */ + uint32_t idx; /* object name index */ + uint16_t spare; uint8_t objtype; /* object type */ uint8_t objsubtype; /* object subtype */ ipfw_obj_ntlv ntlv; /* object name tlv */ diff --git a/sys/netinet6/ip_fw_nat64.h b/sys/netinet6/ip_fw_nat64.h --- a/sys/netinet6/ip_fw_nat64.h +++ b/sys/netinet6/ip_fw_nat64.h @@ -84,9 +84,9 @@ uint64_t spgcreated; /* Number of portgroups created */ uint64_t spgdeleted; /* Number of portgroups deleted */ uint64_t hostcount; /* Number of hosts */ - uint64_t tcpchunks; /* Number of TCP chunks */ - uint64_t udpchunks; /* Number of UDP chunks */ - uint64_t icmpchunks; /* Number of ICMP chunks */ + uint64_t tcpchunks; /* Number of TCP portgroups */ + uint64_t udpchunks; /* Number of UDP portgroups */ + uint64_t icmpchunks; /* Number of ICMP portgroups */ uint64_t _reserved[4]; }; @@ -95,6 +95,10 @@ #define NAT64_ALLOW_PRIVATE 0x0002 /* Allow private IPv4 address * translation */ +#define NAT64LSN_ALLOW_SWAPCONF 0x0004 /* Allow configuration exchange + * between NAT64LSN instances + * during the sets swapping. + */ typedef struct _ipfw_nat64stl_cfg { char name[64]; /* NAT name */ ipfw_obj_ntlv ntlv6; /* object name tlv */ diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -144,16 +144,15 @@ /* Use 128 tables by default */ static unsigned int default_fw_tables = IPFW_TABLES_DEFAULT; -static int jump_lookup_pos(struct ip_fw_chain *chain, struct ip_fw *f, int num, - int tablearg, int jump_backwards); -#ifndef LINEAR_SKIPTO -static int jump_cached(struct ip_fw_chain *chain, struct ip_fw *f, int num, - int tablearg, int jump_backwards); -#define JUMP(ch, f, num, targ, back) jump_cached(ch, f, num, targ, back) +#ifndef IPFIREWALL_LINEAR_SKIPTO +VNET_DEFINE(int, skipto_cache) = 0; #else -#define JUMP(ch, f, num, targ, back) jump_lookup_pos(ch, f, num, targ, back) +VNET_DEFINE(int, skipto_cache) = 1; #endif +static uint32_t jump(struct ip_fw_chain *chain, struct ip_fw *f, + uint32_t num, int tablearg, bool jump_backwards); + /* * Each rule belongs to one of 32 different sets (0..31). * The variable set_disable contains one bit per set. @@ -207,6 +206,9 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(verbose_limit), 0, "Set upper limit of matches of ipfw rules logged"); +SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, skipto_cache, + CTLFLAG_VNET | CTLFLAG_RD, &VNET_NAME(skipto_cache), 0, + "Status of linear skipto cache: 1 - enabled, 0 - disabled."); SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, default_rule, CTLFLAG_RD, &dummy_def, 0, "The default/max possible rule number."); @@ -1236,38 +1238,36 @@ args->flags |= IPFW_ARGS_REF; } -static int -jump_lookup_pos(struct ip_fw_chain *chain, struct ip_fw *f, int num, - int tablearg, int jump_backwards) +static uint32_t +jump_lookup_pos(struct ip_fw_chain *chain, struct ip_fw *f, uint32_t num, + int tablearg, bool jump_backwards) { int f_pos, i; + /* + * Make sure we do not jump backward. + */ i = IP_FW_ARG_TABLEARG(chain, num, skipto); - /* make sure we do not jump backward */ - if (jump_backwards == 0 && i <= f->rulenum) + if (!jump_backwards && i <= f->rulenum) i = f->rulenum + 1; -#ifndef LINEAR_SKIPTO - if (chain->idxmap != NULL) - f_pos = chain->idxmap[i]; - else + if (V_skipto_cache == 0) f_pos = ipfw_find_rule(chain, i, 0); -#else - f_pos = chain->idxmap[i]; -#endif /* LINEAR_SKIPTO */ + else { + /* + * Make sure we do not do out of bounds access. + */ + if (i >= IPFW_DEFAULT_RULE) + i = IPFW_DEFAULT_RULE - 1; + f_pos = chain->idxmap[i]; + } return (f_pos); } - -#ifndef LINEAR_SKIPTO -/* - * Helper function to enable cached rule lookups using - * cache.id and cache.pos fields in ipfw rule. - */ -static int -jump_cached(struct ip_fw_chain *chain, struct ip_fw *f, int num, - int tablearg, int jump_backwards) +static uint32_t +jump(struct ip_fw_chain *chain, struct ip_fw *f, uint32_t num, + int tablearg, bool jump_backwards) { int f_pos; @@ -1312,9 +1312,54 @@ #endif /* !__LP64__ */ return (f_pos); } -#endif /* !LINEAR_SKIPTO */ #define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f) + +static inline int +tvalue_match(struct ip_fw_chain *ch, const ipfw_insn_table *cmd, + uint32_t tablearg) +{ + uint32_t tvalue; + + switch (IPFW_TVALUE_TYPE(&cmd->o)) { + case TVALUE_PIPE: + tvalue = TARG_VAL(ch, tablearg, pipe); + break; + case TVALUE_DIVERT: + tvalue = TARG_VAL(ch, tablearg, divert); + break; + case TVALUE_SKIPTO: + tvalue = TARG_VAL(ch, tablearg, skipto); + break; + case TVALUE_NETGRAPH: + tvalue = TARG_VAL(ch, tablearg, netgraph); + break; + case TVALUE_FIB: + tvalue = TARG_VAL(ch, tablearg, fib); + break; + case TVALUE_NAT: + tvalue = TARG_VAL(ch, tablearg, nat); + break; + case TVALUE_NH4: + tvalue = TARG_VAL(ch, tablearg, nh4); + break; + case TVALUE_DSCP: + tvalue = TARG_VAL(ch, tablearg, dscp); + break; + case TVALUE_LIMIT: + tvalue = TARG_VAL(ch, tablearg, limit); + break; + case TVALUE_MARK: + tvalue = TARG_VAL(ch, tablearg, mark); + break; + case TVALUE_TAG: + default: + tvalue = TARG_VAL(ch, tablearg, tag); + break; + } + return (tvalue == cmd->value); +} + /* * The main check routine for the firewall. * @@ -1389,8 +1434,8 @@ #else struct ucred *ucred_cache = NULL; #endif + uint32_t f_pos = 0; /* index of current rule in the array */ int ucred_lookup = 0; - int f_pos = 0; /* index of current rule in the array */ int retval = 0; struct ifnet *oif, *iif; @@ -2050,104 +2095,133 @@ break; case O_IP_DST_LOOKUP: - { - if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) { - void *pkey; - uint32_t vidx, key; - uint16_t keylen = 0; /* zero if can't match the packet */ - - /* Determine lookup key type */ - vidx = ((ipfw_insn_u32 *)cmd)->d[1]; - switch (vidx) { - case LOOKUP_DST_IP: - case LOOKUP_SRC_IP: - /* Need IP frame */ - if (is_ipv6 == 0 && is_ipv4 == 0) - break; - if (vidx == LOOKUP_DST_IP) - pkey = is_ipv6 ? - (void *)&args->f_id.dst_ip6: - (void *)&dst_ip; + if (IPFW_LOOKUP_TYPE(cmd) != LOOKUP_NONE) { + void *pkey = NULL; + uint32_t key, vidx; + uint16_t keylen = 0; /* zero if can't match the packet */ + uint8_t lookup_type; + + lookup_type = IPFW_LOOKUP_TYPE(cmd); + + switch (lookup_type) { + case LOOKUP_DST_IP: + case LOOKUP_SRC_IP: + if (is_ipv4) { + keylen = sizeof(in_addr_t); + if (lookup_type == LOOKUP_DST_IP) + pkey = &dst_ip; else - pkey = is_ipv6 ? - (void *)&args->f_id.src_ip6: - (void *)&src_ip; - keylen = is_ipv6 ? - sizeof(struct in6_addr): - sizeof(in_addr_t); - break; - case LOOKUP_DST_PORT: - case LOOKUP_SRC_PORT: - /* Need IP frame */ - if (is_ipv6 == 0 && is_ipv4 == 0) - break; - /* Skip fragments */ - if (offset != 0) - break; - /* Skip proto without ports */ - if (proto != IPPROTO_TCP && - proto != IPPROTO_UDP && - proto != IPPROTO_UDPLITE && - proto != IPPROTO_SCTP) - break; - key = vidx == LOOKUP_DST_PORT ? - dst_port: - src_port; - pkey = &key; - keylen = sizeof(key); - break; - case LOOKUP_UID: - case LOOKUP_JAIL: - check_uidgid( - (ipfw_insn_u32 *)cmd, - args, &ucred_lookup, - &ucred_cache); - key = vidx == LOOKUP_UID ? - ucred_cache->cr_uid: - ucred_cache->cr_prison->pr_id; - pkey = &key; - keylen = sizeof(key); - break; - case LOOKUP_DSCP: - /* Need IP frame */ - if (is_ipv6 == 0 && is_ipv4 == 0) - break; - if (is_ipv6) - key = IPV6_DSCP( - (struct ip6_hdr *)ip) >> 2; + pkey = &src_ip; + } else if (is_ipv6) { + keylen = sizeof(struct in6_addr); + if (lookup_type == LOOKUP_DST_IP) + pkey = &args->f_id.dst_ip6; else - key = ip->ip_tos >> 2; - pkey = &key; - keylen = sizeof(key); + pkey = &args->f_id.src_ip6; + } else /* only for L3 */ break; - case LOOKUP_DST_MAC: - case LOOKUP_SRC_MAC: - /* Need ether frame */ - if ((args->flags & IPFW_ARGS_ETHER) == 0) - break; - pkey = vidx == LOOKUP_DST_MAC ? - eh->ether_dhost: - eh->ether_shost; - keylen = ETHER_ADDR_LEN; + case LOOKUP_DSCP: + if (is_ipv4) + key = ip->ip_tos >> 2; + else if (is_ipv6) + key = IPV6_DSCP( + (struct ip6_hdr *)ip) >> 2; + else + break; /* only for L3 */ + + key &= 0x3f; + if (cmdlen == F_INSN_SIZE(ipfw_insn_table)) + key &= insntod(cmd, table)->value; + pkey = &key; + keylen = sizeof(key); + break; + case LOOKUP_DST_PORT: + case LOOKUP_SRC_PORT: + /* only for L3 */ + if (is_ipv6 == 0 && is_ipv4 == 0) { break; - case LOOKUP_MARK: - key = args->rule.pkt_mark; - pkey = &key; - keylen = sizeof(key); + } + /* Skip fragments */ + if (offset != 0) { break; } - if (keylen == 0) + /* Skip proto without ports */ + if (proto != IPPROTO_TCP && + proto != IPPROTO_UDP && + proto != IPPROTO_UDPLITE && + proto != IPPROTO_SCTP) break; - match = ipfw_lookup_table(chain, - cmd->arg1, keylen, pkey, &vidx); - if (!match) + if (lookup_type == LOOKUP_DST_PORT) + key = dst_port; + else + key = src_port; + pkey = &key; + if (cmdlen == F_INSN_SIZE(ipfw_insn_table)) + key &= insntod(cmd, table)->value; + keylen = sizeof(key); + break; + case LOOKUP_DST_MAC: + case LOOKUP_SRC_MAC: + /* only for L2 */ + if ((args->flags & IPFW_ARGS_ETHER) == 0) break; - tablearg = vidx; + + pkey = lookup_type == LOOKUP_DST_MAC ? + eh->ether_dhost : eh->ether_shost; + keylen = ETHER_ADDR_LEN; + break; +#ifndef USERSPACE + case LOOKUP_UID: + case LOOKUP_JAIL: + check_uidgid(insntod(cmd, u32), + args, &ucred_lookup, +#ifdef __FreeBSD__ + &ucred_cache); + if (lookup_type == LOOKUP_UID) + key = ucred_cache->cr_uid; + else if (lookup_type == LOOKUP_JAIL) + key = ucred_cache->cr_prison->pr_id; +#else /* !__FreeBSD__ */ + (void *)&ucred_cache); + if (lookup_type == LOOKUP_UID) + key = ucred_cache.uid; + else if (lookup_type == LOOKUP_JAIL) + key = ucred_cache.xid; +#endif /* !__FreeBSD__ */ + pkey = &key; + if (cmdlen == F_INSN_SIZE(ipfw_insn_table)) + key &= insntod(cmd, table)->value; + keylen = sizeof(key); + break; +#endif /* !USERSPACE */ + case LOOKUP_MARK: + key = args->rule.pkt_mark; + if (cmdlen == F_INSN_SIZE(ipfw_insn_table)) + key &= insntod(cmd, table)->value; + pkey = &key; + keylen = sizeof(key); + break; + case LOOKUP_RULENUM: + key = f->rulenum; + if (cmdlen == F_INSN_SIZE(ipfw_insn_table)) + key &= insntod(cmd, table)->value; + pkey = &key; + keylen = sizeof(key); break; } - /* cmdlen =< F_INSN_SIZE(ipfw_insn_u32) */ - /* FALLTHROUGH */ + /* unknown key type */ + if (keylen == 0) + break; + match = ipfw_lookup_table(chain, + insntod(cmd, kidx)->kidx, keylen, + pkey, &vidx); + + if (match) + tablearg = vidx; + break; } + /* LOOKUP_NONE */ + /* FALLTHROUGH */ case O_IP_SRC_LOOKUP: { void *pkey; @@ -2168,13 +2242,14 @@ pkey = &args->f_id.src_ip6; } else break; - match = ipfw_lookup_table(chain, cmd->arg1, + match = ipfw_lookup_table(chain, + insntod(cmd, kidx)->kidx, keylen, pkey, &vidx); if (!match) break; - if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) { - match = ((ipfw_insn_u32 *)cmd)->d[0] == - TARG_VAL(chain, vidx, tag); + if (cmdlen == F_INSN_SIZE(ipfw_insn_table)) { + match = tvalue_match(chain, + insntod(cmd, table), vidx); if (!match) break; } @@ -2198,13 +2273,14 @@ else pkey = eh->ether_shost; - match = ipfw_lookup_table(chain, cmd->arg1, + match = ipfw_lookup_table(chain, + insntod(cmd, kidx)->kidx, keylen, pkey, &vidx); if (!match) break; - if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) { - match = ((ipfw_insn_u32 *)cmd)->d[0] == - TARG_VAL(chain, vidx, tag); + if (cmdlen == F_INSN_SIZE(ipfw_insn_table)) { + match = tvalue_match(chain, + insntod(cmd, table), vidx); if (!match) break; } @@ -2213,19 +2289,22 @@ } case O_IP_FLOW_LOOKUP: - { - uint32_t v = 0; - match = ipfw_lookup_table(chain, - cmd->arg1, 0, &args->f_id, &v); - if (!match) - break; - if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) - match = ((ipfw_insn_u32 *)cmd)->d[0] == - TARG_VAL(chain, v, tag); - if (match) - tablearg = v; - } + { + uint32_t vidx = 0; + + match = ipfw_lookup_table(chain, + insntod(cmd, kidx)->kidx, 0, + &args->f_id, &vidx); + if (!match) + break; + if (cmdlen == F_INSN_SIZE(ipfw_insn_table)) + match = tvalue_match(chain, + insntod(cmd, table), vidx); + if (match) + tablearg = vidx; break; + } + case O_IP_SRC_MASK: case O_IP_DST_MASK: if (is_ipv4) { @@ -2548,7 +2627,7 @@ case O_LOG: ipfw_log(chain, f, hlen, args, - offset | ip6f_mf, tablearg, ip); + offset | ip6f_mf, tablearg, ip, eh); match = 1; break; @@ -2787,14 +2866,14 @@ if (cmd->arg1 == IP_FW_TARG) mark = TARG_VAL(chain, tablearg, mark); else - mark = ((ipfw_insn_u32 *)cmd)->d[0]; + mark = insntoc(cmd, u32)->d[0]; match = (args->rule.pkt_mark & - ((ipfw_insn_u32 *)cmd)->d[1]) == - (mark & ((ipfw_insn_u32 *)cmd)->d[1]); + insntoc(cmd, u32)->d[1]) == + (mark & insntoc(cmd, u32)->d[1]); break; } - + /* * The second set of opcodes represents 'actions', * i.e. the terminal part of a rule once the packet @@ -2942,7 +3021,8 @@ case O_SKIPTO: IPFW_INC_RULE_COUNTER(f, pktlen); - f_pos = JUMP(chain, f, cmd->arg1, tablearg, 0); + f_pos = jump(chain, f, + insntod(cmd, u32)->d[0], tablearg, false); /* * Skip disabled rules, and re-enter * the inner loop with the correct @@ -2976,7 +3056,7 @@ * stack pointer. */ struct m_tag *mtag; - uint16_t jmpto, *stack; + uint32_t jmpto, *stack; #define IS_CALL ((cmd->len & F_NOT) == 0) #define IS_RETURN ((cmd->len & F_NOT) != 0) @@ -2992,12 +3072,31 @@ break; mtag = m_tag_next(m, mtag); } + + /* + * We keep ruleset id in the first element + * of stack. If it doesn't match chain->id, + * then we can't trust information in the + * stack, since rules were changed. + * We reset stack pointer to be able reuse + * tag if it will be needed. + */ + if (mtag != NULL) { + stack = (uint32_t *)(mtag + 1); + if (stack[0] != chain->id) { + stack[0] = chain->id; + mtag->m_tag_id = 0; + } + } + if (mtag == NULL && IS_CALL) { mtag = m_tag_alloc(MTAG_IPFW_CALL, 0, IPFW_CALLSTACK_SIZE * - sizeof(uint16_t), M_NOWAIT); + sizeof(uint32_t), M_NOWAIT); if (mtag != NULL) m_tag_prepend(m, mtag); + stack = (uint32_t *)(mtag + 1); + stack[0] = chain->id; } /* @@ -3010,7 +3109,8 @@ break; } if (IS_CALL && (mtag == NULL || - mtag->m_tag_id >= IPFW_CALLSTACK_SIZE)) { + mtag->m_tag_id >= + IPFW_CALLSTACK_SIZE - 1)) { printf("ipfw: call stack error, " "go to next rule\n"); l = 0; /* exit inner loop */ @@ -3018,25 +3118,21 @@ } IPFW_INC_RULE_COUNTER(f, pktlen); - stack = (uint16_t *)(mtag + 1); + stack = (uint32_t *)(mtag + 1); - /* - * The `call' action may use cached f_pos - * (in f->next_rule), whose version is written - * in f->next_rule. - * The `return' action, however, doesn't have - * fixed jump address in cmd->arg1 and can't use - * cache. - */ if (IS_CALL) { - stack[mtag->m_tag_id] = f->rulenum; - mtag->m_tag_id++; - f_pos = JUMP(chain, f, cmd->arg1, - tablearg, 1); + stack[++mtag->m_tag_id] = f_pos; + f_pos = jump(chain, f, + insntod(cmd, u32)->d[0], + tablearg, true); } else { /* `return' action */ - mtag->m_tag_id--; - jmpto = stack[mtag->m_tag_id] + 1; - f_pos = ipfw_find_rule(chain, jmpto, 0); + jmpto = stack[mtag->m_tag_id--]; + if (cmd->arg1 == RETURN_NEXT_RULE) + f_pos = jmpto + 1; + else /* RETURN_NEXT_RULENUM */ + f_pos = ipfw_find_rule(chain, + chain->map[ + jmpto]->rulenum + 1, 0); } /* @@ -3045,11 +3141,15 @@ * f_pos, f, l and cmd. * Also clear cmdlen and skip_or */ + MPASS(f_pos < chain->n_rules - 1); for (; f_pos < chain->n_rules - 1 && (V_set_disable & (1 << chain->map[f_pos]->set)); f_pos++) ; - /* Re-enter the inner loop at the dest rule. */ + /* + * Re-enter the inner loop at the dest + * rule. + */ f = chain->map[f_pos]; l = f->cmd_len; cmd = f->cmd; @@ -3302,7 +3402,7 @@ args->rule.pkt_mark = ( (cmd->arg1 == IP_FW_TARG) ? TARG_VAL(chain, tablearg, mark) : - ((ipfw_insn_u32 *)cmd)->d[0]); + insntoc(cmd, u32)->d[0]); IPFW_INC_RULE_COUNTER(f, pktlen); break; @@ -3554,9 +3654,7 @@ ipfw_dyn_init(chain); ipfw_eaction_init(chain, first); -#ifdef LINEAR_SKIPTO ipfw_init_skipto_cache(chain); -#endif ipfw_bpf_init(first); /* First set up some values that are compile time options */ @@ -3613,9 +3711,7 @@ for (i = 0; i < chain->n_rules; i++) ipfw_reap_add(chain, &reap, chain->map[i]); free(chain->map, M_IPFW); -#ifdef LINEAR_SKIPTO ipfw_destroy_skipto_cache(chain); -#endif IPFW_WUNLOCK(chain); IPFW_UH_WUNLOCK(chain); ipfw_destroy_tables(chain, last); diff --git a/sys/netpfil/ipfw/ip_fw_dynamic.c b/sys/netpfil/ipfw/ip_fw_dynamic.c --- a/sys/netpfil/ipfw/ip_fw_dynamic.c +++ b/sys/netpfil/ipfw/ip_fw_dynamic.c @@ -57,8 +57,8 @@ #include #include #include -#include #include +#include #include /* IN6_ARE_ADDR_EQUAL */ #ifdef INET6 @@ -132,9 +132,9 @@ uint32_t hashval; /* hash value used for hash resize */ uint16_t fibnum; /* fib used to send keepalives */ - uint8_t _pad[3]; + uint8_t _pad; uint8_t flags; /* internal flags */ - uint16_t rulenum; /* parent rule number */ + uint32_t rulenum; /* parent rule number */ uint32_t ruleid; /* parent rule id */ uint32_t state; /* TCP session state and flags */ @@ -159,8 +159,7 @@ struct dyn_parent { void *parent; /* pointer to parent rule */ uint32_t count; /* number of linked states */ - uint8_t _pad[2]; - uint16_t rulenum; /* parent rule number */ + uint32_t rulenum; /* parent rule number */ uint32_t ruleid; /* parent rule id */ uint32_t hashval; /* hash value used for hash resize */ uint32_t expire; /* expire time */ @@ -169,7 +168,8 @@ struct dyn_ipv4_state { uint8_t type; /* State type */ uint8_t proto; /* UL Protocol */ - uint16_t kidx; /* named object index */ + uint16_t spare; + uint32_t kidx; /* named object index */ uint16_t sport, dport; /* ULP source and destination ports */ in_addr_t src, dst; /* IPv4 source and destination */ @@ -501,12 +501,12 @@ const struct ipfw_flow_id *, uint32_t, const void *, struct ipfw_dyn_info *, int); static int dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *, - uint32_t, const void *, int, uint32_t, uint16_t); + uint32_t, const void *, int, uint32_t, uint32_t); static struct dyn_ipv6_state *dyn_alloc_ipv6_state( - const struct ipfw_flow_id *, uint32_t, uint16_t, uint8_t); -static int dyn_add_ipv6_state(void *, uint32_t, uint16_t, + const struct ipfw_flow_id *, uint32_t, uint32_t, uint8_t); +static int dyn_add_ipv6_state(void *, uint32_t, uint32_t, const struct ipfw_flow_id *, uint32_t, const void *, int, uint32_t, - struct ipfw_dyn_info *, uint16_t, uint16_t, uint8_t); + struct ipfw_dyn_info *, uint16_t, uint32_t, uint8_t); static void dyn_export_ipv6_state(const struct dyn_ipv6_state *, ipfw_dyn_rule *); @@ -519,33 +519,33 @@ static void dyn_send_keepalive_ipv6(struct ip_fw_chain *); static struct dyn_ipv6_state *dyn_lookup_ipv6_parent( - const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint16_t, + const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint32_t, uint32_t); static struct dyn_ipv6_state *dyn_lookup_ipv6_parent_locked( - const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint16_t, + const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint32_t, uint32_t); -static struct dyn_ipv6_state *dyn_add_ipv6_parent(void *, uint32_t, uint16_t, - const struct ipfw_flow_id *, uint32_t, uint32_t, uint32_t, uint16_t); +static struct dyn_ipv6_state *dyn_add_ipv6_parent(void *, uint32_t, uint32_t, + const struct ipfw_flow_id *, uint32_t, uint32_t, uint32_t, uint32_t); #endif /* INET6 */ /* Functions to work with limit states */ static void *dyn_get_parent_state(const struct ipfw_flow_id *, uint32_t, - struct ip_fw *, uint32_t, uint32_t, uint16_t); + struct ip_fw *, uint32_t, uint32_t, uint32_t); static struct dyn_ipv4_state *dyn_lookup_ipv4_parent( - const struct ipfw_flow_id *, const void *, uint32_t, uint16_t, uint32_t); + const struct ipfw_flow_id *, const void *, uint32_t, uint32_t, uint32_t); static struct dyn_ipv4_state *dyn_lookup_ipv4_parent_locked( - const struct ipfw_flow_id *, const void *, uint32_t, uint16_t, uint32_t); -static struct dyn_parent *dyn_alloc_parent(void *, uint32_t, uint16_t, + const struct ipfw_flow_id *, const void *, uint32_t, uint32_t, uint32_t); +static struct dyn_parent *dyn_alloc_parent(void *, uint32_t, uint32_t, uint32_t); -static struct dyn_ipv4_state *dyn_add_ipv4_parent(void *, uint32_t, uint16_t, - const struct ipfw_flow_id *, uint32_t, uint32_t, uint16_t); +static struct dyn_ipv4_state *dyn_add_ipv4_parent(void *, uint32_t, uint32_t, + const struct ipfw_flow_id *, uint32_t, uint32_t, uint32_t); static void dyn_tick(void *); static void dyn_expire_states(struct ip_fw_chain *, ipfw_range_tlv *); static void dyn_free_states(struct ip_fw_chain *); -static void dyn_export_parent(const struct dyn_parent *, uint16_t, uint8_t, +static void dyn_export_parent(const struct dyn_parent *, uint32_t, uint8_t, ipfw_dyn_rule *); -static void dyn_export_data(const struct dyn_data *, uint16_t, uint8_t, +static void dyn_export_data(const struct dyn_data *, uint32_t, uint8_t, uint8_t, ipfw_dyn_rule *); static uint32_t dyn_update_tcp_state(struct dyn_data *, const struct ipfw_flow_id *, const struct tcphdr *, int); @@ -556,12 +556,12 @@ struct dyn_ipv4_state *dyn_lookup_ipv4_state(const struct ipfw_flow_id *, const void *, struct ipfw_dyn_info *, int); static int dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *, - const void *, int, uint32_t, uint16_t); + const void *, int, uint32_t, uint32_t); static struct dyn_ipv4_state *dyn_alloc_ipv4_state( - const struct ipfw_flow_id *, uint16_t, uint8_t); -static int dyn_add_ipv4_state(void *, uint32_t, uint16_t, + const struct ipfw_flow_id *, uint32_t, uint8_t); +static int dyn_add_ipv4_state(void *, uint32_t, uint32_t, const struct ipfw_flow_id *, const void *, int, uint32_t, - struct ipfw_dyn_info *, uint16_t, uint16_t, uint8_t); + struct ipfw_dyn_info *, uint16_t, uint32_t, uint8_t); static void dyn_export_ipv4_state(const struct dyn_ipv4_state *, ipfw_dyn_rule *); @@ -574,34 +574,41 @@ char name[64]; }; -#define DYN_STATE_OBJ(ch, cmd) \ - ((struct dyn_state_obj *)SRV_OBJECT(ch, (cmd)->arg1)) /* * Classifier callback. * Return 0 if opcode contains object that should be referenced * or rewritten. */ static int -dyn_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +dyn_classify(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) { + ipfw_insn_kidx *cmd; - DYN_DEBUG("opcode %d, arg1 %d", cmd->opcode, cmd->arg1); + if (F_LEN(cmd0) < 2) + return (EINVAL); + + /* + * NOTE: ipfw_insn_kidx and ipfw_insn_limit has overlapped kidx + * field, so we can use one type to get access to kidx field. + */ + cmd = insntod(cmd0, kidx); + DYN_DEBUG("opcode %u, kidx %u", cmd0->opcode, cmd->kidx); /* Don't rewrite "check-state any" */ - if (cmd->arg1 == 0 && - cmd->opcode == O_CHECK_STATE) + if (cmd->kidx == 0 && + cmd0->opcode == O_CHECK_STATE) return (1); - *puidx = cmd->arg1; + *puidx = cmd->kidx; *ptype = 0; return (0); } static void -dyn_update(ipfw_insn *cmd, uint16_t idx) +dyn_update(ipfw_insn *cmd0, uint32_t idx) { - cmd->arg1 = idx; - DYN_DEBUG("opcode %d, arg1 %d", cmd->opcode, cmd->arg1); + insntod(cmd0, kidx)->kidx = idx; + DYN_DEBUG("opcode %u, kidx %u", cmd0->opcode, idx); } static int @@ -611,7 +618,7 @@ ipfw_obj_ntlv *ntlv; const char *name; - DYN_DEBUG("uidx %d", ti->uidx); + DYN_DEBUG("uidx %u", ti->uidx); if (ti->uidx != 0) { if (ti->tlvs == NULL) return (EINVAL); @@ -639,16 +646,16 @@ } static struct named_object * -dyn_findbykidx(struct ip_fw_chain *ch, uint16_t idx) +dyn_findbykidx(struct ip_fw_chain *ch, uint32_t idx) { - DYN_DEBUG("kidx %d", idx); + DYN_DEBUG("kidx %u", idx); return (ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), idx)); } static int dyn_create(struct ip_fw_chain *ch, struct tid_info *ti, - uint16_t *pkidx) + uint32_t *pkidx) { struct namedobj_instance *ni; struct dyn_state_obj *obj; @@ -656,7 +663,7 @@ ipfw_obj_ntlv *ntlv; char *name; - DYN_DEBUG("uidx %d", ti->uidx); + DYN_DEBUG("uidx %u", ti->uidx); if (ti->uidx != 0) { if (ti->tlvs == NULL) return (EINVAL); @@ -686,7 +693,7 @@ no->refcnt++; IPFW_UH_WUNLOCK(ch); free(obj, M_IPFW); - DYN_DEBUG("\tfound kidx %d", *pkidx); + DYN_DEBUG("\tfound kidx %u for name '%s'", *pkidx, no->name); return (0); } if (ipfw_objhash_alloc_idx(ni, &obj->no.kidx) != 0) { @@ -700,7 +707,7 @@ obj->no.refcnt++; *pkidx = obj->no.kidx; IPFW_UH_WUNLOCK(ch); - DYN_DEBUG("\tcreated kidx %d", *pkidx); + DYN_DEBUG("\tcreated kidx %u for name '%s'", *pkidx, name); return (0); } @@ -716,7 +723,7 @@ KASSERT(no->refcnt == 1, ("Destroying object '%s' (type %u, idx %u) with refcnt %u", no->name, no->etlv, no->kidx, no->refcnt)); - DYN_DEBUG("kidx %d", no->kidx); + DYN_DEBUG("kidx %u", no->kidx); obj = SRV_OBJECT(ch, no->kidx); SRV_OBJECT(ch, no->kidx) = NULL; ipfw_objhash_del(CHAIN_TO_SRV(ch), no); @@ -1078,7 +1085,7 @@ */ static int dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *pkt, - const void *ulp, int pktlen, uint32_t bucket, uint16_t kidx) + const void *ulp, int pktlen, uint32_t bucket, uint32_t kidx) { struct dyn_ipv4_state *s; int dir; @@ -1108,7 +1115,7 @@ struct dyn_ipv4_state * dyn_lookup_ipv4_parent(const struct ipfw_flow_id *pkt, const void *rule, - uint32_t ruleid, uint16_t rulenum, uint32_t hashval) + uint32_t ruleid, uint32_t rulenum, uint32_t hashval) { struct dyn_ipv4_state *s; uint32_t version, bucket; @@ -1144,7 +1151,7 @@ static struct dyn_ipv4_state * dyn_lookup_ipv4_parent_locked(const struct ipfw_flow_id *pkt, - const void *rule, uint32_t ruleid, uint16_t rulenum, uint32_t bucket) + const void *rule, uint32_t ruleid, uint32_t rulenum, uint32_t bucket) { struct dyn_ipv4_state *s; @@ -1226,7 +1233,7 @@ */ static int dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *pkt, uint32_t zoneid, - const void *ulp, int pktlen, uint32_t bucket, uint16_t kidx) + const void *ulp, int pktlen, uint32_t bucket, uint32_t kidx) { struct dyn_ipv6_state *s; int dir; @@ -1257,7 +1264,7 @@ static struct dyn_ipv6_state * dyn_lookup_ipv6_parent(const struct ipfw_flow_id *pkt, uint32_t zoneid, - const void *rule, uint32_t ruleid, uint16_t rulenum, uint32_t hashval) + const void *rule, uint32_t ruleid, uint32_t rulenum, uint32_t hashval) { struct dyn_ipv6_state *s; uint32_t version, bucket; @@ -1294,7 +1301,7 @@ static struct dyn_ipv6_state * dyn_lookup_ipv6_parent_locked(const struct ipfw_flow_id *pkt, uint32_t zoneid, - const void *rule, uint32_t ruleid, uint16_t rulenum, uint32_t bucket) + const void *rule, uint32_t ruleid, uint32_t rulenum, uint32_t bucket) { struct dyn_ipv6_state *s; @@ -1332,10 +1339,11 @@ struct ip_fw *rule; IPFW_RLOCK_ASSERT(&V_layer3_chain); + MPASS(F_LEN(cmd) >= F_INSN_SIZE(ipfw_insn_kidx)); data = NULL; rule = NULL; - info->kidx = cmd->arg1; + info->kidx = insntoc(cmd, kidx)->kidx; info->direction = MATCH_NONE; info->hashval = hash_packet(&args->f_id); @@ -1410,7 +1418,6 @@ */ if (V_layer3_chain.map[data->f_pos] == rule) { data->chain_id = V_layer3_chain.id; - info->f_pos = data->f_pos; } else if (V_dyn_keep_states != 0) { /* * The original rule pointer is still usable. @@ -1420,7 +1427,6 @@ MPASS(V_layer3_chain.n_rules > 1); data->chain_id = V_layer3_chain.id; data->f_pos = V_layer3_chain.n_rules - 2; - info->f_pos = data->f_pos; } else { rule = NULL; info->direction = MATCH_NONE; @@ -1429,8 +1435,8 @@ data->rulenum, data); /* info->f_pos doesn't matter here. */ } - } else - info->f_pos = data->f_pos; + } + info->f_pos = data->f_pos; } DYNSTATE_CRITICAL_EXIT(); #if 0 @@ -1451,7 +1457,7 @@ } static struct dyn_parent * -dyn_alloc_parent(void *parent, uint32_t ruleid, uint16_t rulenum, +dyn_alloc_parent(void *parent, uint32_t ruleid, uint32_t rulenum, uint32_t hashval) { struct dyn_parent *limit; @@ -1477,7 +1483,7 @@ } static struct dyn_data * -dyn_alloc_dyndata(void *parent, uint32_t ruleid, uint16_t rulenum, +dyn_alloc_dyndata(void *parent, uint32_t ruleid, uint32_t rulenum, const struct ipfw_flow_id *pkt, const void *ulp, int pktlen, uint32_t hashval, uint16_t fibnum) { @@ -1505,7 +1511,7 @@ } static struct dyn_ipv4_state * -dyn_alloc_ipv4_state(const struct ipfw_flow_id *pkt, uint16_t kidx, +dyn_alloc_ipv4_state(const struct ipfw_flow_id *pkt, uint32_t kidx, uint8_t type) { struct dyn_ipv4_state *s; @@ -1532,9 +1538,9 @@ * is not needed. */ static struct dyn_ipv4_state * -dyn_add_ipv4_parent(void *rule, uint32_t ruleid, uint16_t rulenum, +dyn_add_ipv4_parent(void *rule, uint32_t ruleid, uint32_t rulenum, const struct ipfw_flow_id *pkt, uint32_t hashval, uint32_t version, - uint16_t kidx) + uint32_t kidx) { struct dyn_ipv4_state *s; struct dyn_parent *limit; @@ -1585,10 +1591,10 @@ } static int -dyn_add_ipv4_state(void *parent, uint32_t ruleid, uint16_t rulenum, +dyn_add_ipv4_state(void *parent, uint32_t ruleid, uint32_t rulenum, const struct ipfw_flow_id *pkt, const void *ulp, int pktlen, uint32_t hashval, struct ipfw_dyn_info *info, uint16_t fibnum, - uint16_t kidx, uint8_t type) + uint32_t kidx, uint8_t type) { struct dyn_ipv4_state *s; void *data; @@ -1636,7 +1642,7 @@ #ifdef INET6 static struct dyn_ipv6_state * dyn_alloc_ipv6_state(const struct ipfw_flow_id *pkt, uint32_t zoneid, - uint16_t kidx, uint8_t type) + uint32_t kidx, uint8_t type) { struct dyn_ipv6_state *s; @@ -1663,9 +1669,9 @@ * is not needed. */ static struct dyn_ipv6_state * -dyn_add_ipv6_parent(void *rule, uint32_t ruleid, uint16_t rulenum, +dyn_add_ipv6_parent(void *rule, uint32_t ruleid, uint32_t rulenum, const struct ipfw_flow_id *pkt, uint32_t zoneid, uint32_t hashval, - uint32_t version, uint16_t kidx) + uint32_t version, uint32_t kidx) { struct dyn_ipv6_state *s; struct dyn_parent *limit; @@ -1716,10 +1722,10 @@ } static int -dyn_add_ipv6_state(void *parent, uint32_t ruleid, uint16_t rulenum, +dyn_add_ipv6_state(void *parent, uint32_t ruleid, uint32_t rulenum, const struct ipfw_flow_id *pkt, uint32_t zoneid, const void *ulp, int pktlen, uint32_t hashval, struct ipfw_dyn_info *info, - uint16_t fibnum, uint16_t kidx, uint8_t type) + uint16_t fibnum, uint32_t kidx, uint8_t type) { struct dyn_ipv6_state *s; struct dyn_data *data; @@ -1767,7 +1773,7 @@ static void * dyn_get_parent_state(const struct ipfw_flow_id *pkt, uint32_t zoneid, - struct ip_fw *rule, uint32_t hashval, uint32_t limit, uint16_t kidx) + struct ip_fw *rule, uint32_t hashval, uint32_t limit, uint32_t kidx) { char sbuf[24]; struct dyn_parent *p; @@ -1861,7 +1867,7 @@ dyn_install_state(const struct ipfw_flow_id *pkt, uint32_t zoneid, uint16_t fibnum, const void *ulp, int pktlen, struct ip_fw *rule, struct ipfw_dyn_info *info, uint32_t limit, uint16_t limit_mask, - uint16_t kidx, uint8_t type) + uint32_t kidx, uint8_t type) { struct ipfw_flow_id id; uint32_t hashval, parent_hashval, ruleid, rulenum; @@ -1998,12 +2004,16 @@ limit = 0; limit_mask = 0; } + /* + * NOTE: we assume that kidx field of struct ipfw_insn_kidx + * located in the same place as kidx field of ipfw_insn_limit. + */ return (dyn_install_state(&args->f_id, #ifdef INET6 IS_IP6_FLOW_ID(&args->f_id) ? dyn_getscopeid(args): #endif 0, M_GETFIB(args->m), ulp, pktlen, rule, info, limit, - limit_mask, cmd->o.arg1, cmd->o.opcode)); + limit_mask, cmd->kidx, cmd->o.opcode)); } /* @@ -2107,7 +2117,7 @@ * dynamic states. */ static int -dyn_match_range(uint16_t rulenum, uint8_t set, const ipfw_range_tlv *rt) +dyn_match_range(uint32_t rulenum, uint8_t set, const ipfw_range_tlv *rt) { MPASS(rt != NULL); @@ -2129,7 +2139,7 @@ static void dyn_acquire_rule(struct ip_fw_chain *ch, struct dyn_data *data, - struct ip_fw *rule, uint16_t kidx) + struct ip_fw *rule, uint32_t kidx) { struct dyn_state_obj *obj; @@ -2156,7 +2166,7 @@ static void dyn_release_rule(struct ip_fw_chain *ch, struct dyn_data *data, - struct ip_fw *rule, uint16_t kidx) + struct ip_fw *rule, uint32_t kidx) { struct dyn_state_obj *obj; @@ -2818,8 +2828,8 @@ * Pass through all states and reset eaction for orphaned rules. */ void -ipfw_dyn_reset_eaction(struct ip_fw_chain *ch, uint16_t eaction_id, - uint16_t default_id, uint16_t instance_id) +ipfw_dyn_reset_eaction(struct ip_fw_chain *ch, uint32_t eaction_id, + uint32_t default_id, uint32_t instance_id) { #ifdef INET6 struct dyn_ipv6_state *s6; @@ -2930,70 +2940,47 @@ } static void -dyn_export_parent(const struct dyn_parent *p, uint16_t kidx, uint8_t set, +dyn_export_parent(const struct dyn_parent *p, uint32_t kidx, uint8_t set, ipfw_dyn_rule *dst) { - dst->dyn_type = O_LIMIT_PARENT; + dst->type = O_LIMIT_PARENT; + dst->set = set; dst->kidx = kidx; - dst->count = (uint16_t)DPARENT_COUNT(p); + dst->rulenum = p->rulenum; + dst->count = DPARENT_COUNT(p); dst->expire = TIME_LEQ(p->expire, time_uptime) ? 0: p->expire - time_uptime; - - /* 'rule' is used to pass up the rule number and set */ - memcpy(&dst->rule, &p->rulenum, sizeof(p->rulenum)); - - /* store set number into high word of dst->rule pointer. */ - memcpy((char *)&dst->rule + sizeof(p->rulenum), &set, sizeof(set)); + dst->hashval = p->hashval; /* unused fields */ + dst->pad = 0; dst->pcnt = 0; dst->bcnt = 0; - dst->parent = NULL; - dst->state = 0; dst->ack_fwd = 0; dst->ack_rev = 0; - dst->bucket = p->hashval; - /* - * The legacy userland code will interpret a NULL here as a marker - * for the last dynamic rule. - */ - dst->next = (ipfw_dyn_rule *)1; } static void -dyn_export_data(const struct dyn_data *data, uint16_t kidx, uint8_t type, +dyn_export_data(const struct dyn_data *data, uint32_t kidx, uint8_t type, uint8_t set, ipfw_dyn_rule *dst) { - dst->dyn_type = type; + dst->type = type; + dst->set = set; dst->kidx = kidx; + dst->rulenum = data->rulenum; dst->pcnt = data->pcnt_fwd + data->pcnt_rev; dst->bcnt = data->bcnt_fwd + data->bcnt_rev; dst->expire = TIME_LEQ(data->expire, time_uptime) ? 0: data->expire - time_uptime; - - /* 'rule' is used to pass up the rule number and set */ - memcpy(&dst->rule, &data->rulenum, sizeof(data->rulenum)); - - /* store set number into high word of dst->rule pointer. */ - memcpy((char *)&dst->rule + sizeof(data->rulenum), &set, sizeof(set)); - dst->state = data->state; if (data->flags & DYN_REFERENCED) dst->state |= IPFW_DYN_ORPHANED; - /* unused fields */ - dst->parent = NULL; dst->ack_fwd = data->ack_fwd; dst->ack_rev = data->ack_rev; - dst->count = 0; - dst->bucket = data->hashval; - /* - * The legacy userland code will interpret a NULL here as a marker - * for the last dynamic rule. - */ - dst->next = (ipfw_dyn_rule *)1; + dst->hashval = data->hashval; } static void @@ -3121,52 +3108,6 @@ #undef DYN_EXPORT_STATES } -/* - * Fill given buffer with dynamic states (legacy format). - * IPFW_UH_RLOCK has to be held while calling. - */ -void -ipfw_get_dynamic(struct ip_fw_chain *chain, char **pbp, const char *ep) -{ -#ifdef INET6 - struct dyn_ipv6_state *s6; -#endif - struct dyn_ipv4_state *s4; - ipfw_dyn_rule *p, *last = NULL; - char *bp; - uint32_t bucket; - - if (V_dyn_count == 0) - return; - bp = *pbp; - - IPFW_UH_RLOCK_ASSERT(chain); - -#define DYN_EXPORT_STATES(s, af, head, b) \ - CK_SLIST_FOREACH(s, &V_dyn_ ## head[b], entry) { \ - if (bp + sizeof(*p) > ep) \ - break; \ - p = (ipfw_dyn_rule *)bp; \ - dyn_export_ ## af ## _state(s, p); \ - last = p; \ - bp += sizeof(*p); \ - } - - for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) { - DYN_EXPORT_STATES(s4, ipv4, ipv4_parent, bucket); - DYN_EXPORT_STATES(s4, ipv4, ipv4, bucket); -#ifdef INET6 - DYN_EXPORT_STATES(s6, ipv6, ipv6_parent, bucket); - DYN_EXPORT_STATES(s6, ipv6, ipv6, bucket); -#endif /* INET6 */ - } - - if (last != NULL) /* mark last dynamic rule */ - last->next = NULL; - *pbp = bp; -#undef DYN_EXPORT_STATES -} - void ipfw_dyn_init(struct ip_fw_chain *chain) { diff --git a/sys/netpfil/ipfw/ip_fw_eaction.c b/sys/netpfil/ipfw/ip_fw_eaction.c --- a/sys/netpfil/ipfw/ip_fw_eaction.c +++ b/sys/netpfil/ipfw/ip_fw_eaction.c @@ -68,7 +68,7 @@ * It is possible to pass some additional information to external * action handler using O_EXTERNAL_INSTANCE and O_EXTERNAL_DATA opcodes. * Such opcodes should be next after the O_EXTERNAL_ACTION opcode. - * For the O_EXTERNAL_INSTANCE opcode the cmd->arg1 contains index of named + * For the O_EXTERNAL_INSTANCE opcode the cmd->kidx contains index of named * object related to an instance of external action. * For the O_EXTERNAL_DATA opcode the cmd contains the data that can be used * by external action handler without needing to create named instance. @@ -76,7 +76,7 @@ * In case when eaction module uses named instances, it should register * opcode rewriting routines for O_EXTERNAL_INSTANCE opcode. The * classifier callback can look back into O_EXTERNAL_ACTION opcode (it - * must be in the (ipfw_insn *)(cmd - 1)). By arg1 from O_EXTERNAL_ACTION + * must be in the (ipfw_insn *)(cmd - 2)). By kidx from O_EXTERNAL_ACTION * it can deteremine eaction_id and compare it with its own. * The macro IPFW_TLV_EACTION_NAME(eaction_id) can be used to deteremine * the type of named_object related to external action instance. @@ -92,7 +92,7 @@ }; #define EACTION_OBJ(ch, cmd) \ - ((struct eaction_obj *)SRV_OBJECT((ch), (cmd)->arg1)) + ((struct eaction_obj *)SRV_OBJECT((ch), insntod((cmd), kidx)->kidx)) #if 0 #define EACTION_DEBUG(fmt, ...) do { \ @@ -116,21 +116,28 @@ * Opcode rewriting callbacks. */ static int -eaction_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +eaction_classify(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) { + ipfw_insn_kidx *cmd; - EACTION_DEBUG("opcode %d, arg1 %d", cmd->opcode, cmd->arg1); - *puidx = cmd->arg1; + if (F_LEN(cmd0) <= 1) + return (EINVAL); + + cmd = insntod(cmd0, kidx); + EACTION_DEBUG("opcode %u, kidx %u", cmd0->opcode, cmd->kidx); + *puidx = cmd->kidx; *ptype = 0; return (0); } static void -eaction_update(ipfw_insn *cmd, uint16_t idx) +eaction_update(ipfw_insn *cmd0, uint32_t idx) { + ipfw_insn_kidx *cmd; - cmd->arg1 = idx; - EACTION_DEBUG("opcode %d, arg1 -> %d", cmd->opcode, cmd->arg1); + cmd = insntod(cmd0, kidx); + cmd->kidx = idx; + EACTION_DEBUG("opcode %u, kidx -> %u", cmd0->opcode, cmd->kidx); } static int @@ -162,7 +169,7 @@ } static struct named_object * -eaction_findbykidx(struct ip_fw_chain *ch, uint16_t idx) +eaction_findbykidx(struct ip_fw_chain *ch, uint32_t idx) { EACTION_DEBUG("kidx %u", idx); @@ -182,7 +189,7 @@ static int create_eaction_obj(struct ip_fw_chain *ch, ipfw_eaction_t handler, - const char *name, uint16_t *eaction_id) + const char *name, uint32_t *eaction_id) { struct namedobj_instance *ni; struct eaction_obj *obj; @@ -249,8 +256,8 @@ * Resets all eaction opcodes to default handlers. */ static void -reset_eaction_rules(struct ip_fw_chain *ch, uint16_t eaction_id, - uint16_t instance_id, bool reset_rules) +reset_eaction_rules(struct ip_fw_chain *ch, uint32_t eaction_id, + uint32_t instance_id, bool reset_rules) { struct named_object *no; int i; @@ -332,11 +339,11 @@ * Registers external action handler to the global array. * On success it returns eaction id, otherwise - zero. */ -uint16_t +uint32_t ipfw_add_eaction(struct ip_fw_chain *ch, ipfw_eaction_t handler, const char *name) { - uint16_t eaction_id; + uint32_t eaction_id; eaction_id = 0; if (ipfw_check_object_name_generic(name) == 0) { @@ -351,7 +358,7 @@ * Deregisters external action handler with id eaction_id. */ int -ipfw_del_eaction(struct ip_fw_chain *ch, uint16_t eaction_id) +ipfw_del_eaction(struct ip_fw_chain *ch, uint32_t eaction_id) { struct named_object *no; @@ -371,7 +378,7 @@ int ipfw_reset_eaction(struct ip_fw_chain *ch, struct ip_fw *rule, - uint16_t eaction_id, uint16_t default_id, uint16_t instance_id) + uint32_t eaction_id, uint32_t default_id, uint32_t instance_id) { ipfw_insn *cmd, *icmd; int l; @@ -385,22 +392,23 @@ */ cmd = ipfw_get_action(rule); if (cmd->opcode != O_EXTERNAL_ACTION || - cmd->arg1 != eaction_id) + insntod(cmd, kidx)->kidx != eaction_id) return (0); /* * Check if there is O_EXTERNAL_INSTANCE opcode, we need * to truncate the rule length. * - * NOTE: F_LEN(cmd) must be 1 for O_EXTERNAL_ACTION opcode, + * NOTE: F_LEN(cmd) must be 2 for O_EXTERNAL_ACTION opcode, * and rule length should be enough to keep O_EXTERNAL_INSTANCE - * opcode, thus we do check for l > 1. + * opcode, thus we do check for l > 2. */ l = rule->cmd + rule->cmd_len - cmd; - if (l > 1) { - MPASS(F_LEN(cmd) == 1); - icmd = cmd + 1; + if (l > 2) { + MPASS(F_LEN(cmd) == 2); + icmd = cmd + F_LEN(cmd); if (icmd->opcode == O_EXTERNAL_INSTANCE && - instance_id != 0 && icmd->arg1 != instance_id) + instance_id != 0 && + insntod(icmd, kidx)->kidx != instance_id) return (0); /* * Since named_object related to this instance will be @@ -408,7 +416,7 @@ * the rest of cmd chain just after O_EXTERNAL_ACTION * opcode. */ - EACTION_DEBUG("truncate rule %d: len %u -> %u", + EACTION_DEBUG("truncate rule %u: len %u -> %u", rule->rulenum, rule->cmd_len, rule->cmd_len - F_LEN(icmd)); rule->cmd_len -= F_LEN(icmd); @@ -416,7 +424,7 @@ (uint32_t *)rule->cmd) == rule->cmd_len); } - cmd->arg1 = default_id; /* Set to default id */ + insntod(cmd, kidx)->kidx = default_id; /* Set to default id */ /* * Return 1 when reset successfully happened. */ @@ -429,8 +437,8 @@ * eaction has instance with id == kidx. */ int -ipfw_reset_eaction_instance(struct ip_fw_chain *ch, uint16_t eaction_id, - uint16_t kidx) +ipfw_reset_eaction_instance(struct ip_fw_chain *ch, uint32_t eaction_id, + uint32_t kidx) { struct named_object *no; @@ -448,5 +456,6 @@ ipfw_insn *cmd, int *done) { + MPASS(F_LEN(cmd) == 2); return (EACTION_OBJ(ch, cmd)->handler(ch, args, cmd, done)); } diff --git a/sys/netpfil/ipfw/ip_fw_iface.c b/sys/netpfil/ipfw/ip_fw_iface.c --- a/sys/netpfil/ipfw/ip_fw_iface.c +++ b/sys/netpfil/ipfw/ip_fw_iface.c @@ -69,7 +69,7 @@ struct sockopt_data *sd); static struct ipfw_sopt_handler scodes[] = { - { IP_FW_XIFLIST, 0, HDIR_GET, list_ifaces }, + { IP_FW_XIFLIST, IP_FW3_OPVER, HDIR_GET, list_ifaces }, }; /* @@ -231,7 +231,7 @@ { struct namedobj_instance *ii; - ii = ipfw_objhash_create(DEFAULT_IFACES); + ii = ipfw_objhash_create(DEFAULT_IFACES, DEFAULT_OBJHASH_SIZE); IPFW_UH_WLOCK(ch); if (ch->ifcfg == NULL) { ch->ifcfg = ii; @@ -485,7 +485,7 @@ /* * Lists all interface currently tracked by ipfw. - * Data layout (v0)(current): + * Data layout (v1)(current): * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size * Reply: [ ipfw_obj_lheader ipfw_iface_info x N ] * diff --git a/sys/netpfil/ipfw/ip_fw_log.c b/sys/netpfil/ipfw/ip_fw_log.c --- a/sys/netpfil/ipfw/ip_fw_log.c +++ b/sys/netpfil/ipfw/ip_fw_log.c @@ -46,9 +46,12 @@ #include #include /* for ETHERTYPE_IP */ #include +#include #include #include #include +#include +#include #include #include @@ -92,42 +95,43 @@ #endif /* !__APPLE__ */ #define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f) + +static void +ipfw_log_ipfw0(struct ip_fw_args *args, struct ip *ip) +{ + if (args->flags & IPFW_ARGS_LENMASK) + ipfw_bpf_tap(args->mem, IPFW_ARGS_LENGTH(args->flags)); + else if (args->flags & IPFW_ARGS_ETHER) + /* layer2, use orig hdr */ + ipfw_bpf_mtap(args->m); + else { + /* Add fake header. Later we will store + * more info in the header. + */ + if (ip->ip_v == 4) + ipfw_bpf_mtap2("DDDDDDSSSSSS\x08\x00", + ETHER_HDR_LEN, args->m); + else if (ip->ip_v == 6) + ipfw_bpf_mtap2("DDDDDDSSSSSS\x86\xdd", + ETHER_HDR_LEN, args->m); + else + /* Obviously bogus EtherType. */ + ipfw_bpf_mtap2("DDDDDDSSSSSS\xff\xff", + ETHER_HDR_LEN, args->m); + } +} + /* - * We enter here when we have a rule with O_LOG. * XXX this function alone takes about 2Kbytes of code! */ -void -ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, +static void +ipfw_log_syslog(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip) { char *action; int limit_reached = 0; char action2[92], proto[128], fragment[32], mark_str[24]; - if (V_fw_verbose == 0) { - if (args->flags & IPFW_ARGS_LENMASK) - ipfw_bpf_tap(args->mem, IPFW_ARGS_LENGTH(args->flags)); - else if (args->flags & IPFW_ARGS_ETHER) - /* layer2, use orig hdr */ - ipfw_bpf_mtap(args->m); - else { - /* Add fake header. Later we will store - * more info in the header. - */ - if (ip->ip_v == 4) - ipfw_bpf_mtap2("DDDDDDSSSSSS\x08\x00", - ETHER_HDR_LEN, args->m); - else if (ip->ip_v == 6) - ipfw_bpf_mtap2("DDDDDDSSSSSS\x86\xdd", - ETHER_HDR_LEN, args->m); - else - /* Obviously bogus EtherType. */ - ipfw_bpf_mtap2("DDDDDDSSSSSS\xff\xff", - ETHER_HDR_LEN, args->m); - } - return; - } - /* the old 'log' function */ fragment[0] = '\0'; proto[0] = '\0'; @@ -210,7 +214,7 @@ break; case O_SKIPTO: snprintf(SNPARGS(action2, 0), "SkipTo %d", - TARG(cmd->arg1, skipto)); + TARG(insntod(cmd, u32)->d[0], skipto)); break; case O_PIPE: snprintf(SNPARGS(action2, 0), "Pipe %d", @@ -269,23 +273,25 @@ break; case O_CALLRETURN: if (cmd->len & F_NOT) - action = "Return"; + snprintf(SNPARGS(action2, 0), "Return %s", + cmd->arg1 == RETURN_NEXT_RULENUM ? + "next-rulenum": "next-rule"); else snprintf(SNPARGS(action2, 0), "Call %d", - cmd->arg1); + TARG(insntod(cmd, u32)->d[0], skipto)); break; case O_SETMARK: if (cmd->arg1 == IP_FW_TARG) - snprintf(SNPARGS(action2, 0), "SetMark %#x", + snprintf(SNPARGS(action2, 0), "SetMark %#010x", TARG(cmd->arg1, mark)); else - snprintf(SNPARGS(action2, 0), "SetMark %#x", - ((ipfw_insn_u32 *)cmd)->d[0]); + snprintf(SNPARGS(action2, 0), "SetMark %#010x", + insntoc(cmd, u32)->d[0]); break; case O_EXTERNAL_ACTION: snprintf(SNPARGS(action2, 0), "Eaction %s", ((struct named_object *)SRV_OBJECT(chain, - cmd->arg1))->name); + insntod(cmd, kidx)->kidx))->name); break; default: action = "UNKNOWN"; @@ -438,4 +444,245 @@ "ipfw: limit %d reached on entry %d\n", limit_reached, f ? f->rulenum : -1); } + +static void +ipfw_rtsocklog_fill_l3(struct ip_fw_args *args, + char **buf, struct sockaddr **src, struct sockaddr **dst) +{ + struct sockaddr_in *v4src, *v4dst; +#ifdef INET6 + struct sockaddr_in6 *v6src, *v6dst; + + if (IS_IP6_FLOW_ID(&(args->f_id))) { + v6src = (struct sockaddr_in6 *)*buf; + *buf += sizeof(*v6src); + v6dst = (struct sockaddr_in6 *)*buf; + *buf += sizeof(*v6dst); + v6src->sin6_len = v6dst->sin6_len = sizeof(*v6src); + v6src->sin6_family = v6dst->sin6_family = AF_INET6; + v6src->sin6_addr = args->f_id.src_ip6; + v6dst->sin6_addr = args->f_id.dst_ip6; + + *src = (struct sockaddr *)v6src; + *dst = (struct sockaddr *)v6dst; + } else +#endif + { + v4src = (struct sockaddr_in *)*buf; + *buf += sizeof(*v4src); + v4dst = (struct sockaddr_in *)*buf; + *buf += sizeof(*v4dst); + v4src->sin_len = v4dst->sin_len = sizeof(*v4src); + v4src->sin_family = v4dst->sin_family = AF_INET; + v4src->sin_addr.s_addr = htonl(args->f_id.src_ip); + v4dst->sin_addr.s_addr = htonl(args->f_id.dst_ip); + + *src = (struct sockaddr *)v4src; + *dst = (struct sockaddr *)v4dst; + } +} + +static struct sockaddr * +ipfw_rtsocklog_handle_tablearg(struct ip_fw_chain *chain, ipfw_insn *cmd, + uint32_t tablearg, uint32_t *targ_value, char **buf) +{ + struct sockaddr_in *v4nh = NULL; + + /* handle tablearg now */ + switch (cmd->opcode) { + case O_DIVERT: + case O_TEE: + *targ_value = TARG(cmd->arg1, divert); + break; + case O_NETGRAPH: + case O_NGTEE: + *targ_value = TARG(cmd->arg1, netgraph); + break; + case O_SETDSCP: + *targ_value = (TARG(cmd->arg1, dscp) & 0x3F); + break; + case O_SETFIB: + *targ_value = (TARG(cmd->arg1, fib) & 0x7FFF); + break; + case O_SKIPTO: + case O_CALLRETURN: + if (cmd->opcode == O_CALLRETURN && (cmd->len & F_NOT)) + break; + *targ_value = (TARG(insntod(cmd, u32)->d[0], skipto)); + break; + case O_PIPE: + case O_QUEUE: + *targ_value = TARG(cmd->arg1, pipe); + break; + case O_MARK: + *targ_value = TARG(cmd->arg1, mark); + break; + case O_FORWARD_IP: + v4nh = (struct sockaddr_in *)buf; + buf += sizeof(*v4nh); + *v4nh = ((ipfw_insn_sa *)cmd)->sa; + if (v4nh->sin_addr.s_addr == INADDR_ANY) + v4nh->sin_addr.s_addr = htonl(tablearg); + + return (struct sockaddr *)v4nh; +#ifdef INET6 + case O_FORWARD_IP6: + return (struct sockaddr *)&(((ipfw_insn_sa6 *)cmd)->sa); +#endif + default: + break; + } + + return (NULL); +} + +#define MAX_COMMENT_LEN 80 + +static size_t +ipfw_copy_rule_comment(struct ip_fw *f, char *dst) +{ + ipfw_insn *cmd; + size_t rcomment_len = 0; + int l, cmdlen; + + for (l = f->cmd_len, cmd = f->cmd; l > 0; l -= cmdlen, cmd += cmdlen) { + cmdlen = F_LEN(cmd); + if (cmd->opcode != O_NOP) { + continue; + } else if (cmd->len == 1) { + return (0); + } + break; + } + if (l <= 0) { + return (0); + } + rcomment_len = strnlen((char *)(cmd + 1), MAX_COMMENT_LEN - 1) + 1; + strlcpy(dst, (char *)(cmd + 1), rcomment_len); + return (rcomment_len); +} + +static void +ipfw_log_rtsock(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, + struct ip_fw_args *args, u_short offset, uint32_t tablearg, + void *_eh) +{ + struct sockaddr_dl *sdl_ipfwcmd; + struct ether_header *eh = _eh; + struct rt_addrinfo *info; + uint32_t *targ_value; + ipfwlog_rtsock_hdr_v2 *hdr; + ipfw_insn *cmd; + ipfw_insn_log *l; + char *buf, *orig_buf; + /* at least 4 x sizeof(struct sockaddr_dl) + rule comment (80) */ + size_t buflen = 512; + + /* Should we log? O_LOG is the first one */ + cmd = ACTION_PTR(f); + l = (ipfw_insn_log *)cmd; + + if (l->max_log != 0 && l->log_left == 0) + return; + + l->log_left--; + if (V_fw_verbose != 0 && l->log_left == 0) { + log(LOG_SECURITY | LOG_NOTICE, + "ipfw: limit %d reached on entry %d\n", + l->max_log, f ? f->rulenum : -1); + } + + buf = orig_buf = malloc(buflen, M_TEMP, M_NOWAIT | M_ZERO); + if (buf == NULL) + return; + + info = (struct rt_addrinfo *)buf; + buf += sizeof (*info); + + cmd = ipfw_get_action(f); + sdl_ipfwcmd = (struct sockaddr_dl *)buf; + sdl_ipfwcmd->sdl_family = AF_IPFWLOG; + sdl_ipfwcmd->sdl_index = f->set; + sdl_ipfwcmd->sdl_type = 2; /* version */ + sdl_ipfwcmd->sdl_alen = sizeof(*hdr); + hdr = (ipfwlog_rtsock_hdr_v2 *)(sdl_ipfwcmd->sdl_data); + /* fill rule comment in if any */ + sdl_ipfwcmd->sdl_nlen = ipfw_copy_rule_comment(f, hdr->comment); + targ_value = &hdr->tablearg; + hdr->rulenum = f->rulenum; + hdr->mark = args->rule.pkt_mark; + hdr->cmd = *cmd; + + sdl_ipfwcmd->sdl_len = sizeof(*sdl_ipfwcmd); + if (sizeof(*hdr) + sdl_ipfwcmd->sdl_nlen > sizeof(sdl_ipfwcmd->sdl_data)) { + sdl_ipfwcmd->sdl_len += sizeof(*hdr) + sdl_ipfwcmd->sdl_nlen - + sizeof(sdl_ipfwcmd->sdl_data); + } + buf += sdl_ipfwcmd->sdl_len; + + /* fill L2 in if present */ + if (args->flags & IPFW_ARGS_ETHER && eh != NULL) { + sdl_ipfwcmd->sdl_slen = sizeof(eh->ether_shost); + memcpy(hdr->ether_shost, eh->ether_shost, + sdl_ipfwcmd->sdl_slen); + memcpy(hdr->ether_dhost, eh->ether_dhost, + sdl_ipfwcmd->sdl_slen); + } + + info->rti_info[RTAX_DST] = (struct sockaddr *)sdl_ipfwcmd; + + /* Warn if we're about to stop sending messages */ + if (l->max_log != 0 && l->log_left < (l->max_log >> 1)) { + info->rti_flags |= RTF_PROTO1; + } + + /* handle tablearg */ + info->rti_info[RTAX_GENMASK] = ipfw_rtsocklog_handle_tablearg( + chain, cmd, tablearg, targ_value, &buf); + + /* L3 */ + ipfw_rtsocklog_fill_l3(args, &buf, + &info->rti_info[RTAX_GATEWAY], + &info->rti_info[RTAX_NETMASK]); + + info->rti_ifp = args->ifp; + rtsock_routemsg_info(RTM_IPFWLOG, info, RT_ALL_FIBS); + + free(orig_buf, M_TEMP); +} + +/* + * We enter here when we have a rule with O_LOG. + */ +void +ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, + struct ip_fw_args *args, u_short offset, uint32_t tablearg, + struct ip *ip, void *eh) +{ + ipfw_insn *cmd; + + if (f == NULL || hlen == 0) + return; + + /* O_LOG is the first action */ + cmd = ACTION_PTR(f); + + if (cmd->arg1 == IPFW_LOG_DEFAULT) { + if (V_fw_verbose == 0) { + ipfw_log_ipfw0(args, ip); + return; + } + ipfw_log_syslog(chain, f, hlen, args, offset, tablearg, ip); + return; + } + + if (cmd->arg1 & IPFW_LOG_SYSLOG) + ipfw_log_syslog(chain, f, hlen, args, offset, tablearg, ip); + + if (cmd->arg1 & IPFW_LOG_RTSOCK) + ipfw_log_rtsock(chain, f, hlen, args, offset, tablearg, eh); + + if (cmd->arg1 & IPFW_LOG_IPFW0) + ipfw_log_ipfw0(args, ip); +} /* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -156,6 +156,7 @@ /* In ip_fw_log.c */ struct ip; +struct ip_fw; struct ip_fw_chain; void ipfw_bpf_init(int); @@ -164,7 +165,8 @@ void ipfw_bpf_mtap(struct mbuf *); void ipfw_bpf_mtap2(void *, u_int, struct mbuf *); void ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, - struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip); + struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip, + void *eh); VNET_DECLARE(u_int64_t, norule_counter); #define V_norule_counter VNET(norule_counter) VNET_DECLARE(int, verbose_limit); @@ -196,8 +198,8 @@ (p)->kidx = 0; \ } while (0) struct ipfw_dyn_info { - uint16_t direction; /* match direction */ - uint16_t kidx; /* state name kidx */ + uint32_t direction; /* match direction */ + uint32_t kidx; /* state name kidx */ uint32_t hashval; /* hash value */ uint32_t version; /* bucket version */ uint32_t f_pos; @@ -219,8 +221,8 @@ void ipfw_dyn_uninit(int); /* per-vnet deinitialization */ int ipfw_dyn_len(void); uint32_t ipfw_dyn_get_count(uint32_t *, int *); -void ipfw_dyn_reset_eaction(struct ip_fw_chain *ch, uint16_t eaction_id, - uint16_t default_id, uint16_t instance_id); +void ipfw_dyn_reset_eaction(struct ip_fw_chain *ch, uint32_t eaction_id, + uint32_t default_id, uint32_t instance_id); /* common variables */ VNET_DECLARE(int, fw_one_pass); @@ -235,6 +237,9 @@ VNET_DECLARE(int, ipfw_vnet_ready); #define V_ipfw_vnet_ready VNET(ipfw_vnet_ready) +VNET_DECLARE(int, skipto_cache); +#define V_skipto_cache VNET(skipto_cache) + VNET_DECLARE(u_int32_t, set_disable); #define V_set_disable VNET(set_disable) @@ -276,9 +281,10 @@ struct ip_fw { uint16_t act_ofs; /* offset of action in 32-bit units */ uint16_t cmd_len; /* # of 32-bit words in cmd */ - uint16_t rulenum; /* rule number */ + uint32_t rulenum; /* rule number */ uint8_t set; /* rule set (0..31) */ uint8_t flags; /* currently unused */ + uint16_t _pad; counter_u64_t cntr; /* Pointer to rule counters */ struct ip_fw_jump_cache cache; /* used by jump_fast */ uint32_t timestamp; /* tv_sec of last match */ @@ -306,7 +312,6 @@ #else struct rmlock rwmtx; #endif - int static_len; /* total len of static rules (v0) */ uint32_t gencnt; /* NAT generation count */ LIST_HEAD(nat_list, cfg_nat) nat; /* list of nat entries */ struct ip_fw *default_rule; @@ -324,9 +329,9 @@ /* 64-byte structure representing multi-field table value */ struct table_value { uint32_t tag; /* O_TAG/O_TAGGED */ - uint32_t pipe; /* O_PIPE/O_QUEUE */ + uint16_t pipe; /* O_PIPE/O_QUEUE */ uint16_t divert; /* O_DIVERT/O_TEE */ - uint16_t skipto; /* skipto, CALLRET */ + uint32_t skipto; /* skipto, CALLRET */ uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */ uint16_t fib; /* O_SETFIB */ uint16_t nat; /* O_NAT */ @@ -349,8 +354,7 @@ uint16_t etlv; /* Export TLV id */ uint8_t subtype;/* object subtype within class */ uint8_t set; /* set object belongs to */ - uint16_t kidx; /* object kernel index */ - uint16_t spare; + uint32_t kidx; /* object kernel index */ uint32_t ocnt; /* object counter for internal use */ uint32_t refcnt; /* number of references */ }; @@ -476,8 +480,8 @@ #define IPFW_UH_WUNLOCK(p) rw_wunlock(&(p)->uh_lock) struct obj_idx { - uint16_t uidx; /* internal index supplied by userland */ - uint16_t kidx; /* kernel object index */ + uint32_t uidx; /* internal index supplied by userland */ + uint32_t kidx; /* kernel object index */ uint16_t off; /* tlv offset from rule end in 4-byte words */ uint8_t spare; uint8_t type; /* object type within its category */ @@ -495,36 +499,6 @@ struct obj_idx obuf[8]; /* table references storage */ }; -/* Legacy interface support */ -/* - * FreeBSD 8 export rule format - */ -struct ip_fw_rule0 { - struct ip_fw *x_next; /* linked list of rules */ - struct ip_fw *next_rule; /* ptr to next [skipto] rule */ - /* 'next_rule' is used to pass up 'set_disable' status */ - - uint16_t act_ofs; /* offset of action in 32-bit units */ - uint16_t cmd_len; /* # of 32-bit words in cmd */ - uint16_t rulenum; /* rule number */ - uint8_t set; /* rule set (0..31) */ - uint8_t _pad; /* padding */ - uint32_t id; /* rule id */ - - /* These fields are present in all rules. */ - uint64_t pcnt; /* Packet counter */ - uint64_t bcnt; /* Byte counter */ - uint32_t timestamp; /* tv_sec of last match */ - - ipfw_insn cmd[1]; /* storage for commands */ -}; - -struct ip_fw_bcounter0 { - uint64_t pcnt; /* Packet counter */ - uint64_t bcnt; /* Byte counter */ - uint32_t timestamp; /* tv_sec of last match */ -}; - /* Kernel rule length */ /* * RULE _K_ SIZE _V_ -> @@ -534,9 +508,6 @@ * RULESIZE _V_ -> * get user size rule length */ -/* FreeBSD8 <> current kernel format */ -#define RULEUSIZE0(r) (sizeof(struct ip_fw_rule0) + (r)->cmd_len * 4 - 4) -#define RULEKSIZE0(r) roundup2((sizeof(struct ip_fw) + (r)->cmd_len*4 - 4), 8) /* FreeBSD11 <> current kernel format */ #define RULEUSIZE1(r) (roundup2(sizeof(struct ip_fw_rule) + \ (r)->cmd_len * 4 - 4, 8)) @@ -550,17 +521,17 @@ #define IPFW_TABLES_MAX 65536 #define IPFW_TABLES_DEFAULT 128 #define IPFW_OBJECTS_MAX 65536 -#define IPFW_OBJECTS_DEFAULT 1024 +#define IPFW_OBJECTS_DEFAULT 4096 #define CHAIN_TO_SRV(ch) ((ch)->srvmap) #define SRV_OBJECT(ch, idx) ((ch)->srvstate[(idx)]) struct tid_info { uint32_t set; /* table set */ - uint16_t uidx; /* table index */ + uint32_t uidx; /* table index */ uint8_t type; /* table type */ uint8_t atype; - uint8_t spare; + uint16_t spare; int tlen; /* Total TLV size block */ void *tlvs; /* Pointer to first TLV */ }; @@ -570,11 +541,11 @@ * If true, returns its index and type. * Returns 0 if match is found, 1 overwise. */ -typedef int (ipfw_obj_rw_cl)(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype); +typedef int (ipfw_obj_rw_cl)(ipfw_insn *cmd, uint32_t *puidx, uint8_t *ptype); /* * Updater callback. Sets kernel object reference index to @puidx */ -typedef void (ipfw_obj_rw_upd)(ipfw_insn *cmd, uint16_t puidx); +typedef void (ipfw_obj_rw_upd)(ipfw_insn *cmd, uint32_t puidx); /* * Finder callback. Tries to find named object by name (specified via @ti). * Stores found named object pointer in @pno. @@ -590,7 +561,7 @@ * Returns pointer to named object or NULL. */ typedef struct named_object *(ipfw_obj_fidx_cb)(struct ip_fw_chain *ch, - uint16_t kidx); + uint32_t kidx); /* * Object creator callback. Tries to create object specified by @ti. * Stores newly-allocated object index in @pkidx. @@ -598,7 +569,7 @@ * Returns 0 on success. */ typedef int (ipfw_obj_create_cb)(struct ip_fw_chain *ch, struct tid_info *ti, - uint16_t *pkidx); + uint32_t *pkidx); /* * Object destroy callback. Intended to free resources allocated by * create_object callback. @@ -618,7 +589,7 @@ SWAP_ALL = 0, TEST_ALL, MOVE_ALL, COUNT_ONE, TEST_ONE, MOVE_ONE }; typedef int (ipfw_obj_sets_cb)(struct ip_fw_chain *ch, - uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd); + uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd); struct opcode_obj_rewrite { uint32_t opcode; /* Opcode to act upon */ @@ -654,8 +625,23 @@ void ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic); /* In ip_fw_sockopt.c */ +enum ipfw_opcheck_result { + SUCCESS = 0, + FAILED, + BAD_SIZE, + CHECK_ACTION, +}; +typedef enum ipfw_opcheck_result (*ipfw_check_opcode_t)(ipfw_insn **, + int *, struct rule_check_info *); + +void ipfw_register_compat(ipfw_check_opcode_t); +void ipfw_unregister_compat(void); + +enum ipfw_opcheck_result ipfw_check_opcode(ipfw_insn **, int *, + struct rule_check_info *); void ipfw_init_skipto_cache(struct ip_fw_chain *chain); void ipfw_destroy_skipto_cache(struct ip_fw_chain *chain); +void ipfw_enable_skipto_cache(struct ip_fw_chain *chain); int ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id); int ipfw_ctl3(struct sockopt *sopt); int ipfw_add_protected_rule(struct ip_fw_chain *chain, struct ip_fw *rule, @@ -665,11 +651,15 @@ void ipfw_reap_rules(struct ip_fw *head); void ipfw_init_counters(void); void ipfw_destroy_counters(void); +int ipfw_commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, + int count); struct ip_fw *ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize); void ipfw_free_rule(struct ip_fw *rule); int ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt); -int ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint16_t kidx); +int ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint32_t kidx); ipfw_insn *ipfw_get_action(struct ip_fw *); +int ipfw_check_rule(struct ip_fw_rule *rule, size_t size, + struct rule_check_info *ci); typedef int (sopt_handler_f)(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd); @@ -701,6 +691,7 @@ sizeof(c) / sizeof(c[0])); \ } while(0) +#define DEFAULT_OBJHASH_SIZE 32 struct namedobj_instance; typedef int (objhash_cb_t)(struct namedobj_instance *ni, struct named_object *, void *arg); @@ -708,7 +699,7 @@ uint32_t kopt); typedef int (objhash_cmp_f)(struct named_object *no, const void *key, uint32_t kopt); -struct namedobj_instance *ipfw_objhash_create(uint32_t items); +struct namedobj_instance *ipfw_objhash_create(uint32_t items, size_t hash_size); void ipfw_objhash_destroy(struct namedobj_instance *); void ipfw_objhash_bitmap_alloc(uint32_t items, void **idx, int *pblocks); void ipfw_objhash_bitmap_merge(struct namedobj_instance *ni, @@ -722,7 +713,7 @@ struct named_object *ipfw_objhash_lookup_name_type(struct namedobj_instance *ni, uint32_t set, uint32_t type, const char *name); struct named_object *ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, - uint16_t idx); + uint32_t idx); int ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a, struct named_object *b); void ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no); @@ -733,14 +724,14 @@ void *arg); int ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f, void *arg, uint16_t type); -int ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx); -int ipfw_objhash_alloc_idx(void *n, uint16_t *pidx); +int ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t idx); +int ipfw_objhash_alloc_idx(void *n, uint32_t *pidx); void ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f, objhash_cmp_f *cmp_f); int ipfw_objhash_find_type(struct namedobj_instance *ni, struct tid_info *ti, uint32_t etlv, struct named_object **pno); void ipfw_export_obj_ntlv(struct named_object *no, ipfw_obj_ntlv *ntlv); -ipfw_obj_ntlv *ipfw_find_name_tlv_type(void *tlvs, int len, uint16_t uidx, +ipfw_obj_ntlv *ipfw_find_name_tlv_type(void *tlvs, int len, uint32_t uidx, uint32_t etlv); void ipfw_init_obj_rewriter(void); void ipfw_destroy_obj_rewriter(void); @@ -749,13 +740,13 @@ int create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti); -void update_opcode_kidx(ipfw_insn *cmd, uint16_t idx); -int classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx); +void update_opcode_kidx(ipfw_insn *cmd, uint32_t idx); +int classify_opcode_kidx(ipfw_insn *cmd, uint32_t *puidx); void ipfw_init_srv(struct ip_fw_chain *ch); void ipfw_destroy_srv(struct ip_fw_chain *ch); int ipfw_check_object_name_generic(const char *name); int ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type, - uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd); + uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd); /* In ip_fw_eaction.c */ typedef int (ipfw_eaction_t)(struct ip_fw_chain *ch, struct ip_fw_args *args, @@ -763,15 +754,15 @@ int ipfw_eaction_init(struct ip_fw_chain *ch, int first); void ipfw_eaction_uninit(struct ip_fw_chain *ch, int last); -uint16_t ipfw_add_eaction(struct ip_fw_chain *ch, ipfw_eaction_t handler, +uint32_t ipfw_add_eaction(struct ip_fw_chain *ch, ipfw_eaction_t handler, const char *name); -int ipfw_del_eaction(struct ip_fw_chain *ch, uint16_t eaction_id); +int ipfw_del_eaction(struct ip_fw_chain *ch, uint32_t eaction_id); int ipfw_run_eaction(struct ip_fw_chain *ch, struct ip_fw_args *args, ipfw_insn *cmd, int *done); int ipfw_reset_eaction(struct ip_fw_chain *ch, struct ip_fw *rule, - uint16_t eaction_id, uint16_t default_id, uint16_t instance_id); -int ipfw_reset_eaction_instance(struct ip_fw_chain *ch, uint16_t eaction_id, - uint16_t instance_id); + uint32_t eaction_id, uint32_t default_id, uint32_t instance_id); +int ipfw_reset_eaction_instance(struct ip_fw_chain *ch, uint32_t eaction_id, + uint32_t instance_id); /* In ip_fw_table.c */ struct table_info; @@ -779,12 +770,12 @@ typedef int (table_lookup_t)(struct table_info *ti, void *key, uint32_t keylen, uint32_t *val); -int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, +int ipfw_lookup_table(struct ip_fw_chain *ch, uint32_t tbl, uint16_t plen, void *paddr, uint32_t *val); struct named_object *ipfw_objhash_lookup_table_kidx(struct ip_fw_chain *ch, - uint16_t kidx); -int ipfw_ref_table(struct ip_fw_chain *ch, ipfw_obj_ntlv *ntlv, uint16_t *kidx); -void ipfw_unref_table(struct ip_fw_chain *ch, uint16_t kidx); + uint32_t kidx); +int ipfw_ref_table(struct ip_fw_chain *ch, ipfw_obj_ntlv *ntlv, uint32_t *kidx); +void ipfw_unref_table(struct ip_fw_chain *ch, uint32_t kidx); int ipfw_init_tables(struct ip_fw_chain *ch, int first); int ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables); int ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int nsets); diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -2,7 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa - * Copyright (c) 2014 Yandex LLC + * Copyright (c) 2014-2024 Yandex LLC * Copyright (c) 2014 Alexander V. Chernikov * * Supported by: Valeria Paoli @@ -74,18 +74,22 @@ #include #endif -static int ipfw_ctl(struct sockopt *sopt); +static enum ipfw_opcheck_result +check_opcode_compat_nop(ipfw_insn **pcmd, int *plen, + struct rule_check_info *ci) +{ + + /* Compatibility code is not registered */ + return (FAILED); +} + +static ipfw_check_opcode_t check_opcode_f = check_opcode_compat_nop; + static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci); -static int check_ipfw_rule1(struct ip_fw_rule *rule, int size, - struct rule_check_info *ci); -static int check_ipfw_rule0(struct ip_fw_rule0 *rule, int size, - struct rule_check_info *ci); static int rewrite_rule_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci); -#define NAMEDOBJ_HASH_SIZE 32 - struct namedobj_instance { struct namedobjects_head *names; struct namedobjects_head *values; @@ -108,25 +112,8 @@ MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); -static int dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3, - struct sockopt_data *sd); -static int add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, - struct sockopt_data *sd); -static int del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, - struct sockopt_data *sd); -static int clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, - struct sockopt_data *sd); -static int move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, - struct sockopt_data *sd); -static int manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3, - struct sockopt_data *sd); -static int dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3, - struct sockopt_data *sd); -static int dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3, - struct sockopt_data *sd); - /* ctl3 handler data */ -struct mtx ctl3_lock; +static struct mtx ctl3_lock; #define CTL3_LOCK_INIT() mtx_init(&ctl3_lock, "ctl3_lock", NULL, MTX_DEF) #define CTL3_LOCK_DESTROY() mtx_destroy(&ctl3_lock) #define CTL3_LOCK() mtx_lock(&ctl3_lock) @@ -136,28 +123,31 @@ static size_t ctl3_hsize; static uint64_t ctl3_refct, ctl3_gencnt; #define CTL3_SMALLBUF 4096 /* small page-size write buffer */ -#define CTL3_LARGEBUF 16 * 1024 * 1024 /* handle large rulesets */ +#define CTL3_LARGEBUF (16 * 1024 * 1024) /* handle large rulesets */ static int ipfw_flush_sopt_data(struct sockopt_data *sd); -static struct ipfw_sopt_handler scodes[] = { - { IP_FW_XGET, 0, HDIR_GET, dump_config }, - { IP_FW_XADD, 0, HDIR_BOTH, add_rules }, - { IP_FW_XDEL, 0, HDIR_BOTH, del_rules }, - { IP_FW_XZERO, 0, HDIR_SET, clear_rules }, - { IP_FW_XRESETLOG, 0, HDIR_SET, clear_rules }, - { IP_FW_XMOVE, 0, HDIR_SET, move_rules }, - { IP_FW_SET_SWAP, 0, HDIR_SET, manage_sets }, - { IP_FW_SET_MOVE, 0, HDIR_SET, manage_sets }, - { IP_FW_SET_ENABLE, 0, HDIR_SET, manage_sets }, - { IP_FW_DUMP_SOPTCODES, 0, HDIR_GET, dump_soptcodes }, - { IP_FW_DUMP_SRVOBJECTS,0, HDIR_GET, dump_srvobjects }, +static sopt_handler_f dump_config, add_rules, del_rules, clear_rules, + move_rules, manage_sets, dump_soptcodes, dump_srvobjects, + manage_skiptocache; + +static struct ipfw_sopt_handler scodes[] = { + { IP_FW_XGET, IP_FW3_OPVER, HDIR_GET, dump_config }, + { IP_FW_XADD, IP_FW3_OPVER, HDIR_BOTH, add_rules }, + { IP_FW_XDEL, IP_FW3_OPVER, HDIR_BOTH, del_rules }, + { IP_FW_XZERO, IP_FW3_OPVER, HDIR_SET, clear_rules }, + { IP_FW_XRESETLOG, IP_FW3_OPVER, HDIR_SET, clear_rules }, + { IP_FW_XMOVE, IP_FW3_OPVER, HDIR_SET, move_rules }, + { IP_FW_SET_SWAP, IP_FW3_OPVER, HDIR_SET, manage_sets }, + { IP_FW_SET_MOVE, IP_FW3_OPVER, HDIR_SET, manage_sets }, + { IP_FW_SET_ENABLE, IP_FW3_OPVER, HDIR_SET, manage_sets }, + { IP_FW_DUMP_SOPTCODES, IP_FW3_OPVER, HDIR_GET, dump_soptcodes }, + { IP_FW_DUMP_SRVOBJECTS, IP_FW3_OPVER, HDIR_GET, dump_srvobjects }, + { IP_FW_SKIPTO_CACHE, IP_FW3_OPVER, HDIR_BOTH, manage_skiptocache }, }; -static int -set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule); static struct opcode_obj_rewrite *find_op_rw(ipfw_insn *cmd, - uint16_t *puidx, uint8_t *ptype); + uint32_t *puidx, uint8_t *ptype); static int ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule, struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti); static int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, @@ -165,7 +155,7 @@ static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule); static void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib, struct obj_idx *end); -static int export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx, +static int export_objhash_ntlv(struct namedobj_instance *ni, uint32_t kidx, struct sockopt_data *sd); /* @@ -256,7 +246,7 @@ static void update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map) { - int *smap, rulenum; + uint32_t *smap, rulenum; int i, mi; IPFW_UH_WLOCK_ASSERT(chain); @@ -268,10 +258,10 @@ if (smap == NULL) return; - for (i = 0; i < 65536; i++) { + for (i = 0; i <= IPFW_DEFAULT_RULE; i++) { smap[i] = mi; /* Use the same rule index until i < rulenum */ - if (i != rulenum || i == 65535) + if (i != rulenum || i == IPFW_DEFAULT_RULE) continue; /* Find next rule with num > i */ rulenum = map[++mi]->rulenum; @@ -286,7 +276,7 @@ static void swap_skipto_cache(struct ip_fw_chain *chain) { - int *map; + uint32_t *map; IPFW_UH_WLOCK_ASSERT(chain); IPFW_WLOCK_ASSERT(chain); @@ -302,10 +292,12 @@ void ipfw_init_skipto_cache(struct ip_fw_chain *chain) { - int *idxmap, *idxmap_back; + uint32_t *idxmap, *idxmap_back; - idxmap = malloc(65536 * sizeof(int), M_IPFW, M_WAITOK | M_ZERO); - idxmap_back = malloc(65536 * sizeof(int), M_IPFW, M_WAITOK); + idxmap = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t), + M_IPFW, M_WAITOK | M_ZERO); + idxmap_back = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t), + M_IPFW, M_WAITOK | M_ZERO); /* * Note we may be called at any time after initialization, @@ -323,7 +315,8 @@ /* Set backup pointer first to permit building cache */ chain->idxmap_back = idxmap_back; - update_skipto_cache(chain, chain->map); + if (V_skipto_cache != 0) + update_skipto_cache(chain, chain->map); IPFW_WLOCK(chain); /* It is now safe to set chain->idxmap ptr */ chain->idxmap = idxmap; @@ -338,11 +331,8 @@ void ipfw_destroy_skipto_cache(struct ip_fw_chain *chain) { - - if (chain->idxmap != NULL) - free(chain->idxmap, M_IPFW); - if (chain->idxmap != NULL) - free(chain->idxmap_back, M_IPFW); + free(chain->idxmap, M_IPFW); + free(chain->idxmap_back, M_IPFW); } /* @@ -412,50 +402,6 @@ } } -static void -export_cntr0_base(struct ip_fw *krule, struct ip_fw_bcounter0 *cntr) -{ - struct timeval boottime; - - if (krule->cntr != NULL) { - cntr->pcnt = counter_u64_fetch(krule->cntr); - cntr->bcnt = counter_u64_fetch(krule->cntr + 1); - cntr->timestamp = krule->timestamp; - } - if (cntr->timestamp > 0) { - getboottime(&boottime); - cntr->timestamp += boottime.tv_sec; - } -} - -/* - * Copies rule @urule from v1 userland format (current). - * to kernel @krule. - * Assume @krule is zeroed. - */ -static void -import_rule1(struct rule_check_info *ci) -{ - struct ip_fw_rule *urule; - struct ip_fw *krule; - - urule = (struct ip_fw_rule *)ci->urule; - krule = (struct ip_fw *)ci->krule; - - /* copy header */ - krule->act_ofs = urule->act_ofs; - krule->cmd_len = urule->cmd_len; - krule->rulenum = urule->rulenum; - krule->set = urule->set; - krule->flags = urule->flags; - - /* Save rulenum offset */ - ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum); - - /* Copy opcodes */ - memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); -} - /* * Export rule into v1 format (Current). * Layout: @@ -497,196 +443,17 @@ memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t)); } -/* - * Copies rule @urule from FreeBSD8 userland format (v0) - * to kernel @krule. - * Assume @krule is zeroed. - */ -static void -import_rule0(struct rule_check_info *ci) -{ - struct ip_fw_rule0 *urule; - struct ip_fw *krule; - int cmdlen, l; - ipfw_insn *cmd; - ipfw_insn_limit *lcmd; - ipfw_insn_if *cmdif; - - urule = (struct ip_fw_rule0 *)ci->urule; - krule = (struct ip_fw *)ci->krule; - - /* copy header */ - krule->act_ofs = urule->act_ofs; - krule->cmd_len = urule->cmd_len; - krule->rulenum = urule->rulenum; - krule->set = urule->set; - if ((urule->_pad & 1) != 0) - krule->flags |= IPFW_RULE_NOOPT; - - /* Save rulenum offset */ - ci->urule_numoff = offsetof(struct ip_fw_rule0, rulenum); - - /* Copy opcodes */ - memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); - - /* - * Alter opcodes: - * 1) convert tablearg value from 65535 to 0 - * 2) Add high bit to O_SETFIB/O_SETDSCP values (to make room - * for targ). - * 3) convert table number in iface opcodes to u16 - * 4) convert old `nat global` into new 65535 - */ - l = krule->cmd_len; - cmd = krule->cmd; - cmdlen = 0; - - for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { - cmdlen = F_LEN(cmd); - - switch (cmd->opcode) { - /* Opcodes supporting tablearg */ - case O_TAG: - case O_TAGGED: - case O_PIPE: - case O_QUEUE: - case O_DIVERT: - case O_TEE: - case O_SKIPTO: - case O_CALLRETURN: - case O_NETGRAPH: - case O_NGTEE: - case O_NAT: - if (cmd->arg1 == IP_FW_TABLEARG) - cmd->arg1 = IP_FW_TARG; - else if (cmd->arg1 == 0) - cmd->arg1 = IP_FW_NAT44_GLOBAL; - break; - case O_SETFIB: - case O_SETDSCP: - case O_SETMARK: - case O_MARK: - if (cmd->arg1 == IP_FW_TABLEARG) - cmd->arg1 = IP_FW_TARG; - else - cmd->arg1 |= 0x8000; - break; - case O_LIMIT: - lcmd = (ipfw_insn_limit *)cmd; - if (lcmd->conn_limit == IP_FW_TABLEARG) - lcmd->conn_limit = IP_FW_TARG; - break; - /* Interface tables */ - case O_XMIT: - case O_RECV: - case O_VIA: - /* Interface table, possibly */ - cmdif = (ipfw_insn_if *)cmd; - if (cmdif->name[0] != '\1') - break; - - cmdif->p.kidx = (uint16_t)cmdif->p.glob; - break; - } - } -} - -/* - * Copies rule @krule from kernel to FreeBSD8 userland format (v0) - */ -static void -export_rule0(struct ip_fw *krule, struct ip_fw_rule0 *urule, int len) -{ - int cmdlen, l; - ipfw_insn *cmd; - ipfw_insn_limit *lcmd; - ipfw_insn_if *cmdif; - - /* copy header */ - memset(urule, 0, len); - urule->act_ofs = krule->act_ofs; - urule->cmd_len = krule->cmd_len; - urule->rulenum = krule->rulenum; - urule->set = krule->set; - if ((krule->flags & IPFW_RULE_NOOPT) != 0) - urule->_pad |= 1; - - /* Copy opcodes */ - memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t)); - - /* Export counters */ - export_cntr0_base(krule, (struct ip_fw_bcounter0 *)&urule->pcnt); - - /* - * Alter opcodes: - * 1) convert tablearg value from 0 to 65535 - * 2) Remove highest bit from O_SETFIB/O_SETDSCP values. - * 3) convert table number in iface opcodes to int - */ - l = urule->cmd_len; - cmd = urule->cmd; - cmdlen = 0; - - for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { - cmdlen = F_LEN(cmd); - - switch (cmd->opcode) { - /* Opcodes supporting tablearg */ - case O_TAG: - case O_TAGGED: - case O_PIPE: - case O_QUEUE: - case O_DIVERT: - case O_TEE: - case O_SKIPTO: - case O_CALLRETURN: - case O_NETGRAPH: - case O_NGTEE: - case O_NAT: - if (cmd->arg1 == IP_FW_TARG) - cmd->arg1 = IP_FW_TABLEARG; - else if (cmd->arg1 == IP_FW_NAT44_GLOBAL) - cmd->arg1 = 0; - break; - case O_SETFIB: - case O_SETDSCP: - case O_SETMARK: - case O_MARK: - if (cmd->arg1 == IP_FW_TARG) - cmd->arg1 = IP_FW_TABLEARG; - else - cmd->arg1 &= ~0x8000; - break; - case O_LIMIT: - lcmd = (ipfw_insn_limit *)cmd; - if (lcmd->conn_limit == IP_FW_TARG) - lcmd->conn_limit = IP_FW_TABLEARG; - break; - /* Interface tables */ - case O_XMIT: - case O_RECV: - case O_VIA: - /* Interface table, possibly */ - cmdif = (ipfw_insn_if *)cmd; - if (cmdif->name[0] != '\1') - break; - - cmdif->p.glob = cmdif->p.kidx; - break; - } - } -} - /* * Add new rule(s) to the list possibly creating rule number for each. * Update the rule_number in the input struct so the caller knows it as well. * Must be called without IPFW_UH held */ -static int -commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count) +int +ipfw_commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, + int count) { - int error, i, insert_before, tcount; - uint16_t rulenum, *pnum; + int error, i, insert_before, tcount, rule_idx, last_rule_idx; + uint32_t rulenum; struct rule_check_info *ci; struct ip_fw *krule; struct ip_fw **map; /* the new array of pointers */ @@ -704,12 +471,14 @@ */ error = rewrite_rule_uidx(chain, ci); if (error != 0) { + /* * rewrite failed, state for current rule * has been reverted. Check if we need to * revert more. */ if (tcount > 0) { + /* * We have some more table rules * we need to rollback. @@ -721,8 +490,10 @@ if (ci->object_opcodes == 0) continue; unref_rule_objects(chain,ci->krule); + } IPFW_UH_WUNLOCK(chain); + } return (error); @@ -754,36 +525,41 @@ else if (V_autoinc_step > 1000) V_autoinc_step = 1000; - /* FIXME: Handle count > 1 */ - ci = rci; - krule = ci->krule; - rulenum = krule->rulenum; - - /* find the insertion point, we will insert before */ - insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE; - i = ipfw_find_rule(chain, insert_before, 0); - /* duplicate first part */ - if (i > 0) - bcopy(chain->map, map, i * sizeof(struct ip_fw *)); - map[i] = krule; - /* duplicate remaining part, we always have the default rule */ - bcopy(chain->map + i, map + i + 1, - sizeof(struct ip_fw *) *(chain->n_rules - i)); - if (rulenum == 0) { - /* Compute rule number and write it back */ - rulenum = i > 0 ? map[i-1]->rulenum : 0; - if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) - rulenum += V_autoinc_step; - krule->rulenum = rulenum; - /* Save number to userland rule */ - pnum = (uint16_t *)((caddr_t)ci->urule + ci->urule_numoff); - *pnum = rulenum; + last_rule_idx = 0; + for (ci = rci, i = 0; i < count; ci++, i++) { + krule = ci->krule; + rulenum = krule->rulenum; + + krule->id = chain->id + 1; + + /* find the insertion point, we will insert before */ + insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE; + rule_idx = ipfw_find_rule(chain, insert_before, 0); + /* duplicate the previous part */ + if (last_rule_idx < rule_idx) + bcopy(chain->map + last_rule_idx, map + last_rule_idx + i, + (rule_idx - last_rule_idx) * sizeof(struct ip_fw *)); + last_rule_idx = rule_idx; + map[rule_idx + i] = krule; + if (rulenum == 0) { + /* Compute rule number and write it back */ + rulenum = rule_idx + i > 0 ? map[rule_idx + i - 1]->rulenum : 0; + if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) + rulenum += V_autoinc_step; + krule->rulenum = rulenum; + /* Save number to userland rule */ + memcpy((char *)ci->urule + ci->urule_numoff, &rulenum, + sizeof(rulenum)); + } } - krule->id = chain->id + 1; - update_skipto_cache(chain, map); - map = swap_map(chain, map, chain->n_rules + 1); - chain->static_len += RULEUSIZE0(krule); + /* duplicate the remaining part, we always have the default rule */ + bcopy(chain->map + last_rule_idx, map + last_rule_idx + count, + (chain->n_rules - last_rule_idx) * sizeof(struct ip_fw *)); + + if (V_skipto_cache != 0) + update_skipto_cache(chain, map); + map = swap_map(chain, map, chain->n_rules + count); IPFW_UH_WUNLOCK(chain); if (map) free(map, M_IPFW); @@ -808,7 +584,6 @@ rule->id = chain->id + 1; /* We add rule in the end of chain, no need to update skipto cache */ map = swap_map(chain, map, chain->n_rules + 1); - chain->static_len += RULEUSIZE0(rule); IPFW_UH_WUNLOCK(chain); free(map, M_IPFW); return (0); @@ -889,7 +664,7 @@ } struct manage_sets_args { - uint16_t set; + uint32_t set; uint8_t new_set; }; @@ -939,7 +714,7 @@ */ int ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type, - uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) + uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) { struct manage_sets_args args; struct named_object *no; @@ -1005,7 +780,7 @@ delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel) { struct ip_fw *reap, *rule, **map; - int end, start; + uint32_t end, start; int i, n, ndyn, ofs; reap = NULL; @@ -1076,7 +851,6 @@ rule = map[i]; if (ipfw_match_range(rule, rt) == 0) continue; - chain->static_len -= RULEUSIZE0(rule); ipfw_reap_add(chain, &reap, rule); } IPFW_UH_WUNLOCK(chain); @@ -1094,8 +868,8 @@ struct opcode_obj_rewrite *rw; struct ip_fw *rule; ipfw_insn *cmd; + uint32_t kidx; int cmdlen, i, l, c; - uint16_t kidx; IPFW_UH_WLOCK_ASSERT(ch); @@ -1508,173 +1282,9 @@ return (ret); } -/** - * Remove all rules with given number, or do set manipulation. - * Assumes chain != NULL && *chain != NULL. - * - * The argument is an uint32_t. The low 16 bit are the rule or set number; - * the next 8 bits are the new set; the top 8 bits indicate the command: - * - * 0 delete rules numbered "rulenum" - * 1 delete rules in set "rulenum" - * 2 move rules "rulenum" to set "new_set" - * 3 move rules from set "rulenum" to set "new_set" - * 4 swap sets "rulenum" and "new_set" - * 5 delete rules "rulenum" and set "new_set" - */ -static int -del_entry(struct ip_fw_chain *chain, uint32_t arg) -{ - uint32_t num; /* rule number or old_set */ - uint8_t cmd, new_set; - int do_del, ndel; - int error = 0; - ipfw_range_tlv rt; - - num = arg & 0xffff; - cmd = (arg >> 24) & 0xff; - new_set = (arg >> 16) & 0xff; - - if (cmd > 5 || new_set > RESVD_SET) - return EINVAL; - if (cmd == 0 || cmd == 2 || cmd == 5) { - if (num >= IPFW_DEFAULT_RULE) - return EINVAL; - } else { - if (num > RESVD_SET) /* old_set */ - return EINVAL; - } - - /* Convert old requests into new representation */ - memset(&rt, 0, sizeof(rt)); - rt.start_rule = num; - rt.end_rule = num; - rt.set = num; - rt.new_set = new_set; - do_del = 0; - - switch (cmd) { - case 0: /* delete rules numbered "rulenum" */ - if (num == 0) - rt.flags |= IPFW_RCFLAG_ALL; - else - rt.flags |= IPFW_RCFLAG_RANGE; - do_del = 1; - break; - case 1: /* delete rules in set "rulenum" */ - rt.flags |= IPFW_RCFLAG_SET; - do_del = 1; - break; - case 5: /* delete rules "rulenum" and set "new_set" */ - rt.flags |= IPFW_RCFLAG_RANGE | IPFW_RCFLAG_SET; - rt.set = new_set; - rt.new_set = 0; - do_del = 1; - break; - case 2: /* move rules "rulenum" to set "new_set" */ - rt.flags |= IPFW_RCFLAG_RANGE; - break; - case 3: /* move rules from set "rulenum" to set "new_set" */ - IPFW_UH_WLOCK(chain); - error = swap_sets(chain, &rt, 1); - IPFW_UH_WUNLOCK(chain); - return (error); - case 4: /* swap sets "rulenum" and "new_set" */ - IPFW_UH_WLOCK(chain); - error = swap_sets(chain, &rt, 0); - IPFW_UH_WUNLOCK(chain); - return (error); - default: - return (ENOTSUP); - } - - if (do_del != 0) { - if ((error = delete_range(chain, &rt, &ndel)) != 0) - return (error); - - if (ndel == 0 && (cmd != 1 && num != 0)) - return (EINVAL); - - return (0); - } - - return (move_range(chain, &rt)); -} - -/** - * Reset some or all counters on firewall rules. - * The argument `arg' is an u_int32_t. The low 16 bit are the rule number, - * the next 8 bits are the set number, the top 8 bits are the command: - * 0 work with rules from all set's; - * 1 work with rules only from specified set. - * Specified rule number is zero if we want to clear all entries. - * log_only is 1 if we only want to reset logs, zero otherwise. - */ -static int -zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only) -{ - struct ip_fw *rule; - char *msg; - int i; - - uint16_t rulenum = arg & 0xffff; - uint8_t set = (arg >> 16) & 0xff; - uint8_t cmd = (arg >> 24) & 0xff; - - if (cmd > 1) - return (EINVAL); - if (cmd == 1 && set > RESVD_SET) - return (EINVAL); - - IPFW_UH_RLOCK(chain); - if (rulenum == 0) { - V_norule_counter = 0; - for (i = 0; i < chain->n_rules; i++) { - rule = chain->map[i]; - /* Skip rules not in our set. */ - if (cmd == 1 && rule->set != set) - continue; - clear_counters(rule, log_only); - } - msg = log_only ? "All logging counts reset" : - "Accounting cleared"; - } else { - int cleared = 0; - for (i = 0; i < chain->n_rules; i++) { - rule = chain->map[i]; - if (rule->rulenum == rulenum) { - if (cmd == 0 || rule->set == set) - clear_counters(rule, log_only); - cleared = 1; - } - if (rule->rulenum > rulenum) - break; - } - if (!cleared) { /* we did not find any matching rules */ - IPFW_UH_RUNLOCK(chain); - return (EINVAL); - } - msg = log_only ? "logging count reset" : "cleared"; - } - IPFW_UH_RUNLOCK(chain); - - if (V_fw_verbose) { - int lev = LOG_SECURITY | LOG_NOTICE; - - if (rulenum) - log(lev, "ipfw: Entry %d %s.\n", rulenum, msg); - else - log(lev, "ipfw: %s.\n", msg); - } - return (0); -} - -/* - * Check rule head in FreeBSD11 format - * - */ -static int -check_ipfw_rule1(struct ip_fw_rule *rule, int size, +/* Check rule format */ +int +ipfw_check_rule(struct ip_fw_rule *rule, size_t size, struct rule_check_info *ci) { int l; @@ -1687,40 +1297,7 @@ /* Check for valid cmd_len */ l = roundup2(RULESIZE(rule), sizeof(uint64_t)); if (l != size) { - printf("ipfw: size mismatch (have %d want %d)\n", size, l); - return (EINVAL); - } - if (rule->act_ofs >= rule->cmd_len) { - printf("ipfw: bogus action offset (%u > %u)\n", - rule->act_ofs, rule->cmd_len - 1); - return (EINVAL); - } - - if (rule->rulenum > IPFW_DEFAULT_RULE - 1) - return (EINVAL); - - return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci)); -} - -/* - * Check rule head in FreeBSD8 format - * - */ -static int -check_ipfw_rule0(struct ip_fw_rule0 *rule, int size, - struct rule_check_info *ci) -{ - int l; - - if (size < sizeof(*rule)) { - printf("ipfw: rule too short\n"); - return (EINVAL); - } - - /* Check for valid cmd_len */ - l = sizeof(*rule) + rule->cmd_len * 4 - 4; - if (l != size) { - printf("ipfw: size mismatch (have %d want %d)\n", size, l); + printf("ipfw: size mismatch (have %zu want %d)\n", size, l); return (EINVAL); } if (rule->act_ofs >= rule->cmd_len) { @@ -1735,499 +1312,377 @@ return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci)); } -static int -check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) +enum ipfw_opcheck_result +ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct rule_check_info *ci) { - int cmdlen, l; - int have_action; + ipfw_insn *cmd; + size_t cmdlen; - have_action = 0; + cmd = *pcmd; + cmdlen = F_LEN(cmd); - /* - * Now go for the individual checks. Very simple ones, basically only - * instruction sizes. - */ - for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) { - cmdlen = F_LEN(cmd); - if (cmdlen > l) { - printf("ipfw: opcode %d size truncated\n", - cmd->opcode); - return EINVAL; - } - switch (cmd->opcode) { - case O_PROBE_STATE: - case O_KEEP_STATE: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; - ci->object_opcodes++; - break; - case O_PROTO: - case O_IP_SRC_ME: - case O_IP_DST_ME: - case O_LAYER2: - case O_IN: - case O_FRAG: - case O_DIVERTED: - case O_IPOPT: - case O_IPTOS: - case O_IPPRECEDENCE: - case O_IPVER: - case O_SOCKARG: - case O_TCPFLAGS: - case O_TCPOPTS: - case O_ESTAB: - case O_VERREVPATH: - case O_VERSRCREACH: - case O_ANTISPOOF: - case O_IPSEC: + switch (cmd->opcode) { + case O_PROBE_STATE: + case O_KEEP_STATE: + if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) + return (BAD_SIZE); + ci->object_opcodes++; + break; + case O_PROTO: + case O_IP_SRC_ME: + case O_IP_DST_ME: + case O_LAYER2: + case O_IN: + case O_FRAG: + case O_DIVERTED: + case O_IPOPT: + case O_IPTOS: + case O_IPPRECEDENCE: + case O_IPVER: + case O_SOCKARG: + case O_TCPFLAGS: + case O_TCPOPTS: + case O_ESTAB: + case O_VERREVPATH: + case O_VERSRCREACH: + case O_ANTISPOOF: + case O_IPSEC: #ifdef INET6 - case O_IP6_SRC_ME: - case O_IP6_DST_ME: - case O_EXT_HDR: - case O_IP6: + case O_IP6_SRC_ME: + case O_IP6_DST_ME: + case O_EXT_HDR: + case O_IP6: #endif - case O_IP4: - case O_TAG: - case O_SKIP_ACTION: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; - break; + case O_IP4: + case O_TAG: + case O_SKIP_ACTION: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + return (BAD_SIZE); + break; - case O_EXTERNAL_ACTION: - if (cmd->arg1 == 0 || - cmdlen != F_INSN_SIZE(ipfw_insn)) { - printf("ipfw: invalid external " - "action opcode\n"); - return (EINVAL); - } - ci->object_opcodes++; - /* - * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA - * opcode? - */ - if (l != cmdlen) { - l -= cmdlen; - cmd += cmdlen; - cmdlen = F_LEN(cmd); - if (cmd->opcode == O_EXTERNAL_DATA) - goto check_action; - if (cmd->opcode != O_EXTERNAL_INSTANCE) { - printf("ipfw: invalid opcode " - "next to external action %u\n", - cmd->opcode); - return (EINVAL); - } - if (cmd->arg1 == 0 || - cmdlen != F_INSN_SIZE(ipfw_insn)) { - printf("ipfw: invalid external " - "action instance opcode\n"); - return (EINVAL); - } - ci->object_opcodes++; - } - goto check_action; - - case O_FIB: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; - if (cmd->arg1 >= rt_numfibs) { - printf("ipfw: invalid fib number %d\n", - cmd->arg1); - return EINVAL; - } - break; + case O_EXTERNAL_ACTION: + if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) + return (BAD_SIZE); - case O_SETFIB: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; - if ((cmd->arg1 != IP_FW_TARG) && - ((cmd->arg1 & 0x7FFF) >= rt_numfibs)) { - printf("ipfw: invalid fib number %d\n", - cmd->arg1 & 0x7FFF); - return EINVAL; + if (insntod(cmd, kidx)->kidx == 0) + return (FAILED); + ci->object_opcodes++; + /* + * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA + * opcode? + */ + if (*plen != cmdlen) { + *plen -= cmdlen; + cmd += cmdlen; + *pcmd = cmd; + cmdlen = F_LEN(cmd); + if (cmd->opcode == O_EXTERNAL_DATA) + return (CHECK_ACTION); + if (cmd->opcode != O_EXTERNAL_INSTANCE) { + printf("ipfw: invalid opcode " + "next to external action %u\n", + cmd->opcode); + return (FAILED); } - goto check_action; - - case O_UID: - case O_GID: - case O_JAIL: - case O_IP_SRC: - case O_IP_DST: - case O_TCPSEQ: - case O_TCPACK: - case O_PROB: - case O_ICMPTYPE: - if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) - goto bad_size; - break; - - case O_LIMIT: - if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) - goto bad_size; + if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) + return (BAD_SIZE); + if (insntod(cmd, kidx)->kidx == 0) + return (FAILED); ci->object_opcodes++; - break; + } + return (CHECK_ACTION); + + case O_FIB: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + return (BAD_SIZE); + if (cmd->arg1 >= rt_numfibs) { + printf("ipfw: invalid fib number %d\n", + cmd->arg1); + return (FAILED); + } + break; - case O_LOG: - if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) - goto bad_size; + case O_SETFIB: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + return (BAD_SIZE); + if ((cmd->arg1 != IP_FW_TARG) && + ((cmd->arg1 & 0x7FFF) >= rt_numfibs)) { + printf("ipfw: invalid fib number %d\n", + cmd->arg1 & 0x7FFF); + return (FAILED); + } + return (CHECK_ACTION); + + case O_UID: + case O_GID: + case O_JAIL: + case O_IP_SRC: + case O_IP_DST: + case O_TCPSEQ: + case O_TCPACK: + case O_PROB: + case O_ICMPTYPE: + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) + return (BAD_SIZE); + break; - ((ipfw_insn_log *)cmd)->log_left = - ((ipfw_insn_log *)cmd)->max_log; + case O_LIMIT: + if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) + return (BAD_SIZE); + ci->object_opcodes++; + break; - break; + case O_LOG: + if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) + return (BAD_SIZE); + insntod(cmd, log)->log_left = insntod(cmd, log)->max_log; + break; - case O_IP_SRC_MASK: - case O_IP_DST_MASK: - /* only odd command lengths */ - if ((cmdlen & 1) == 0) - goto bad_size; - break; + case O_IP_SRC_MASK: + case O_IP_DST_MASK: + /* only odd command lengths */ + if ((cmdlen & 1) == 0) + return (BAD_SIZE); + break; - case O_IP_SRC_SET: - case O_IP_DST_SET: - if (cmd->arg1 == 0 || cmd->arg1 > 256) { - printf("ipfw: invalid set size %d\n", - cmd->arg1); - return EINVAL; - } - if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + - (cmd->arg1+31)/32 ) - goto bad_size; - break; + case O_IP_SRC_SET: + case O_IP_DST_SET: + if (cmd->arg1 == 0 || cmd->arg1 > 256) { + printf("ipfw: invalid set size %d\n", + cmd->arg1); + return (FAILED); + } + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + + (cmd->arg1+31)/32 ) + return (BAD_SIZE); + break; - case O_IP_SRC_LOOKUP: - if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) - goto bad_size; - case O_IP_DST_LOOKUP: - if (cmd->arg1 >= V_fw_tables_max) { - printf("ipfw: invalid table number %d\n", - cmd->arg1); - return (EINVAL); - } - if (cmdlen != F_INSN_SIZE(ipfw_insn) && - cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 && - cmdlen != F_INSN_SIZE(ipfw_insn_u32)) - goto bad_size; - ci->object_opcodes++; - break; - case O_IP_FLOW_LOOKUP: - case O_MAC_DST_LOOKUP: - case O_MAC_SRC_LOOKUP: - if (cmd->arg1 >= V_fw_tables_max) { - printf("ipfw: invalid table number %d\n", - cmd->arg1); - return (EINVAL); - } - if (cmdlen != F_INSN_SIZE(ipfw_insn) && - cmdlen != F_INSN_SIZE(ipfw_insn_u32)) - goto bad_size; - ci->object_opcodes++; - break; - case O_MACADDR2: - if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) - goto bad_size; - break; + case O_IP_SRC_LOOKUP: + case O_IP_DST_LOOKUP: + case O_IP_FLOW_LOOKUP: + case O_MAC_SRC_LOOKUP: + case O_MAC_DST_LOOKUP: + if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx) && + cmdlen != F_INSN_SIZE(ipfw_insn_table)) + return (BAD_SIZE); + if (insntod(cmd, kidx)->kidx >= V_fw_tables_max) { + printf("ipfw: invalid table index %u\n", + insntod(cmd, kidx)->kidx); + return (FAILED); + } + ci->object_opcodes++; + break; + case O_MACADDR2: + if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) + return (BAD_SIZE); + break; - case O_NOP: - case O_IPID: - case O_IPTTL: - case O_IPLEN: - case O_TCPDATALEN: - case O_TCPMSS: - case O_TCPWIN: - case O_TAGGED: - if (cmdlen < 1 || cmdlen > 31) - goto bad_size; - break; + case O_NOP: + case O_IPID: + case O_IPTTL: + case O_IPLEN: + case O_TCPDATALEN: + case O_TCPMSS: + case O_TCPWIN: + case O_TAGGED: + if (cmdlen < 1 || cmdlen > 31) + return (BAD_SIZE); + break; - case O_DSCP: - case O_MARK: - if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1) - goto bad_size; - break; + case O_DSCP: + case O_MARK: + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1) + return (BAD_SIZE); + break; - case O_MAC_TYPE: - case O_IP_SRCPORT: - case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ - if (cmdlen < 2 || cmdlen > 31) - goto bad_size; - break; + case O_MAC_TYPE: + case O_IP_SRCPORT: + case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ + if (cmdlen < 2 || cmdlen > 31) + return (BAD_SIZE); + break; - case O_RECV: - case O_XMIT: - case O_VIA: - if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) - goto bad_size; - ci->object_opcodes++; - break; + case O_RECV: + case O_XMIT: + case O_VIA: + if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) + return (BAD_SIZE); + ci->object_opcodes++; + break; - case O_ALTQ: - if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) - goto bad_size; - break; + case O_ALTQ: + if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) + return (BAD_SIZE); + break; - case O_PIPE: - case O_QUEUE: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; - goto check_action; + case O_PIPE: + case O_QUEUE: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + return (BAD_SIZE); + return (CHECK_ACTION); - case O_FORWARD_IP: - if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) - goto bad_size; - goto check_action; + case O_FORWARD_IP: + if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) + return (BAD_SIZE); + return (CHECK_ACTION); #ifdef INET6 - case O_FORWARD_IP6: - if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6)) - goto bad_size; - goto check_action; + case O_FORWARD_IP6: + if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6)) + return (BAD_SIZE); + return (CHECK_ACTION); #endif /* INET6 */ - case O_DIVERT: - case O_TEE: - if (ip_divert_ptr == NULL) - return EINVAL; - else - goto check_size; - case O_NETGRAPH: - case O_NGTEE: - if (ng_ipfw_input_p == NULL) - return EINVAL; - else - goto check_size; - case O_NAT: - if (!IPFW_NAT_LOADED) - return EINVAL; - if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) - goto bad_size; - goto check_action; - case O_CHECK_STATE: - ci->object_opcodes++; - goto check_size; - case O_SETMARK: - if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) - goto bad_size; - goto check_action; - case O_REJECT: - /* "unreach needfrag" has variable len. */ - if ((cmdlen == F_INSN_SIZE(ipfw_insn) || - cmdlen == F_INSN_SIZE(ipfw_insn_u16))) - goto check_action; - /* FALLTHROUGH */ - case O_FORWARD_MAC: /* XXX not implemented yet */ - case O_COUNT: - case O_ACCEPT: - case O_DENY: - case O_SETDSCP: + case O_DIVERT: + case O_TEE: + if (ip_divert_ptr == NULL) + return (FAILED); + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + return (BAD_SIZE); + return (CHECK_ACTION); + case O_NETGRAPH: + case O_NGTEE: + if (ng_ipfw_input_p == NULL) + return (FAILED); + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + return (BAD_SIZE); + return (CHECK_ACTION); + case O_NAT: + if (!IPFW_NAT_LOADED) + return (FAILED); + if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) + return (BAD_SIZE); + return (CHECK_ACTION); + + case O_SKIPTO: + case O_CALLRETURN: + case O_SETMARK: + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) + return (BAD_SIZE); + return (CHECK_ACTION); + + case O_CHECK_STATE: + if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) + return (BAD_SIZE); + ci->object_opcodes++; + return (CHECK_ACTION); + + case O_FORWARD_MAC: /* XXX not implemented yet */ + case O_COUNT: + case O_ACCEPT: + case O_DENY: + case O_REJECT: + case O_SETDSCP: #ifdef INET6 - case O_UNREACH6: + case O_UNREACH6: #endif - case O_SKIPTO: - case O_REASS: - case O_CALLRETURN: -check_size: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; -check_action: - if (have_action) { - printf("ipfw: opcode %d, multiple actions" - " not allowed\n", - cmd->opcode); - return (EINVAL); - } - have_action = 1; - if (l != cmdlen) { - printf("ipfw: opcode %d, action must be" - " last opcode\n", - cmd->opcode); - return (EINVAL); - } - break; + case O_REASS: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + return (BAD_SIZE); + return (CHECK_ACTION); #ifdef INET6 + case O_IP6_SRC: + case O_IP6_DST: + if (cmdlen != F_INSN_SIZE(struct in6_addr) + + F_INSN_SIZE(ipfw_insn)) + return (BAD_SIZE); + break; + + case O_FLOW6ID: + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + + ((ipfw_insn_u32 *)cmd)->o.arg1) + return (BAD_SIZE); + break; + + case O_IP6_SRC_MASK: + case O_IP6_DST_MASK: + if ( !(cmdlen & 1) || cmdlen > 127) + return (BAD_SIZE); + break; + case O_ICMP6TYPE: + if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) + return (BAD_SIZE); + break; +#endif + + default: + switch (cmd->opcode) { +#ifndef INET6 + case O_IP6_SRC_ME: + case O_IP6_DST_ME: + case O_EXT_HDR: + case O_IP6: + case O_UNREACH6: case O_IP6_SRC: case O_IP6_DST: - if (cmdlen != F_INSN_SIZE(struct in6_addr) + - F_INSN_SIZE(ipfw_insn)) - goto bad_size; - break; - case O_FLOW6ID: - if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + - ((ipfw_insn_u32 *)cmd)->o.arg1) - goto bad_size; - break; - case O_IP6_SRC_MASK: case O_IP6_DST_MASK: - if ( !(cmdlen & 1) || cmdlen > 127) - goto bad_size; - break; case O_ICMP6TYPE: - if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) - goto bad_size; - break; + printf("ipfw: no IPv6 support in kernel\n"); + return (FAILED); #endif - default: - switch (cmd->opcode) { -#ifndef INET6 - case O_IP6_SRC_ME: - case O_IP6_DST_ME: - case O_EXT_HDR: - case O_IP6: - case O_UNREACH6: - case O_IP6_SRC: - case O_IP6_DST: - case O_FLOW6ID: - case O_IP6_SRC_MASK: - case O_IP6_DST_MASK: - case O_ICMP6TYPE: - printf("ipfw: no IPv6 support in kernel\n"); - return (EPROTONOSUPPORT); -#endif - default: - printf("ipfw: opcode %d, unknown opcode\n", - cmd->opcode); - return (EINVAL); - } + printf("ipfw: opcode %d: unknown opcode\n", + cmd->opcode); + return (FAILED); } } - if (have_action == 0) { - printf("ipfw: missing action\n"); - return (EINVAL); - } - return 0; - -bad_size: - printf("ipfw: opcode %d size %d wrong\n", - cmd->opcode, cmdlen); - return (EINVAL); + return (SUCCESS); } -/* - * Translation of requests for compatibility with FreeBSD 7.2/8. - * a static variable tells us if we have an old client from userland, - * and if necessary we translate requests and responses between the - * two formats. - */ -static int is7 = 0; - -struct ip_fw7 { - struct ip_fw7 *next; /* linked list of rules */ - struct ip_fw7 *next_rule; /* ptr to next [skipto] rule */ - /* 'next_rule' is used to pass up 'set_disable' status */ - - uint16_t act_ofs; /* offset of action in 32-bit units */ - uint16_t cmd_len; /* # of 32-bit words in cmd */ - uint16_t rulenum; /* rule number */ - uint8_t set; /* rule set (0..31) */ - // #define RESVD_SET 31 /* set for default and persistent rules */ - uint8_t _pad; /* padding */ - // uint32_t id; /* rule id, only in v.8 */ - /* These fields are present in all rules. */ - uint64_t pcnt; /* Packet counter */ - uint64_t bcnt; /* Byte counter */ - uint32_t timestamp; /* tv_sec of last match */ - - ipfw_insn cmd[1]; /* storage for commands */ -}; - -static int convert_rule_to_7(struct ip_fw_rule0 *rule); -static int convert_rule_to_8(struct ip_fw_rule0 *rule); - -#ifndef RULESIZE7 -#define RULESIZE7(rule) (sizeof(struct ip_fw7) + \ - ((struct ip_fw7 *)(rule))->cmd_len * 4 - 4) -#endif - -/* - * Copy the static and dynamic rules to the supplied buffer - * and return the amount of space actually used. - * Must be run under IPFW_UH_RLOCK - */ -static size_t -ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) +static __noinline int +check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) { - char *bp = buf; - char *ep = bp + space; - struct ip_fw *rule; - struct ip_fw_rule0 *dst; - struct timeval boottime; - int error, i, l, warnflag; - time_t boot_seconds; - - warnflag = 0; - - getboottime(&boottime); - boot_seconds = boottime.tv_sec; - for (i = 0; i < chain->n_rules; i++) { - rule = chain->map[i]; - - if (is7) { - /* Convert rule to FreeBSd 7.2 format */ - l = RULESIZE7(rule); - if (bp + l + sizeof(uint32_t) <= ep) { - bcopy(rule, bp, l + sizeof(uint32_t)); - error = set_legacy_obj_kidx(chain, - (struct ip_fw_rule0 *)bp); - if (error != 0) - return (0); - error = convert_rule_to_7((struct ip_fw_rule0 *) bp); - if (error) - return 0; /*XXX correct? */ - /* - * XXX HACK. Store the disable mask in the "next" - * pointer in a wild attempt to keep the ABI the same. - * Why do we do this on EVERY rule? - */ - bcopy(&V_set_disable, - &(((struct ip_fw7 *)bp)->next_rule), - sizeof(V_set_disable)); - if (((struct ip_fw7 *)bp)->timestamp) - ((struct ip_fw7 *)bp)->timestamp += boot_seconds; - bp += l; - } - continue; /* go to next rule */ - } + int cmdlen, l; + int have_action, ret; - l = RULEUSIZE0(rule); - if (bp + l > ep) { /* should not happen */ - printf("overflow dumping static rules\n"); - break; + /* + * Now go for the individual checks. Very simple ones, basically only + * instruction sizes. + */ + have_action = 0; + for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) { + cmdlen = F_LEN(cmd); + if (cmdlen > l) { + printf("ipfw: opcode %d: size truncated\n", + cmd->opcode); + return (EINVAL); } - dst = (struct ip_fw_rule0 *)bp; - export_rule0(rule, dst, l); - error = set_legacy_obj_kidx(chain, dst); - - /* - * XXX HACK. Store the disable mask in the "next" - * pointer in a wild attempt to keep the ABI the same. - * Why do we do this on EVERY rule? - * - * XXX: "ipfw set show" (ab)uses IP_FW_GET to read disabled mask - * so we need to fail _after_ saving at least one mask. - */ - bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable)); - if (dst->timestamp) - dst->timestamp += boot_seconds; - bp += l; - - if (error != 0) { - if (error == 2) { - /* Non-fatal table rewrite error. */ - warnflag = 1; - continue; + if (ci->version != IP_FW3_OPVER) + ret = (*check_opcode_f)(&cmd, &l, ci); + else + ret = ipfw_check_opcode(&cmd, &l, ci); + + if (ret == CHECK_ACTION) { + if (have_action != 0) { + printf("ipfw: opcode %d: multiple actions" + " not allowed\n", cmd->opcode); + ret = FAILED; + } else + have_action = 1; + + if (l != F_LEN(cmd)) { + printf("ipfw: opcode %d: action must be" + " last opcode\n", cmd->opcode); + ret = FAILED; } - printf("Stop on rule %d. Fail to convert table\n", - rule->rulenum); - break; + } + switch (ret) { + case SUCCESS: + continue; + case BAD_SIZE: + printf("ipfw: opcode %d: wrong size %d\n", + cmd->opcode, cmdlen); + /* FALLTHROUGH */ + case FAILED: + return (EINVAL); } } - if (warnflag != 0) - printf("ipfw: process %s is using legacy interfaces," - " consider rebuilding\n", ""); - ipfw_get_dynamic(chain, &bp, ep); /* protected by the dynamic lock */ - return (bp - (char *)buf); + if (have_action == 0) { + printf("ipfw: missing action\n"); + return (EINVAL); + } + return (0); } struct dump_args { @@ -2257,7 +1712,7 @@ * Returns 0 on success. */ static int -export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx, +export_objhash_ntlv(struct namedobj_instance *ni, uint32_t kidx, struct sockopt_data *sd) { struct named_object *no; @@ -2278,7 +1733,8 @@ export_named_objects(struct namedobj_instance *ni, struct dump_args *da, struct sockopt_data *sd) { - int error, i; + uint32_t i; + int error; for (i = 0; i < IPFW_TABLES_MAX && da->tcount > 0; i++) { if ((da->bmask[i / 32] & (1 << (i % 32))) == 0) @@ -2356,7 +1812,7 @@ } int -ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint16_t kidx) +ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint32_t kidx) { uint32_t bidx; @@ -2383,8 +1839,8 @@ { struct opcode_obj_rewrite *rw; ipfw_insn *cmd; + uint32_t kidx; int cmdlen, l; - uint16_t kidx; uint8_t subtype; l = rule->cmd_len; @@ -2554,7 +2010,7 @@ { struct opcode_obj_rewrite *rw; struct obj_idx *p; - uint16_t kidx; + uint32_t kidx; int error; /* @@ -2590,67 +2046,10 @@ unref_oib_objects(ch, cmd, oib, pidx); IPFW_UH_WUNLOCK(ch); - return (error); - } - - return (0); -} - -/* - * Compatibility function for old ipfw(8) binaries. - * Rewrites table/nat kernel indices with userland ones. - * Convert tables matching '/^\d+$/' to their atoi() value. - * Use number 65535 for other tables. - * - * Returns 0 on success. - */ -static int -set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule) -{ - struct opcode_obj_rewrite *rw; - struct named_object *no; - ipfw_insn *cmd; - char *end; - long val; - int cmdlen, error, l; - uint16_t kidx, uidx; - uint8_t subtype; - - error = 0; - - l = rule->cmd_len; - cmd = rule->cmd; - cmdlen = 0; - for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { - cmdlen = F_LEN(cmd); - - /* Check if is index in given opcode */ - rw = find_op_rw(cmd, &kidx, &subtype); - if (rw == NULL) - continue; - - /* Try to find referenced kernel object */ - no = rw->find_bykidx(ch, kidx); - if (no == NULL) - continue; - - val = strtol(no->name, &end, 10); - if (*end == '\0' && val < 65535) { - uidx = val; - } else { - /* - * We are called via legacy opcode. - * Save error and show table as fake number - * not to make ipfw(8) hang. - */ - uidx = 65535; - error = 2; - } - - rw->update(cmd, uidx); + return (error); } - return (error); + return (0); } /* @@ -2694,8 +2093,8 @@ struct opcode_obj_rewrite *rw; struct named_object *no; ipfw_insn *cmd; + uint32_t kidx; int cmdlen, l; - uint16_t kidx; uint8_t subtype; IPFW_UH_WLOCK_ASSERT(ch); @@ -2897,8 +2296,8 @@ } /* - * Adds one or more rules to ipfw @chain. - * Data layout (version 0)(current): + * Parses one or more rules from userland. + * Data layout (version 1)(current): * Request: * [ * ip_fw3_opheader @@ -2921,52 +2320,45 @@ * * Returns 0 on success. */ -static int -add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, - struct sockopt_data *sd) +static __noinline int +parse_rules_v1(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd, ipfw_obj_ctlv **prtlv, + struct rule_check_info **pci) { ipfw_obj_ctlv *ctlv, *rtlv, *tstate; ipfw_obj_ntlv *ntlv; - int clen, error, idx; - uint32_t count, read; + struct rule_check_info *ci, *cbuf; struct ip_fw_rule *r; - struct rule_check_info rci, *ci, *cbuf; - int i, rsize; + size_t count, clen, read, rsize; + uint32_t idx, rulenum; + int error; op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize); ctlv = (ipfw_obj_ctlv *)(op3 + 1); - read = sizeof(ip_fw3_opheader); - rtlv = NULL; - tstate = NULL; - cbuf = NULL; - memset(&rci, 0, sizeof(struct rule_check_info)); - if (read + sizeof(*ctlv) > sd->valsize) return (EINVAL); + rtlv = NULL; + tstate = NULL; + cbuf = NULL; + /* Table names or other named objects. */ if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { + /* Check size and alignment. */ clen = ctlv->head.length; - /* Check size and alignment */ - if (clen > sd->valsize || clen < sizeof(*ctlv)) - return (EINVAL); - if ((clen % sizeof(uint64_t)) != 0) + if (read + clen > sd->valsize || clen < sizeof(*ctlv) || + (clen % sizeof(uint64_t)) != 0) return (EINVAL); - - /* - * Some table names or other named objects. - * Check for validness. - */ + /* Check for validness. */ count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv); if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv)) return (EINVAL); - /* * Check each TLV. * Ensure TLVs are sorted ascending and * there are no duplicates. */ - idx = -1; + idx = 0; ntlv = (ipfw_obj_ntlv *)(ctlv + 1); while (count > 0) { if (ntlv->head.length != sizeof(ipfw_obj_ntlv)) @@ -2987,70 +2379,59 @@ tstate = ctlv; read += ctlv->head.length; ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); - } - if (read + sizeof(*ctlv) > sd->valsize) - return (EINVAL); + if (read + sizeof(*ctlv) > sd->valsize) + return (EINVAL); + } + /* List of rules. */ if (ctlv->head.type == IPFW_TLV_RULE_LIST) { clen = ctlv->head.length; - if (clen + read > sd->valsize || clen < sizeof(*ctlv)) + if (read + clen > sd->valsize || clen < sizeof(*ctlv) || + (clen % sizeof(uint64_t)) != 0) return (EINVAL); - if ((clen % sizeof(uint64_t)) != 0) - return (EINVAL); - - /* - * TODO: Permit adding multiple rules at once - */ - if (ctlv->count != 1) - return (ENOTSUP); clen -= sizeof(*ctlv); - - if (ctlv->count > clen / sizeof(struct ip_fw_rule)) + if (ctlv->count == 0 || + ctlv->count > clen / sizeof(struct ip_fw_rule)) return (EINVAL); - /* Allocate state for each rule or use stack */ - if (ctlv->count == 1) { - memset(&rci, 0, sizeof(struct rule_check_info)); - cbuf = &rci; - } else - cbuf = malloc(ctlv->count * sizeof(*ci), M_TEMP, - M_WAITOK | M_ZERO); - ci = cbuf; + /* Allocate state for each rule */ + cbuf = malloc(ctlv->count * sizeof(struct rule_check_info), + M_TEMP, M_WAITOK | M_ZERO); /* * Check each rule for validness. * Ensure numbered rules are sorted ascending * and properly aligned */ - idx = 0; - r = (struct ip_fw_rule *)(ctlv + 1); + rulenum = 0; count = 0; error = 0; + ci = cbuf; + r = (struct ip_fw_rule *)(ctlv + 1); while (clen > 0) { - rsize = roundup2(RULESIZE(r), sizeof(uint64_t)); - if (rsize > clen || ctlv->count <= count) { + rsize = RULEUSIZE1(r); + if (rsize > clen || count > ctlv->count) { error = EINVAL; break; } - ci->ctlv = tstate; - error = check_ipfw_rule1(r, rsize, ci); + ci->version = IP_FW3_OPVER; + error = ipfw_check_rule(r, rsize, ci); if (error != 0) break; /* Check sorting */ - if (r->rulenum != 0 && r->rulenum < idx) { - printf("rulenum %d idx %d\n", r->rulenum, idx); + if (count != 0 && ((rulenum == 0) != (r->rulenum == 0) || + r->rulenum < rulenum)) { + printf("ipfw: wrong order: rulenum %u" + " vs %u\n", r->rulenum, rulenum); error = EINVAL; break; } - idx = r->rulenum; - + rulenum = r->rulenum; ci->urule = (caddr_t)r; - - rsize = roundup2(rsize, sizeof(uint64_t)); clen -= rsize; r = (struct ip_fw_rule *)((caddr_t)r + rsize); count++; @@ -3058,8 +2439,7 @@ } if (ctlv->count != count || error != 0) { - if (cbuf != &rci) - free(cbuf, M_TEMP); + free(cbuf, M_TEMP); return (EINVAL); } @@ -3068,37 +2448,78 @@ ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); } - if (read != sd->valsize || rtlv == NULL || rtlv->count == 0) { - if (cbuf != NULL && cbuf != &rci) - free(cbuf, M_TEMP); + if (read != sd->valsize || rtlv == NULL) { + free(cbuf, M_TEMP); return (EINVAL); } + *prtlv = rtlv; + *pci = cbuf; + return (0); +} + +/* + * Copy rule @urule from v1 userland format (current) to kernel @krule. + */ +static void +import_rule_v1(struct ip_fw_chain *chain, struct rule_check_info *ci) +{ + struct ip_fw_rule *urule; + struct ip_fw *krule; + + urule = (struct ip_fw_rule *)ci->urule; + krule = ci->krule = ipfw_alloc_rule(chain, RULEKSIZE1(urule)); + + krule->act_ofs = urule->act_ofs; + krule->cmd_len = urule->cmd_len; + krule->rulenum = urule->rulenum; + krule->set = urule->set; + krule->flags = urule->flags; + + /* Save rulenum offset */ + ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum); + + /* Copy opcodes */ + memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); +} + +/* + * Adds one or more rules to ipfw @chain. + */ +static int +add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_ctlv *rtlv; + struct rule_check_info *ci, *nci; + int i, ret; + /* - * Passed rules seems to be valid. - * Allocate storage and try to add them to chain. + * Check rules buffer for validness. */ - for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) { - clen = RULEKSIZE1((struct ip_fw_rule *)ci->urule); - ci->krule = ipfw_alloc_rule(chain, clen); - import_rule1(ci); - } - - if ((error = commit_rules(chain, cbuf, rtlv->count)) != 0) { - /* Free allocate krules */ - for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) + ret = parse_rules_v1(chain, op3, sd, &rtlv, &nci); + if (ret != 0) + return (ret); + /* + * Allocate storage for the kernel representation of rules. + */ + for (i = 0, ci = nci; i < rtlv->count; i++, ci++) + import_rule_v1(chain, ci); + /* + * Try to add new rules to the chain. + */ + if ((ret = ipfw_commit_rules(chain, nci, rtlv->count)) != 0) { + for (i = 0, ci = nci; i < rtlv->count; i++, ci++) ipfw_free_rule(ci->krule); } - - if (cbuf != NULL && cbuf != &rci) - free(cbuf, M_TEMP); - - return (error); + /* Cleanup after parse_rules() */ + free(nci, M_TEMP); + return (ret); } /* * Lists all sopts currently registered. - * Data layout (v0)(current): + * Data layout (v1)(current): * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size * Reply: [ ipfw_obj_lheader ipfw_sopt_info x N ] * @@ -3113,7 +2534,8 @@ struct ipfw_sopt_handler *sh; uint32_t count, n, size; - olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); + olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd, + sizeof(*olh)); if (olh == NULL) return (EINVAL); if (sd->valsize < olh->size) @@ -3209,10 +2631,10 @@ * Returns pointer to handler or NULL. */ static struct opcode_obj_rewrite * -find_op_rw(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +find_op_rw(ipfw_insn *cmd, uint32_t *puidx, uint8_t *ptype) { struct opcode_obj_rewrite *rw, *lo, *hi; - uint16_t uidx; + uint32_t uidx; uint8_t subtype; if (find_op_rw_range(cmd->opcode, &lo, &hi) != 0) @@ -3231,7 +2653,7 @@ return (NULL); } int -classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx) +classify_opcode_kidx(ipfw_insn *cmd, uint32_t *puidx) { if (find_op_rw(cmd, puidx, NULL) == NULL) @@ -3240,7 +2662,7 @@ } void -update_opcode_kidx(ipfw_insn *cmd, uint16_t idx) +update_opcode_kidx(ipfw_insn *cmd, uint32_t idx) { struct opcode_obj_rewrite *rw; @@ -3252,7 +2674,6 @@ void ipfw_init_obj_rewriter(void) { - ctl3_rewriters = NULL; ctl3_rsize = 0; } @@ -3260,7 +2681,6 @@ void ipfw_destroy_obj_rewriter(void) { - if (ctl3_rewriters != NULL) free(ctl3_rewriters, M_IPFW); ctl3_rewriters = NULL; @@ -3393,6 +2813,49 @@ return (0); } +void +ipfw_enable_skipto_cache(struct ip_fw_chain *chain) +{ + + IPFW_UH_WLOCK_ASSERT(chain); + update_skipto_cache(chain, chain->map); + + IPFW_WLOCK(chain); + swap_skipto_cache(chain); + V_skipto_cache = 1; + IPFW_WUNLOCK(chain); +} + +/* + * Enables or disable skipto cache. + * Request: [ ipfw_cmd_header ] size = ipfw_cmd_header.size + * Reply: [ ipfw_cmd_header ] + * Returns 0 on success + */ +static int +manage_skiptocache(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_cmd_header *hdr; + + if (sd->valsize != sizeof(*hdr)) + return (EINVAL); + + hdr = (ipfw_cmd_header *)ipfw_get_sopt_space(sd, sd->valsize); + if (hdr->cmd != SKIPTO_CACHE_DISABLE && + hdr->cmd != SKIPTO_CACHE_ENABLE) + return (EOPNOTSUPP); + + IPFW_UH_WLOCK(chain); + if (hdr->cmd != V_skipto_cache) { + if (hdr->cmd == SKIPTO_CACHE_ENABLE) + ipfw_enable_skipto_cache(chain); + V_skipto_cache = hdr->cmd; + } + IPFW_UH_WUNLOCK(chain); + return (0); +} + /* * Compares two sopt handlers (code, version and handler ptr). * Used both as qsort() and bsearch(). @@ -3488,7 +2951,6 @@ void ipfw_init_sopt_handler(void) { - CTL3_LOCK_INIT(); IPFW_ADD_SOPT_HANDLER(1, scodes); } @@ -3496,11 +2958,22 @@ void ipfw_destroy_sopt_handler(void) { - IPFW_DEL_SOPT_HANDLER(1, scodes); CTL3_LOCK_DESTROY(); } +void +ipfw_register_compat(ipfw_check_opcode_t f) +{ + check_opcode_f = f; +} + +void +ipfw_unregister_compat(void) +{ + check_opcode_f = check_opcode_compat_nop; +} + /* * Adds one or more sockopt handlers to the global array. * Function may sleep. @@ -3683,7 +3156,7 @@ return (error); if (sopt->sopt_name != IP_FW3) - return (ipfw_ctl(sopt)); + return (EOPNOTSUPP); chain = &V_layer3_chain; error = 0; @@ -3796,432 +3269,6 @@ return (error); } -/** - * {set|get}sockopt parser. - */ -int -ipfw_ctl(struct sockopt *sopt) -{ -#define RULE_MAXSIZE (512*sizeof(u_int32_t)) - int error; - size_t size; - struct ip_fw *buf; - struct ip_fw_rule0 *rule; - struct ip_fw_chain *chain; - u_int32_t rulenum[2]; - uint32_t opt; - struct rule_check_info ci; - IPFW_RLOCK_TRACKER; - - chain = &V_layer3_chain; - error = 0; - - opt = sopt->sopt_name; - - /* - * Disallow modifications in really-really secure mode, but still allow - * the logging counters to be reset. - */ - if (opt == IP_FW_ADD || - (sopt->sopt_dir == SOPT_SET && opt != IP_FW_RESETLOG)) { - error = securelevel_ge(sopt->sopt_td->td_ucred, 3); - if (error != 0) - return (error); - } - - switch (opt) { - case IP_FW_GET: - /* - * pass up a copy of the current rules. Static rules - * come first (the last of which has number IPFW_DEFAULT_RULE), - * followed by a possibly empty list of dynamic rule. - * The last dynamic rule has NULL in the "next" field. - * - * Note that the calculated size is used to bound the - * amount of data returned to the user. The rule set may - * change between calculating the size and returning the - * data in which case we'll just return what fits. - */ - for (;;) { - int len = 0, want; - - size = chain->static_len; - size += ipfw_dyn_len(); - if (size >= sopt->sopt_valsize) - break; - buf = malloc(size, M_TEMP, M_WAITOK | M_ZERO); - IPFW_UH_RLOCK(chain); - /* check again how much space we need */ - want = chain->static_len + ipfw_dyn_len(); - if (size >= want) - len = ipfw_getrules(chain, buf, size); - IPFW_UH_RUNLOCK(chain); - if (size >= want) - error = sooptcopyout(sopt, buf, len); - free(buf, M_TEMP); - if (size >= want) - break; - } - break; - - case IP_FW_FLUSH: - /* locking is done within del_entry() */ - error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */ - break; - - case IP_FW_ADD: - rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK); - error = sooptcopyin(sopt, rule, RULE_MAXSIZE, - sizeof(struct ip_fw7) ); - - memset(&ci, 0, sizeof(struct rule_check_info)); - - /* - * If the size of commands equals RULESIZE7 then we assume - * a FreeBSD7.2 binary is talking to us (set is7=1). - * is7 is persistent so the next 'ipfw list' command - * will use this format. - * NOTE: If wrong version is guessed (this can happen if - * the first ipfw command is 'ipfw [pipe] list') - * the ipfw binary may crash or loop infinitly... - */ - size = sopt->sopt_valsize; - if (size == RULESIZE7(rule)) { - is7 = 1; - error = convert_rule_to_8(rule); - if (error) { - free(rule, M_TEMP); - return error; - } - size = RULESIZE(rule); - } else - is7 = 0; - if (error == 0) - error = check_ipfw_rule0(rule, size, &ci); - if (error == 0) { - /* locking is done within add_rule() */ - struct ip_fw *krule; - krule = ipfw_alloc_rule(chain, RULEKSIZE0(rule)); - ci.urule = (caddr_t)rule; - ci.krule = krule; - import_rule0(&ci); - error = commit_rules(chain, &ci, 1); - if (error != 0) - ipfw_free_rule(ci.krule); - else if (sopt->sopt_dir == SOPT_GET) { - if (is7) { - error = convert_rule_to_7(rule); - size = RULESIZE7(rule); - if (error) { - free(rule, M_TEMP); - return error; - } - } - error = sooptcopyout(sopt, rule, size); - } - } - free(rule, M_TEMP); - break; - - case IP_FW_DEL: - /* - * IP_FW_DEL is used for deleting single rules or sets, - * and (ab)used to atomically manipulate sets. Argument size - * is used to distinguish between the two: - * sizeof(u_int32_t) - * delete single rule or set of rules, - * or reassign rules (or sets) to a different set. - * 2*sizeof(u_int32_t) - * atomic disable/enable sets. - * first u_int32_t contains sets to be disabled, - * second u_int32_t contains sets to be enabled. - */ - error = sooptcopyin(sopt, rulenum, - 2*sizeof(u_int32_t), sizeof(u_int32_t)); - if (error) - break; - size = sopt->sopt_valsize; - if (size == sizeof(u_int32_t) && rulenum[0] != 0) { - /* delete or reassign, locking done in del_entry() */ - error = del_entry(chain, rulenum[0]); - } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */ - IPFW_UH_WLOCK(chain); - V_set_disable = - (V_set_disable | rulenum[0]) & ~rulenum[1] & - ~(1<sopt_val != 0) { - error = sooptcopyin(sopt, rulenum, - sizeof(u_int32_t), sizeof(u_int32_t)); - if (error) - break; - } - error = zero_entry(chain, rulenum[0], - sopt->sopt_name == IP_FW_RESETLOG); - break; - - /*--- TABLE opcodes ---*/ - case IP_FW_TABLE_ADD: - case IP_FW_TABLE_DEL: - { - ipfw_table_entry ent; - struct tentry_info tei; - struct tid_info ti; - struct table_value v; - - error = sooptcopyin(sopt, &ent, - sizeof(ent), sizeof(ent)); - if (error) - break; - - memset(&tei, 0, sizeof(tei)); - tei.paddr = &ent.addr; - tei.subtype = AF_INET; - tei.masklen = ent.masklen; - ipfw_import_table_value_legacy(ent.value, &v); - tei.pvalue = &v; - memset(&ti, 0, sizeof(ti)); - ti.uidx = ent.tbl; - ti.type = IPFW_TABLE_CIDR; - - error = (opt == IP_FW_TABLE_ADD) ? - add_table_entry(chain, &ti, &tei, 0, 1) : - del_table_entry(chain, &ti, &tei, 0, 1); - } - break; - - case IP_FW_TABLE_FLUSH: - { - u_int16_t tbl; - struct tid_info ti; - - error = sooptcopyin(sopt, &tbl, - sizeof(tbl), sizeof(tbl)); - if (error) - break; - memset(&ti, 0, sizeof(ti)); - ti.uidx = tbl; - error = flush_table(chain, &ti); - } - break; - - case IP_FW_TABLE_GETSIZE: - { - u_int32_t tbl, cnt; - struct tid_info ti; - - if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl), - sizeof(tbl)))) - break; - memset(&ti, 0, sizeof(ti)); - ti.uidx = tbl; - IPFW_RLOCK(chain); - error = ipfw_count_table(chain, &ti, &cnt); - IPFW_RUNLOCK(chain); - if (error) - break; - error = sooptcopyout(sopt, &cnt, sizeof(cnt)); - } - break; - - case IP_FW_TABLE_LIST: - { - ipfw_table *tbl; - struct tid_info ti; - - if (sopt->sopt_valsize < sizeof(*tbl)) { - error = EINVAL; - break; - } - size = sopt->sopt_valsize; - tbl = malloc(size, M_TEMP, M_WAITOK); - error = sooptcopyin(sopt, tbl, size, sizeof(*tbl)); - if (error) { - free(tbl, M_TEMP); - break; - } - tbl->size = (size - sizeof(*tbl)) / - sizeof(ipfw_table_entry); - memset(&ti, 0, sizeof(ti)); - ti.uidx = tbl->tbl; - IPFW_RLOCK(chain); - error = ipfw_dump_table_legacy(chain, &ti, tbl); - IPFW_RUNLOCK(chain); - if (error) { - free(tbl, M_TEMP); - break; - } - error = sooptcopyout(sopt, tbl, size); - free(tbl, M_TEMP); - } - break; - - /*--- NAT operations are protected by the IPFW_LOCK ---*/ - case IP_FW_NAT_CFG: - if (IPFW_NAT_LOADED) - error = ipfw_nat_cfg_ptr(sopt); - else { - printf("IP_FW_NAT_CFG: %s\n", - "ipfw_nat not present, please load it"); - error = EINVAL; - } - break; - - case IP_FW_NAT_DEL: - if (IPFW_NAT_LOADED) - error = ipfw_nat_del_ptr(sopt); - else { - printf("IP_FW_NAT_DEL: %s\n", - "ipfw_nat not present, please load it"); - error = EINVAL; - } - break; - - case IP_FW_NAT_GET_CONFIG: - if (IPFW_NAT_LOADED) - error = ipfw_nat_get_cfg_ptr(sopt); - else { - printf("IP_FW_NAT_GET_CFG: %s\n", - "ipfw_nat not present, please load it"); - error = EINVAL; - } - break; - - case IP_FW_NAT_GET_LOG: - if (IPFW_NAT_LOADED) - error = ipfw_nat_get_log_ptr(sopt); - else { - printf("IP_FW_NAT_GET_LOG: %s\n", - "ipfw_nat not present, please load it"); - error = EINVAL; - } - break; - - default: - printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name); - error = EINVAL; - } - - return (error); -#undef RULE_MAXSIZE -} -#define RULE_MAXSIZE (256*sizeof(u_int32_t)) - -/* Functions to convert rules 7.2 <==> 8.0 */ -static int -convert_rule_to_7(struct ip_fw_rule0 *rule) -{ - /* Used to modify original rule */ - struct ip_fw7 *rule7 = (struct ip_fw7 *)rule; - /* copy of original rule, version 8 */ - struct ip_fw_rule0 *tmp; - - /* Used to copy commands */ - ipfw_insn *ccmd, *dst; - int ll = 0, ccmdlen = 0; - - tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); - if (tmp == NULL) { - return 1; //XXX error - } - bcopy(rule, tmp, RULE_MAXSIZE); - - /* Copy fields */ - //rule7->_pad = tmp->_pad; - rule7->set = tmp->set; - rule7->rulenum = tmp->rulenum; - rule7->cmd_len = tmp->cmd_len; - rule7->act_ofs = tmp->act_ofs; - rule7->next_rule = (struct ip_fw7 *)tmp->next_rule; - rule7->cmd_len = tmp->cmd_len; - rule7->pcnt = tmp->pcnt; - rule7->bcnt = tmp->bcnt; - rule7->timestamp = tmp->timestamp; - - /* Copy commands */ - for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ; - ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { - ccmdlen = F_LEN(ccmd); - - bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); - - if (dst->opcode > O_NAT) - /* O_REASS doesn't exists in 7.2 version, so - * decrement opcode if it is after O_REASS - */ - dst->opcode--; - - if (ccmdlen > ll) { - printf("ipfw: opcode %d size truncated\n", - ccmd->opcode); - return EINVAL; - } - } - free(tmp, M_TEMP); - - return 0; -} - -static int -convert_rule_to_8(struct ip_fw_rule0 *rule) -{ - /* Used to modify original rule */ - struct ip_fw7 *rule7 = (struct ip_fw7 *) rule; - - /* Used to copy commands */ - ipfw_insn *ccmd, *dst; - int ll = 0, ccmdlen = 0; - - /* Copy of original rule */ - struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); - if (tmp == NULL) { - return 1; //XXX error - } - - bcopy(rule7, tmp, RULE_MAXSIZE); - - for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ; - ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { - ccmdlen = F_LEN(ccmd); - - bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); - - if (dst->opcode > O_NAT) - /* O_REASS doesn't exists in 7.2 version, so - * increment opcode if it is after O_REASS - */ - dst->opcode++; - - if (ccmdlen > ll) { - printf("ipfw: opcode %d size truncated\n", - ccmd->opcode); - return EINVAL; - } - } - - rule->_pad = tmp->_pad; - rule->set = tmp->set; - rule->rulenum = tmp->rulenum; - rule->cmd_len = tmp->cmd_len; - rule->act_ofs = tmp->act_ofs; - rule->next_rule = (struct ip_fw *)tmp->next_rule; - rule->cmd_len = tmp->cmd_len; - rule->id = 0; /* XXX see if is ok = 0 */ - rule->pcnt = tmp->pcnt; - rule->bcnt = tmp->bcnt; - rule->timestamp = tmp->timestamp; - - free (tmp, M_TEMP); - return 0; -} - /* * Named object api * @@ -4230,8 +3277,8 @@ void ipfw_init_srv(struct ip_fw_chain *ch) { - - ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT); + ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT, + DEFAULT_OBJHASH_SIZE); ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT, M_IPFW, M_WAITOK | M_ZERO); } @@ -4239,7 +3286,6 @@ void ipfw_destroy_srv(struct ip_fw_chain *ch) { - free(ch->srvstate, M_IPFW); ipfw_objhash_destroy(ch->srvmap); } @@ -4314,7 +3360,6 @@ void ipfw_objhash_bitmap_free(void *idx, int blocks) { - free(idx, M_IPFW); } @@ -4324,19 +3369,19 @@ * Return pointer to new instance. */ struct namedobj_instance * -ipfw_objhash_create(uint32_t items) +ipfw_objhash_create(uint32_t items, size_t hash_size) { struct namedobj_instance *ni; int i; size_t size; size = sizeof(struct namedobj_instance) + - sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE + - sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE; + sizeof(struct namedobjects_head) * hash_size + + sizeof(struct namedobjects_head) * hash_size; ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO); - ni->nn_size = NAMEDOBJ_HASH_SIZE; - ni->nv_size = NAMEDOBJ_HASH_SIZE; + ni->nn_size = hash_size; + ni->nv_size = hash_size; ni->names = (struct namedobjects_head *)(ni +1); ni->values = &ni->names[ni->nn_size]; @@ -4360,7 +3405,6 @@ void ipfw_objhash_destroy(struct namedobj_instance *ni) { - free(ni->idx_mask, M_IPFW); free(ni, M_IPFW); } @@ -4425,7 +3469,7 @@ * Returns pointer to found TLV or NULL. */ ipfw_obj_ntlv * -ipfw_find_name_tlv_type(void *tlvs, int len, uint16_t uidx, uint32_t etlv) +ipfw_find_name_tlv_type(void *tlvs, int len, uint32_t uidx, uint32_t etlv) { ipfw_obj_ntlv *ntlv; uintptr_t pa, pe; @@ -4519,7 +3563,7 @@ } struct named_object * -ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint16_t kidx) +ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint32_t kidx) { struct named_object *no; uint32_t hash; @@ -4645,7 +3689,7 @@ * Returns 0 on success. */ int -ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx) +ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t idx) { u_long *mask; int i, v; @@ -4676,7 +3720,7 @@ * Returns 0 on success. */ int -ipfw_objhash_alloc_idx(void *n, uint16_t *pidx) +ipfw_objhash_alloc_idx(void *n, uint32_t *pidx) { struct namedobj_instance *ni; u_long *mask; diff --git a/sys/netpfil/ipfw/ip_fw_table.h b/sys/netpfil/ipfw/ip_fw_table.h --- a/sys/netpfil/ipfw/ip_fw_table.h +++ b/sys/netpfil/ipfw/ip_fw_table.h @@ -156,10 +156,6 @@ int del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, struct tentry_info *tei, uint8_t flags, uint32_t count); int flush_table(struct ip_fw_chain *ch, struct tid_info *ti); -void ipfw_import_table_value_legacy(uint32_t value, struct table_value *v); -uint32_t ipfw_export_table_value_legacy(struct table_value *v); -int ipfw_get_table_size(struct ip_fw_chain *ch, ip_fw3_opheader *op3, - struct sockopt_data *sd); /* ipfw_table_value.c functions */ struct table_config; @@ -180,7 +176,7 @@ struct rule_check_info *ci); int ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule, uint32_t *bmask); -int ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, +int ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint32_t kidx, struct sockopt_data *sd); void ipfw_unref_rule_tables(struct ip_fw_chain *chain, struct ip_fw *rule); struct namedobj_instance *ipfw_get_table_objhash(struct ip_fw_chain *ch); @@ -190,7 +186,7 @@ uint32_t new_set); void ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t old_set, uint32_t new_set, int mv); -int ipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint16_t kidx, +int ipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint32_t kidx, ta_foreach_f f, void *arg); /* internal functions */ @@ -220,13 +216,5 @@ void del_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts); void rollback_toperation_state(struct ip_fw_chain *ch, void *object); -/* Legacy interfaces */ -int ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, - uint32_t *cnt); -int ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, - uint32_t *cnt); -int ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti, - ipfw_table *tbl); - #endif /* _KERNEL */ #endif /* _IPFW2_TABLE_H */ diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -2,7 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. - * Copyright (c) 2014 Yandex LLC + * Copyright (c) 2014-2024 Yandex LLC * Copyright (c) 2014 Alexander V. Chernikov * * Redistribution and use in source and binary forms, with or without @@ -98,7 +98,7 @@ static void free_table_config(struct namedobj_instance *ni, struct table_config *tc); static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, - char *aname, ipfw_xtable_info *i, uint16_t *pkidx, int ref); + char *aname, ipfw_xtable_info *i, uint32_t *pkidx, int ref); static void link_table(struct ip_fw_chain *ch, struct table_config *tc); static void unlink_table(struct ip_fw_chain *ch, struct table_config *tc); static int find_ref_table(struct ip_fw_chain *ch, struct tid_info *ti, @@ -110,7 +110,6 @@ static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc, ipfw_xtable_info *i); static int dump_table_tentry(void *e, void *arg); -static int dump_table_xentry(void *e, void *arg); static int swap_tables(struct ip_fw_chain *ch, struct tid_info *a, struct tid_info *b); @@ -257,7 +256,7 @@ */ static int create_table_compat(struct ip_fw_chain *ch, struct tid_info *ti, - uint16_t *pkidx) + uint32_t *pkidx) { ipfw_xtable_info xi; int error; @@ -289,7 +288,7 @@ { struct namedobj_instance *ni; struct table_config *tc; - uint16_t kidx; + uint32_t kidx; int error; IPFW_UH_WLOCK_ASSERT(ch); @@ -330,7 +329,7 @@ return (error); tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, kidx); - KASSERT(tc != NULL, ("create_table_compat returned bad idx %d", kidx)); + KASSERT(tc != NULL, ("create_table_compat returned bad idx %u", kidx)); /* OK, now we've got referenced table. */ *ptc = tc; @@ -545,13 +544,12 @@ { struct table_config *tc; struct table_algo *ta; - uint16_t kidx; - int error, first_error, i, rollback; - uint32_t num, numadd; struct tentry_info *ptei; struct tableop_state ts; char ta_buf[TA_BUF_SZ]; caddr_t ta_buf_m, v; + uint32_t kidx, num, numadd; + int error, first_error, i, rollback; memset(&ts, 0, sizeof(ts)); ta = NULL; @@ -717,11 +715,10 @@ struct table_config *tc; struct table_algo *ta; struct tentry_info *ptei; - uint16_t kidx; - int error, first_error, i; - uint32_t num, numdel; char ta_buf[TA_BUF_SZ]; caddr_t ta_buf_m, v; + uint32_t kidx, num, numdel; + int error, first_error, i; /* * Find and reference existing table. @@ -895,61 +892,6 @@ return (error); } -/* - * Adds or deletes record in table. - * Data layout (v0): - * Request: [ ip_fw3_opheader ipfw_table_xentry ] - * - * Returns 0 on success - */ -static int -manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, - struct sockopt_data *sd) -{ - ipfw_table_xentry *xent; - struct tentry_info tei; - struct tid_info ti; - struct table_value v; - int error, hdrlen, read; - - hdrlen = offsetof(ipfw_table_xentry, k); - - /* Check minimum header size */ - if (sd->valsize < (sizeof(*op3) + hdrlen)) - return (EINVAL); - - read = sizeof(ip_fw3_opheader); - - /* Check if xentry len field is valid */ - xent = (ipfw_table_xentry *)(op3 + 1); - if (xent->len < hdrlen || xent->len + read > sd->valsize) - return (EINVAL); - - memset(&tei, 0, sizeof(tei)); - tei.paddr = &xent->k; - tei.masklen = xent->masklen; - ipfw_import_table_value_legacy(xent->value, &v); - tei.pvalue = &v; - /* Old requests compatibility */ - tei.flags = TEI_FLAGS_COMPAT; - if (xent->type == IPFW_TABLE_ADDR) { - if (xent->len - hdrlen == sizeof(in_addr_t)) - tei.subtype = AF_INET; - else - tei.subtype = AF_INET6; - } - - memset(&ti, 0, sizeof(ti)); - ti.uidx = xent->tbl; - ti.type = xent->type; - - error = (op3->opcode == IP_FW_TABLE_XADD) ? - add_table_entry(ch, &ti, &tei, 0, 1) : - del_table_entry(ch, &ti, &tei, 0, 1); - - return (error); -} - /* * Adds or deletes record in table. * Data layout (v1)(current): @@ -968,7 +910,8 @@ ipfw_obj_header *oh; struct tentry_info *ptei, tei, *tei_buf; struct tid_info ti; - int error, i, kidx, read; + uint32_t kidx; + int error, i, read; /* Check minimum header size */ if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv))) @@ -1210,7 +1153,7 @@ char algostate[64], *pstate; struct tableop_state ts; int error, need_gc; - uint16_t kidx; + uint32_t kidx; uint8_t tflags; /* @@ -1500,7 +1443,7 @@ /* Free obj index */ if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0) - printf("Error unlinking kidx %d from table %s\n", + printf("Error unlinking kidx %u from table %s\n", tc->no.kidx, tc->tablename); /* Unref values used in tables while holding UH lock */ @@ -1533,7 +1476,7 @@ if (ntables > IPFW_TABLES_MAX) ntables = IPFW_TABLES_MAX; /* Alight to nearest power of 2 */ - ntables = roundup_pow_of_two(ntables); + ntables = roundup_pow_of_two(ntables); /* Allocate new pointers */ tablestate = malloc(ntables * sizeof(struct table_info), @@ -1595,7 +1538,7 @@ * Lookup table's named object by its @kidx. */ struct named_object * -ipfw_objhash_lookup_table_kidx(struct ip_fw_chain *ch, uint16_t kidx) +ipfw_objhash_lookup_table_kidx(struct ip_fw_chain *ch, uint32_t kidx) { return (ipfw_objhash_lookup_kidx(CHAIN_TO_NI(ch), kidx)); @@ -1606,7 +1549,7 @@ * On success return its @kidx. */ int -ipfw_ref_table(struct ip_fw_chain *ch, ipfw_obj_ntlv *ntlv, uint16_t *kidx) +ipfw_ref_table(struct ip_fw_chain *ch, ipfw_obj_ntlv *ntlv, uint32_t *kidx) { struct tid_info ti; struct table_config *tc; @@ -1629,7 +1572,7 @@ } void -ipfw_unref_table(struct ip_fw_chain *ch, uint16_t kidx) +ipfw_unref_table(struct ip_fw_chain *ch, uint32_t kidx) { struct namedobj_instance *ni; @@ -1638,7 +1581,7 @@ IPFW_UH_WLOCK_ASSERT(ch); ni = CHAIN_TO_NI(ch); no = ipfw_objhash_lookup_kidx(ni, kidx); - KASSERT(no != NULL, ("Table with index %d not found", kidx)); + KASSERT(no != NULL, ("Table with index %u not found", kidx)); no->refcnt--; } @@ -1649,7 +1592,7 @@ * Returns 1 if key was found. */ int -ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, +ipfw_lookup_table(struct ip_fw_chain *ch, uint32_t tbl, uint16_t plen, void *paddr, uint32_t *val) { struct table_info *ti; @@ -1853,12 +1796,12 @@ */ static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, - char *aname, ipfw_xtable_info *i, uint16_t *pkidx, int compat) + char *aname, ipfw_xtable_info *i, uint32_t *pkidx, int compat) { struct namedobj_instance *ni; struct table_config *tc, *tc_new, *tmp; struct table_algo *ta; - uint16_t kidx; + uint32_t kidx; ni = CHAIN_TO_NI(ch); @@ -1958,7 +1901,7 @@ * Returns 0 on success. */ int -ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, +ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint32_t kidx, struct sockopt_data *sd) { struct namedobj_instance *ni; @@ -1991,7 +1934,6 @@ uint16_t uidx; int error; uint32_t size; - ipfw_table_entry *ent; ta_foreach_f *f; void *farg; ipfw_obj_tentry tent; @@ -2199,202 +2141,6 @@ return (da.error); } -/* - * Dumps all table data - * Data layout (version 0)(legacy): - * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE() - * Reply: [ ipfw_xtable ipfw_table_xentry x N ] - * - * Returns 0 on success - */ -static int -dump_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, - struct sockopt_data *sd) -{ - ipfw_xtable *xtbl; - struct tid_info ti; - struct table_config *tc; - struct table_algo *ta; - struct dump_args da; - size_t sz, count; - - xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable)); - if (xtbl == NULL) - return (EINVAL); - - memset(&ti, 0, sizeof(ti)); - ti.uidx = xtbl->tbl; - - IPFW_UH_RLOCK(ch); - if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { - IPFW_UH_RUNLOCK(ch); - return (0); - } - count = table_get_count(ch, tc); - sz = count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable); - - xtbl->cnt = count; - xtbl->size = sz; - xtbl->type = tc->no.subtype; - xtbl->tbl = ti.uidx; - - if (sd->valsize < sz) { - /* - * Submitted buffer size is not enough. - * WE've already filled in @i structure with - * relevant table info including size, so we - * can return. Buffer will be flushed automatically. - */ - IPFW_UH_RUNLOCK(ch); - return (ENOMEM); - } - - /* Do the actual dump in eXtended format */ - memset(&da, 0, sizeof(da)); - da.ch = ch; - da.ti = KIDX_TO_TI(ch, tc->no.kidx); - da.tc = tc; - da.sd = sd; - - ta = tc->ta; - - ta->foreach(tc->astate, da.ti, dump_table_xentry, &da); - IPFW_UH_RUNLOCK(ch); - - return (0); -} - -/* - * Legacy function to retrieve number of items in table. - */ -static int -get_table_size(struct ip_fw_chain *ch, ip_fw3_opheader *op3, - struct sockopt_data *sd) -{ - uint32_t *tbl; - struct tid_info ti; - size_t sz; - int error; - - sz = sizeof(*op3) + sizeof(uint32_t); - op3 = (ip_fw3_opheader *)ipfw_get_sopt_header(sd, sz); - if (op3 == NULL) - return (EINVAL); - - tbl = (uint32_t *)(op3 + 1); - memset(&ti, 0, sizeof(ti)); - ti.uidx = *tbl; - IPFW_UH_RLOCK(ch); - error = ipfw_count_xtable(ch, &ti, tbl); - IPFW_UH_RUNLOCK(ch); - return (error); -} - -/* - * Legacy IP_FW_TABLE_GETSIZE handler - */ -int -ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) -{ - struct table_config *tc; - - if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) - return (ESRCH); - *cnt = table_get_count(ch, tc); - return (0); -} - -/* - * Legacy IP_FW_TABLE_XGETSIZE handler - */ -int -ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) -{ - struct table_config *tc; - uint32_t count; - - if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) { - *cnt = 0; - return (0); /* 'table all list' requires success */ - } - - count = table_get_count(ch, tc); - *cnt = count * sizeof(ipfw_table_xentry); - if (count > 0) - *cnt += sizeof(ipfw_xtable); - return (0); -} - -static int -dump_table_entry(void *e, void *arg) -{ - struct dump_args *da; - struct table_config *tc; - struct table_algo *ta; - ipfw_table_entry *ent; - struct table_value *pval; - int error; - - da = (struct dump_args *)arg; - - tc = da->tc; - ta = tc->ta; - - /* Out of memory, returning */ - if (da->cnt == da->size) - return (1); - ent = da->ent++; - ent->tbl = da->uidx; - da->cnt++; - - error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent); - if (error != 0) - return (error); - - ent->addr = da->tent.k.addr.s_addr; - ent->masklen = da->tent.masklen; - pval = get_table_value(da->ch, da->tc, da->tent.v.kidx); - ent->value = ipfw_export_table_value_legacy(pval); - - return (0); -} - -/* - * Dumps table in pre-8.1 legacy format. - */ -int -ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti, - ipfw_table *tbl) -{ - struct table_config *tc; - struct table_algo *ta; - struct dump_args da; - - tbl->cnt = 0; - - if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) - return (0); /* XXX: We should return ESRCH */ - - ta = tc->ta; - - /* This dump format supports IPv4 only */ - if (tc->no.subtype != IPFW_TABLE_ADDR) - return (0); - - memset(&da, 0, sizeof(da)); - da.ch = ch; - da.ti = KIDX_TO_TI(ch, tc->no.kidx); - da.tc = tc; - da.ent = &tbl->ent[0]; - da.size = tbl->size; - - tbl->cnt = 0; - ta->foreach(tc->astate, da.ti, dump_table_entry, &da); - tbl->cnt = da.cnt; - - return (0); -} - /* * Dumps table entry in eXtended format (v1)(current). */ @@ -2432,52 +2178,6 @@ return (0); } -/* - * Dumps table entry in eXtended format (v0). - */ -static int -dump_table_xentry(void *e, void *arg) -{ - struct dump_args *da; - struct table_config *tc; - struct table_algo *ta; - ipfw_table_xentry *xent; - ipfw_obj_tentry *tent; - struct table_value *pval; - int error; - - da = (struct dump_args *)arg; - - tc = da->tc; - ta = tc->ta; - - xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent)); - /* Out of memory, returning */ - if (xent == NULL) - return (1); - xent->len = sizeof(ipfw_table_xentry); - xent->tbl = da->uidx; - - memset(&da->tent, 0, sizeof(da->tent)); - tent = &da->tent; - error = ta->dump_tentry(tc->astate, da->ti, e, tent); - if (error != 0) - return (error); - - /* Convert current format to previous one */ - xent->masklen = tent->masklen; - pval = get_table_value(da->ch, da->tc, da->tent.v.kidx); - xent->value = ipfw_export_table_value_legacy(pval); - /* Apply some hacks */ - if (tc->no.subtype == IPFW_TABLE_ADDR && tent->subtype == AF_INET) { - xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr; - xent->flags = IPFW_TCF_INET; - } else - memcpy(&xent->k, &tent->k, sizeof(xent->k)); - - return (0); -} - /* * Helper function to export table algo data * to tentry format before calling user function. @@ -2510,7 +2210,7 @@ * Allow external consumers to read table entries in standard format. */ int -ipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint16_t kidx, +ipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint32_t kidx, ta_foreach_f *f, void *arg) { struct namedobj_instance *ni; @@ -2540,7 +2240,7 @@ /* * Table algorithms - */ + */ /* * Finds algorithm by index, table type or supplied name. @@ -2720,90 +2420,78 @@ } static int -classify_srcdst(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +classify_srcdst(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) { + ipfw_insn_table *cmd; + /* Basic IPv4/IPv6 or u32 lookups */ - *puidx = cmd->arg1; - /* Assume ADDR by default */ - *ptype = IPFW_TABLE_ADDR; - int v; - - if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) { - /* - * generic lookup. The key must be - * in 32bit big-endian format. - */ - v = ((ipfw_insn_u32 *)cmd)->d[1]; - switch (v) { - case LOOKUP_DST_IP: - case LOOKUP_SRC_IP: - break; - case LOOKUP_DST_PORT: - case LOOKUP_SRC_PORT: - case LOOKUP_UID: - case LOOKUP_JAIL: - case LOOKUP_DSCP: - case LOOKUP_MARK: - *ptype = IPFW_TABLE_NUMBER; - break; - case LOOKUP_DST_MAC: - case LOOKUP_SRC_MAC: - *ptype = IPFW_TABLE_MAC; - break; - } + cmd = insntod(cmd0, table); + *puidx = cmd->kidx; + switch(cmd0->arg1) { + case LOOKUP_DST_IP: + case LOOKUP_SRC_IP: + default: + /* IPv4 src/dst */ + *ptype = IPFW_TABLE_ADDR; + break; + case LOOKUP_DST_PORT: + case LOOKUP_SRC_PORT: + case LOOKUP_UID: + case LOOKUP_JAIL: + case LOOKUP_DSCP: + case LOOKUP_MARK: + case LOOKUP_RULENUM: + *ptype = IPFW_TABLE_NUMBER; + break; + case LOOKUP_DST_MAC: + case LOOKUP_SRC_MAC: + *ptype = IPFW_TABLE_MAC; + break; } - return (0); } static int -classify_via(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +classify_via(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) { ipfw_insn_if *cmdif; /* Interface table, possibly */ - cmdif = (ipfw_insn_if *)cmd; + cmdif = insntod(cmd0, if); if (cmdif->name[0] != '\1') return (1); *ptype = IPFW_TABLE_INTERFACE; - *puidx = cmdif->p.kidx; - + *puidx = cmdif->p.kidx; /* XXXAE */ return (0); } static int -classify_flow(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +classify_flow(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) { - - *puidx = cmd->arg1; + *puidx = insntod(cmd0, table)->kidx; *ptype = IPFW_TABLE_FLOW; - return (0); } static int -classify_mac_lookup(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +classify_mac_lookup(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) { - *puidx = cmd->arg1; + *puidx = insntod(cmd0, table)->kidx; *ptype = IPFW_TABLE_MAC; return (0); } static void -update_arg1(ipfw_insn *cmd, uint16_t idx) +update_kidx(ipfw_insn *cmd0, uint32_t idx) { - - cmd->arg1 = idx; + insntod(cmd0, table)->kidx = idx; } static void -update_via(ipfw_insn *cmd, uint16_t idx) +update_via(ipfw_insn *cmd0, uint32_t idx) { - ipfw_insn_if *cmdif; - - cmdif = (ipfw_insn_if *)cmd; - cmdif->p.kidx = idx; + insntod(cmd0, if)->p.kidx = idx; } static int @@ -2825,7 +2513,7 @@ /* XXX: sets-sets! */ static struct named_object * -table_findbykidx(struct ip_fw_chain *ch, uint16_t idx) +table_findbykidx(struct ip_fw_chain *ch, uint32_t idx) { struct namedobj_instance *ni; struct table_config *tc; @@ -2833,13 +2521,13 @@ IPFW_UH_WLOCK_ASSERT(ch); ni = CHAIN_TO_NI(ch); tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, idx); - KASSERT(tc != NULL, ("Table with index %d not found", idx)); + KASSERT(tc != NULL, ("Table with index %u not found", idx)); return (&tc->no); } static int -table_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, +table_manage_sets(struct ip_fw_chain *ch, uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) { @@ -2884,7 +2572,7 @@ * so it should be called first. */ static int -table_manage_sets_all(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, +table_manage_sets_all(struct ip_fw_chain *ch, uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) { @@ -2914,7 +2602,7 @@ .opcode = O_IP_SRC_LOOKUP, .etlv = IPFW_TLV_TBL_NAME, .classifier = classify_srcdst, - .update = update_arg1, + .update = update_kidx, .find_byname = table_findbyname, .find_bykidx = table_findbykidx, .create_object = create_table_compat, @@ -2924,7 +2612,7 @@ .opcode = O_IP_DST_LOOKUP, .etlv = IPFW_TLV_TBL_NAME, .classifier = classify_srcdst, - .update = update_arg1, + .update = update_kidx, .find_byname = table_findbyname, .find_bykidx = table_findbykidx, .create_object = create_table_compat, @@ -2934,7 +2622,7 @@ .opcode = O_IP_FLOW_LOOKUP, .etlv = IPFW_TLV_TBL_NAME, .classifier = classify_flow, - .update = update_arg1, + .update = update_kidx, .find_byname = table_findbyname, .find_bykidx = table_findbykidx, .create_object = create_table_compat, @@ -2944,7 +2632,7 @@ .opcode = O_MAC_SRC_LOOKUP, .etlv = IPFW_TLV_TBL_NAME, .classifier = classify_mac_lookup, - .update = update_arg1, + .update = update_kidx, .find_byname = table_findbyname, .find_bykidx = table_findbykidx, .create_object = create_table_compat, @@ -2954,7 +2642,7 @@ .opcode = O_MAC_DST_LOOKUP, .etlv = IPFW_TLV_TBL_NAME, .classifier = classify_mac_lookup, - .update = update_arg1, + .update = update_kidx, .find_byname = table_findbyname, .find_bykidx = table_findbykidx, .create_object = create_table_compat, @@ -3019,7 +2707,7 @@ struct ip_fw *rule; ipfw_insn *cmd; int cmdlen, i, l; - uint16_t kidx; + uint32_t kidx; uint8_t subtype; IPFW_UH_WLOCK(ch); @@ -3277,22 +2965,18 @@ } static struct ipfw_sopt_handler scodes[] = { - { IP_FW_TABLE_XCREATE, 0, HDIR_SET, create_table }, - { IP_FW_TABLE_XDESTROY, 0, HDIR_SET, flush_table_v0 }, - { IP_FW_TABLE_XFLUSH, 0, HDIR_SET, flush_table_v0 }, - { IP_FW_TABLE_XMODIFY, 0, HDIR_BOTH, modify_table }, - { IP_FW_TABLE_XINFO, 0, HDIR_GET, describe_table }, - { IP_FW_TABLES_XLIST, 0, HDIR_GET, list_tables }, - { IP_FW_TABLE_XLIST, 0, HDIR_GET, dump_table_v0 }, - { IP_FW_TABLE_XLIST, 1, HDIR_GET, dump_table_v1 }, - { IP_FW_TABLE_XADD, 0, HDIR_BOTH, manage_table_ent_v0 }, - { IP_FW_TABLE_XADD, 1, HDIR_BOTH, manage_table_ent_v1 }, - { IP_FW_TABLE_XDEL, 0, HDIR_BOTH, manage_table_ent_v0 }, - { IP_FW_TABLE_XDEL, 1, HDIR_BOTH, manage_table_ent_v1 }, - { IP_FW_TABLE_XFIND, 0, HDIR_GET, find_table_entry }, - { IP_FW_TABLE_XSWAP, 0, HDIR_SET, swap_table }, - { IP_FW_TABLES_ALIST, 0, HDIR_GET, list_table_algo }, - { IP_FW_TABLE_XGETSIZE, 0, HDIR_GET, get_table_size }, + { IP_FW_TABLE_XCREATE, IP_FW3_OPVER, HDIR_SET, create_table }, + { IP_FW_TABLE_XDESTROY, IP_FW3_OPVER, HDIR_SET, flush_table_v0 }, + { IP_FW_TABLE_XFLUSH, IP_FW3_OPVER, HDIR_SET, flush_table_v0 }, + { IP_FW_TABLE_XMODIFY, IP_FW3_OPVER, HDIR_BOTH, modify_table }, + { IP_FW_TABLE_XINFO, IP_FW3_OPVER, HDIR_GET, describe_table }, + { IP_FW_TABLES_XLIST, IP_FW3_OPVER, HDIR_GET, list_tables }, + { IP_FW_TABLE_XLIST, IP_FW3_OPVER, HDIR_GET, dump_table_v1 }, + { IP_FW_TABLE_XADD, IP_FW3_OPVER, HDIR_BOTH, manage_table_ent_v1 }, + { IP_FW_TABLE_XDEL, IP_FW3_OPVER, HDIR_BOTH, manage_table_ent_v1 }, + { IP_FW_TABLE_XFIND, IP_FW3_OPVER, HDIR_GET, find_table_entry }, + { IP_FW_TABLE_XSWAP, IP_FW3_OPVER, HDIR_SET, swap_table }, + { IP_FW_TABLES_ALIST, IP_FW3_OPVER, HDIR_GET, list_table_algo }, }; static int @@ -3348,7 +3032,8 @@ M_IPFW, M_WAITOK | M_ZERO); tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO); - tcfg->namehash = ipfw_objhash_create(V_fw_tables_max); + tcfg->namehash = ipfw_objhash_create(V_fw_tables_max, + DEFAULT_OBJHASH_SIZE); ch->tblcfg = tcfg; ipfw_table_value_init(ch, first); diff --git a/sys/netpfil/ipfw/ip_fw_table_algo.c b/sys/netpfil/ipfw/ip_fw_table_algo.c --- a/sys/netpfil/ipfw/ip_fw_table_algo.c +++ b/sys/netpfil/ipfw/ip_fw_table_algo.c @@ -2104,7 +2104,7 @@ icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO); - icfg->ii = ipfw_objhash_create(DEFAULT_IFIDX_SIZE); + icfg->ii = ipfw_objhash_create(DEFAULT_IFIDX_SIZE, DEFAULT_OBJHASH_SIZE); icfg->size = DEFAULT_IFIDX_SIZE; icfg->main_ptr = malloc(sizeof(struct ifidx) * icfg->size, M_IPFW, M_WAITOK | M_ZERO); @@ -3195,8 +3195,7 @@ struct fhashentry *ent; struct fhashentry4 *m4; struct ipfw_flow_id *id; - uint32_t hsize; - uint16_t hash; + uint32_t hash, hsize; id = (struct ipfw_flow_id *)key; head = (struct fhashbhead *)ti->state; @@ -4018,9 +4017,9 @@ struct mac_radix_entry { struct radix_node rn[2]; + struct sa_mac sa; uint32_t value; uint8_t masklen; - struct sa_mac sa; }; struct mac_radix_cfg { @@ -4306,7 +4305,7 @@ if (tent->subtype == AF_LINK) { struct sa_mac sa; KEY_LEN(sa) = KEY_LEN_MAC; - memcpy(tent->k.mac, sa.mac_addr.octet, ETHER_ADDR_LEN); + memcpy(sa.mac_addr.octet, tent->k.mac, ETHER_ADDR_LEN); rnh = (struct radix_node_head *)ti->state; e = rnh->rnh_matchaddr(&sa, &rnh->rh); } diff --git a/sys/netpfil/ipfw/ip_fw_table_value.c b/sys/netpfil/ipfw/ip_fw_table_value.c --- a/sys/netpfil/ipfw/ip_fw_table_value.c +++ b/sys/netpfil/ipfw/ip_fw_table_value.c @@ -65,7 +65,7 @@ struct sockopt_data *sd); static struct ipfw_sopt_handler scodes[] = { - { IP_FW_TABLE_VLIST, 0, HDIR_GET, list_table_values }, + { IP_FW_TABLE_VLIST, IP_FW3_OPVER, HDIR_GET, list_table_values }, }; #define CHAIN_TO_VI(chain) (CHAIN_TO_TCFG(chain)->valhash) @@ -76,6 +76,7 @@ struct table_value *pval; /* Pointer to real table value */ }; #define VALDATA_START_SIZE 64 /* Allocate 64-items array by default */ +#define VALDATA_HASH_SIZE 65536 struct vdump_args { struct ip_fw_chain *ch; @@ -361,10 +362,10 @@ */ static int alloc_table_vidx(struct ip_fw_chain *ch, struct tableop_state *ts, - struct namedobj_instance *vi, uint16_t *pvidx, uint8_t flags) + struct namedobj_instance *vi, uint32_t *pvidx, uint8_t flags) { int error, vlimit; - uint16_t vidx; + uint32_t vidx; IPFW_UH_WLOCK_ASSERT(ch); @@ -473,8 +474,7 @@ struct namedobj_instance *vi; struct table_config *tc; struct tentry_info *tei, *ptei; - uint32_t count, vlimit; - uint16_t vidx; + uint32_t count, vidx, vlimit; struct table_val_link *ptv; struct table_value tval, *pval; @@ -595,42 +595,6 @@ return (0); } -/* - * Compatibility function used to import data from old - * IP_FW_TABLE_ADD / IP_FW_TABLE_XADD opcodes. - */ -void -ipfw_import_table_value_legacy(uint32_t value, struct table_value *v) -{ - - memset(v, 0, sizeof(*v)); - v->tag = value; - v->pipe = value; - v->divert = value; - v->skipto = value; - v->netgraph = value; - v->fib = value; - v->nat = value; - v->nh4 = value; /* host format */ - v->dscp = value; - v->limit = value; - v->mark = value; -} - -/* - * Export data to legacy table dumps opcodes. - */ -uint32_t -ipfw_export_table_value_legacy(struct table_value *v) -{ - - /* - * TODO: provide more compatibility depending on - * vmask value. - */ - return (v->tag); -} - /* * Imports table value from current userland format. * Saves value in kernel format to the same place. @@ -775,7 +739,7 @@ tcfg = ch->tblcfg; tcfg->val_size = VALDATA_START_SIZE; - tcfg->valhash = ipfw_objhash_create(tcfg->val_size); + tcfg->valhash = ipfw_objhash_create(tcfg->val_size, VALDATA_HASH_SIZE); ipfw_objhash_set_funcs(tcfg->valhash, hash_table_value, cmp_table_value); diff --git a/sys/netpfil/ipfw/nat64/ip_fw_nat64.h b/sys/netpfil/ipfw/nat64/ip_fw_nat64.h --- a/sys/netpfil/ipfw/nat64/ip_fw_nat64.h +++ b/sys/netpfil/ipfw/nat64/ip_fw_nat64.h @@ -55,4 +55,52 @@ int nat64clat_init(struct ip_fw_chain *ch, int first); void nat64clat_uninit(struct ip_fw_chain *ch, int last); +#define NAT64_DEFINE_OPCODE_REWRITER(mod, name, ops) \ +static int \ +mod ## _classify(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) \ +{ \ + ipfw_insn *icmd; \ + icmd = cmd0 - F_LEN(cmd0); \ + if (icmd->opcode != O_EXTERNAL_ACTION || \ + insntod(icmd, kidx)->kidx != V_ ## mod ## _eid) \ + return (1); \ + *puidx = insntod(cmd0, kidx)->kidx; \ + *ptype = 0; \ + return (0); \ +} \ +static void \ +mod ## _update_kidx(ipfw_insn *cmd0, uint32_t idx) \ +{ \ + insntod(cmd0, kidx)->kidx = idx; \ +} \ +static int \ +mod ## _findbyname(struct ip_fw_chain *ch, struct tid_info *ti, \ + struct named_object **pno) \ +{ \ + return (ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, \ + IPFW_TLV_## name ## _NAME, pno)); \ +} \ +static struct named_object * \ +mod ## _findbykidx(struct ip_fw_chain *ch, uint32_t idx) \ +{ \ + struct namedobj_instance *ni; \ + struct named_object *no; \ + IPFW_UH_WLOCK_ASSERT(ch); \ + ni = CHAIN_TO_SRV(ch); \ + no = ipfw_objhash_lookup_kidx(ni, idx); \ + KASSERT(no != NULL, ("NAT with index %u not found", idx)); \ + return (no); \ +} \ +static struct opcode_obj_rewrite ops[] = { \ + { \ + .opcode = O_EXTERNAL_INSTANCE, \ + .etlv = IPFW_TLV_EACTION /* just show it isn't table */,\ + .classifier = mod ## _classify, \ + .update = mod ## _update_kidx, \ + .find_byname = mod ## _findbyname, \ + .find_bykidx = mod ## _findbykidx, \ + .manage_sets = mod ## _manage_sets, \ + }, \ +} + #endif /* _IP_FW_NAT64_H_ */ diff --git a/sys/netpfil/ipfw/nat64/nat64clat.h b/sys/netpfil/ipfw/nat64/nat64clat.h --- a/sys/netpfil/ipfw/nat64/nat64clat.h +++ b/sys/netpfil/ipfw/nat64/nat64clat.h @@ -42,7 +42,7 @@ char name[64]; }; -VNET_DECLARE(uint16_t, nat64clat_eid); +VNET_DECLARE(uint32_t, nat64clat_eid); #define V_nat64clat_eid VNET(nat64clat_eid) #define IPFW_TLV_NAT64CLAT_NAME IPFW_TLV_EACTION_NAME(V_nat64clat_eid) diff --git a/sys/netpfil/ipfw/nat64/nat64clat.c b/sys/netpfil/ipfw/nat64/nat64clat.c --- a/sys/netpfil/ipfw/nat64/nat64clat.c +++ b/sys/netpfil/ipfw/nat64/nat64clat.c @@ -59,7 +59,7 @@ #include "nat64clat.h" #define NAT64_LOOKUP(chain, cmd) \ - (struct nat64clat_cfg *)SRV_OBJECT((chain), (cmd)->arg1) + (struct nat64clat_cfg *)SRV_OBJECT((chain), insntod(cmd, kidx)->kidx) static void nat64clat_log(struct pfloghdr *plog, struct mbuf *m, sa_family_t family, @@ -68,7 +68,7 @@ static uint32_t pktid = 0; memset(plog, 0, sizeof(*plog)); - plog->length = PFLOG_HDRLEN; + plog->length = PFLOG_REAL_HDRLEN; plog->af = family; plog->action = PF_NAT; plog->dir = PF_IN; @@ -210,9 +210,9 @@ IPFW_RLOCK_ASSERT(chain); *done = 0; /* try next rule if not matched */ - icmd = cmd + 1; + icmd = cmd + F_LEN(cmd); if (cmd->opcode != O_EXTERNAL_ACTION || - cmd->arg1 != V_nat64clat_eid || + insntod(cmd, kidx)->kidx != V_nat64clat_eid || icmd->opcode != O_EXTERNAL_INSTANCE || (cfg = NAT64_LOOKUP(chain, icmd)) == NULL) return (0); diff --git a/sys/netpfil/ipfw/nat64/nat64clat_control.c b/sys/netpfil/ipfw/nat64/nat64clat_control.c --- a/sys/netpfil/ipfw/nat64/nat64clat_control.c +++ b/sys/netpfil/ipfw/nat64/nat64clat_control.c @@ -59,7 +59,7 @@ #include "nat64clat.h" -VNET_DEFINE(uint16_t, nat64clat_eid) = 0; +VNET_DEFINE(uint32_t, nat64clat_eid) = 0; static struct nat64clat_cfg *nat64clat_alloc_config(const char *name, uint8_t set); @@ -484,81 +484,23 @@ } static struct ipfw_sopt_handler scodes[] = { - { IP_FW_NAT64CLAT_CREATE, 0, HDIR_SET, nat64clat_create }, - { IP_FW_NAT64CLAT_DESTROY,0, HDIR_SET, nat64clat_destroy }, - { IP_FW_NAT64CLAT_CONFIG, 0, HDIR_BOTH, nat64clat_config }, - { IP_FW_NAT64CLAT_LIST, 0, HDIR_GET, nat64clat_list }, - { IP_FW_NAT64CLAT_STATS, 0, HDIR_GET, nat64clat_stats }, - { IP_FW_NAT64CLAT_RESET_STATS,0, HDIR_SET, nat64clat_reset_stats }, + { IP_FW_NAT64CLAT_CREATE, IP_FW3_OPVER, HDIR_SET, nat64clat_create }, + { IP_FW_NAT64CLAT_DESTROY, IP_FW3_OPVER, HDIR_SET, nat64clat_destroy }, + { IP_FW_NAT64CLAT_CONFIG, IP_FW3_OPVER, HDIR_BOTH, nat64clat_config }, + { IP_FW_NAT64CLAT_LIST, IP_FW3_OPVER, HDIR_GET, nat64clat_list }, + { IP_FW_NAT64CLAT_STATS, IP_FW3_OPVER, HDIR_GET, nat64clat_stats }, + { IP_FW_NAT64CLAT_RESET_STATS, IP_FW3_OPVER, HDIR_SET, nat64clat_reset_stats }, }; static int -nat64clat_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) -{ - ipfw_insn *icmd; - - icmd = cmd - 1; - if (icmd->opcode != O_EXTERNAL_ACTION || - icmd->arg1 != V_nat64clat_eid) - return (1); - - *puidx = cmd->arg1; - *ptype = 0; - return (0); -} - -static void -nat64clat_update_arg1(ipfw_insn *cmd, uint16_t idx) -{ - - cmd->arg1 = idx; -} - -static int -nat64clat_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, - struct named_object **pno) -{ - int err; - - err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, - IPFW_TLV_NAT64CLAT_NAME, pno); - return (err); -} - -static struct named_object * -nat64clat_findbykidx(struct ip_fw_chain *ch, uint16_t idx) -{ - struct namedobj_instance *ni; - struct named_object *no; - - IPFW_UH_WLOCK_ASSERT(ch); - ni = CHAIN_TO_SRV(ch); - no = ipfw_objhash_lookup_kidx(ni, idx); - KASSERT(no != NULL, ("NAT with index %d not found", idx)); - - return (no); -} - -static int -nat64clat_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, +nat64clat_manage_sets(struct ip_fw_chain *ch, uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) { - return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64CLAT_NAME, - set, new_set, cmd)); + return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), + IPFW_TLV_NAT64CLAT_NAME, set, new_set, cmd)); } - -static struct opcode_obj_rewrite opcodes[] = { - { - .opcode = O_EXTERNAL_INSTANCE, - .etlv = IPFW_TLV_EACTION /* just show it isn't table */, - .classifier = nat64clat_classify, - .update = nat64clat_update_arg1, - .find_byname = nat64clat_findbyname, - .find_bykidx = nat64clat_findbykidx, - .manage_sets = nat64clat_manage_sets, - }, -}; +NAT64_DEFINE_OPCODE_REWRITER(nat64clat, NAT64CLAT, opcodes); static int destroy_config_cb(struct namedobj_instance *ni, struct named_object *no, diff --git a/sys/netpfil/ipfw/nat64/nat64lsn.h b/sys/netpfil/ipfw/nat64/nat64lsn.h --- a/sys/netpfil/ipfw/nat64/nat64lsn.h +++ b/sys/netpfil/ipfw/nat64/nat64lsn.h @@ -1,9 +1,9 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2015-2019 Yandex LLC + * Copyright (c) 2015-2020 Yandex LLC * Copyright (c) 2015 Alexander V. Chernikov - * Copyright (c) 2015-2019 Andrey V. Elsukov + * Copyright (c) 2015-2020 Andrey V. Elsukov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -64,13 +64,12 @@ #define ISSET64(mask, bit) ((mask) & ((uint64_t)1 << (bit))) #define ISSET32(mask, bit) ((mask) & ((uint32_t)1 << (bit))) struct nat64lsn_pg { - CK_SLIST_ENTRY(nat64lsn_pg) entries; - uint16_t base_port; uint16_t timestamp; uint8_t proto; uint8_t chunks_count; - uint8_t spare[2]; + uint16_t flags; +#define NAT64LSN_DEADPG 1 union { uint64_t freemask64; @@ -83,6 +82,11 @@ struct nat64lsn_states_chunk *states; struct nat64lsn_states_chunk **states_chunk; }; + /* + * An alias object holds chain of all allocated PGs. + * The chain is used mostly by expiration code. + */ + CK_SLIST_ENTRY(nat64lsn_pg) entries; }; #define CHUNK_BY_FADDR(p, a) ((a) & ((p)->chunks_count - 1)) @@ -123,28 +127,39 @@ struct mtx lock; in_addr_t addr; /* host byte order */ uint32_t hosts_count; - uint32_t portgroups_count; - uint32_t tcp_chunkmask; - uint32_t udp_chunkmask; - uint32_t icmp_chunkmask; + uint16_t timestamp; + uint16_t tcp_pgcount; + uint16_t udp_pgcount; + uint16_t icmp_pgcount; + /* + * We keep PGs in chunks by 32 PGs in each. + * Each chunk allocated by demand, and then corresponding bit + * is set in chunkmask. + * + * Also we keep last used PG's index for each protocol. + * pgidx / 32 = index of pgchunk; + * pgidx % 32 = index of pgptr in pgchunk. + */ + uint32_t tcp_chunkmask; uint32_t tcp_pgidx; + + uint32_t udp_chunkmask; uint32_t udp_pgidx; - uint32_t icmp_pgidx; - uint16_t timestamp; - uint16_t spare; + uint32_t icmp_chunkmask; + uint32_t icmp_pgidx; + /* + * Each pgchunk keeps 32 pointers to PGs. If pgptr pointer is + * valid, we have corresponding bit set in the pgmask. + */ uint32_t tcp_pgmask[32]; uint32_t udp_pgmask[32]; uint32_t icmp_pgmask[32]; + struct nat64lsn_pgchunk *tcp[32]; struct nat64lsn_pgchunk *udp[32]; struct nat64lsn_pgchunk *icmp[32]; - - /* pointer to PG that can be used for faster state allocation */ - struct nat64lsn_pg *tcp_pg; - struct nat64lsn_pg *udp_pg; - struct nat64lsn_pg *icmp_pg; }; #define ALIAS_LOCK_INIT(p) \ mtx_init(&(p)->lock, "alias_lock", NULL, MTX_DEF) @@ -177,7 +192,7 @@ #define HOST_LOCK(p) mtx_lock(&(p)->lock) #define HOST_UNLOCK(p) mtx_unlock(&(p)->lock) -VNET_DECLARE(uint16_t, nat64lsn_eid); +VNET_DECLARE(uint32_t, nat64lsn_eid); #define V_nat64lsn_eid VNET(nat64lsn_eid) #define IPFW_TLV_NAT64LSN_NAME IPFW_TLV_EACTION_NAME(V_nat64lsn_eid) @@ -189,8 +204,6 @@ STAILQ_HEAD(nat64lsn_job_head, nat64lsn_job_item); struct nat64lsn_cfg { - struct named_object no; - struct nat64lsn_hosts_slist *hosts_hash; struct nat64lsn_alias *aliases; /* array of aliases */ @@ -216,7 +229,8 @@ uint16_t st_icmp_ttl; /* ICMP expire */ struct nat64_config base; -#define NAT64LSN_FLAGSMASK (NAT64_LOG | NAT64_ALLOW_PRIVATE) +#define NAT64LSN_FLAGSMASK (NAT64_LOG | NAT64_ALLOW_PRIVATE | \ + NAT64LSN_ALLOW_SWAPCONF) #define NAT64LSN_ANYPREFIX 0x00000100 struct mtx periodic_lock; @@ -228,6 +242,12 @@ char name[64]; /* Nat instance name */ }; +struct nat64lsn_instance { + struct named_object no; + struct nat64lsn_cfg *cfg; + char name[64]; /* Nat instance name */ +}; + /* CFG_LOCK protects cfg->hosts_hash from modification */ #define CFG_LOCK_INIT(p) \ mtx_init(&(p)->lock, "cfg_lock", NULL, MTX_DEF) @@ -241,9 +261,11 @@ #define CALLOUT_LOCK(p) mtx_lock(&(p)->periodic_lock) #define CALLOUT_UNLOCK(p) mtx_unlock(&(p)->periodic_lock) -struct nat64lsn_cfg *nat64lsn_init_instance(struct ip_fw_chain *ch, +MALLOC_DECLARE(M_NAT64LSN); + +struct nat64lsn_cfg *nat64lsn_init_config(struct ip_fw_chain *ch, in_addr_t prefix, int plen); -void nat64lsn_destroy_instance(struct nat64lsn_cfg *cfg); +void nat64lsn_destroy_config(struct nat64lsn_cfg *cfg); void nat64lsn_start_instance(struct nat64lsn_cfg *cfg); void nat64lsn_init_internal(void); void nat64lsn_uninit_internal(void); diff --git a/sys/netpfil/ipfw/nat64/nat64lsn.c b/sys/netpfil/ipfw/nat64/nat64lsn.c --- a/sys/netpfil/ipfw/nat64/nat64lsn.c +++ b/sys/netpfil/ipfw/nat64/nat64lsn.c @@ -1,9 +1,9 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2015-2019 Yandex LLC + * Copyright (c) 2015-2020 Yandex LLC * Copyright (c) 2015 Alexander V. Chernikov - * Copyright (c) 2016-2019 Andrey V. Elsukov + * Copyright (c) 2016-2020 Andrey V. Elsukov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -84,7 +84,7 @@ static void nat64lsn_periodic(void *data); #define PERIODIC_DELAY 4 #define NAT64_LOOKUP(chain, cmd) \ - (struct nat64lsn_cfg *)SRV_OBJECT((chain), (cmd)->arg1) + (struct nat64lsn_instance *)SRV_OBJECT((chain), insntod(cmd, kidx)->kidx) /* * Delayed job queue, used to create new hosts * and new portgroups @@ -178,7 +178,7 @@ { memset(plog, 0, sizeof(*plog)); - plog->length = PFLOG_HDRLEN; + plog->length = PFLOG_REAL_HDRLEN; plog->af = family; plog->action = PF_NAT; plog->dir = PF_IN; @@ -212,6 +212,21 @@ return (CK_SLIST_FIRST(&host->aliases)); } +static struct nat64lsn_alias* +nat64lsn_get_alias(struct nat64lsn_cfg *cfg, + const struct ipfw_flow_id *f_id __unused) +{ + static uint32_t idx = 0; + + /* + * We can choose alias by number of allocated PGs, + * not used yet by other hosts, or some static configured + * by user. + * XXX: for now we choose it using round robin. + */ + return (&ALIAS_BYHASH(cfg, idx++)); +} + #define STATE_HVAL(c, d) HVAL((d), 2, (c)->hash_seed) #define STATE_HASH(h, v) \ ((h)->states_hash[(v) & ((h)->states_hashsize - 1)]) @@ -255,53 +270,47 @@ ((uint64_t)ck_pr_load_32(FREEMASK_CHUNK((pg), (n)) + 1) << 32) #endif /* !__LP64__ */ -#define NAT64LSN_TRY_PGCNT 32 + +#define NAT64LSN_TRY_PGCNT 36 static struct nat64lsn_pg* nat64lsn_get_pg(uint32_t *chunkmask, uint32_t *pgmask, - struct nat64lsn_pgchunk **chunks, struct nat64lsn_pg **pgptr, - uint32_t *pgidx, in_addr_t faddr) + struct nat64lsn_pgchunk **chunks, uint32_t *pgidx, in_addr_t faddr) { - struct nat64lsn_pg *pg, *oldpg; + struct nat64lsn_pg *pg; uint32_t idx, oldidx; int cnt; - cnt = 0; - /* First try last used PG */ - oldpg = pg = ck_pr_load_ptr(pgptr); + /* First try last used PG. */ idx = oldidx = ck_pr_load_32(pgidx); - /* If pgidx is out of range, reset it to the first pgchunk */ - if (!ISSET32(*chunkmask, idx / 32)) - idx = 0; + MPASS(idx < 1024); + cnt = 0; do { ck_pr_fence_load(); - if (pg != NULL && FREEMASK_BITCOUNT(pg, faddr) > 0) { - /* - * If last used PG has not free states, - * try to update pointer. - * NOTE: it can be already updated by jobs handler, - * thus we use CAS operation. - */ + if (idx > 1023 || !ISSET32(*chunkmask, idx / 32)) { + /* If it is first try, reset idx to first PG */ + idx = 0; + /* Stop if idx is out of range */ if (cnt > 0) - ck_pr_cas_ptr(pgptr, oldpg, pg); - return (pg); + break; } - /* Stop if idx is out of range */ - if (!ISSET32(*chunkmask, idx / 32)) - break; - - if (ISSET32(pgmask[idx / 32], idx % 32)) + if (ISSET32(pgmask[idx / 32], idx % 32)) { pg = ck_pr_load_ptr( &chunks[idx / 32]->pgptr[idx % 32]); - else - pg = NULL; - + ck_pr_fence_load(); + /* + * Make sure that pg did not become DEAD. + */ + if ((pg->flags & NAT64LSN_DEADPG) == 0 && + FREEMASK_BITCOUNT(pg, faddr) > 0) { + if (cnt > 0) + ck_pr_cas_32(pgidx, oldidx, idx); + return (pg); + } + } idx++; } while (++cnt < NAT64LSN_TRY_PGCNT); - - /* If pgidx is out of range, reset it to the first pgchunk */ - if (!ISSET32(*chunkmask, idx / 32)) - idx = 0; - ck_pr_cas_32(pgidx, oldidx, idx); + if (oldidx != idx) + ck_pr_cas_32(pgidx, oldidx, idx); return (NULL); } @@ -330,27 +339,24 @@ switch (proto) { case IPPROTO_TCP: - pg = nat64lsn_get_pg( - &link->alias->tcp_chunkmask, link->alias->tcp_pgmask, - link->alias->tcp, &link->alias->tcp_pg, + pg = nat64lsn_get_pg(&link->alias->tcp_chunkmask, + link->alias->tcp_pgmask, link->alias->tcp, &link->alias->tcp_pgidx, faddr); break; case IPPROTO_UDP: - pg = nat64lsn_get_pg( - &link->alias->udp_chunkmask, link->alias->udp_pgmask, - link->alias->udp, &link->alias->udp_pg, + pg = nat64lsn_get_pg(&link->alias->udp_chunkmask, + link->alias->udp_pgmask, link->alias->udp, &link->alias->udp_pgidx, faddr); break; case IPPROTO_ICMP: - pg = nat64lsn_get_pg( - &link->alias->icmp_chunkmask, link->alias->icmp_pgmask, - link->alias->icmp, &link->alias->icmp_pg, + pg = nat64lsn_get_pg(&link->alias->icmp_chunkmask, + link->alias->icmp_pgmask, link->alias->icmp, &link->alias->icmp_pgidx, faddr); break; default: panic("%s: wrong proto %d", __func__, proto); } - if (pg == NULL) + if (pg == NULL || (pg->flags & NAT64LSN_DEADPG) != 0) return (NULL); /* Check that PG has some free states */ @@ -385,14 +391,10 @@ /* Insert new state into host's hash table */ HOST_LOCK(host); + SET_AGE(host->timestamp); CK_SLIST_INSERT_HEAD(&STATE_HASH(host, hval), state, entries); host->states_count++; - /* - * XXX: In case if host is going to be expired, - * reset NAT64LSN_DEADHOST flag. - */ - host->flags &= ~NAT64LSN_DEADHOST; HOST_UNLOCK(host); NAT64STAT_INC(&cfg->base.stats, screated); /* Mark the state as ready for translate4 */ @@ -563,7 +565,7 @@ len = ip->ip_hl << 2; switch (ip->ip_p) { case IPPROTO_ICMP: - len += ICMP_MINLEN; /* Enough to get icmp_id */ + len += ICMP_MINLEN; break; case IPPROTO_TCP: len += sizeof(struct tcphdr); @@ -740,6 +742,32 @@ return (0); } +#define PGCOUNT_ADD(alias, proto, value) \ + switch (proto) { \ + case IPPROTO_TCP: (alias)->tcp_pgcount += (value); break; \ + case IPPROTO_UDP: (alias)->udp_pgcount += (value); break; \ + case IPPROTO_ICMP: (alias)->icmp_pgcount += (value); break; \ + } +#define PGCOUNT_INC(alias, proto) PGCOUNT_ADD(alias, proto, 1) +#define PGCOUNT_DEC(alias, proto) PGCOUNT_ADD(alias, proto, -1) + +static inline void +nat64lsn_state_cleanup(struct nat64lsn_state *state) +{ + + /* + * Reset READY flag and wait until it become + * safe for translate4. + */ + ck_pr_btr_32(&state->flags, NAT64_BIT_READY_IPV4); + /* + * And set STALE flag for deferred deletion in the + * next pass of nat64lsn_maintain_pg(). + */ + ck_pr_bts_32(&state->flags, NAT64_BIT_STALE); + ck_pr_fence_store(); +} + static int nat64lsn_maintain_pg(struct nat64lsn_cfg *cfg, struct nat64lsn_pg *pg) { @@ -781,19 +809,12 @@ HOST_LOCK(host); CK_SLIST_REMOVE(&STATE_HASH(host, state->hval), state, nat64lsn_state, entries); - host->states_count--; - HOST_UNLOCK(host); - - /* Reset READY flag */ - ck_pr_btr_32(&state->flags, NAT64_BIT_READY_IPV4); - /* And set STALE flag */ - ck_pr_bts_32(&state->flags, NAT64_BIT_STALE); - ck_pr_fence_store(); /* - * Now translate6 will not use this state, wait - * until it become safe for translate4, then mark - * state as free. + * Now translate6 will not use this state. */ + host->states_count--; + HOST_UNLOCK(host); + nat64lsn_state_cleanup(state); } } @@ -814,7 +835,7 @@ struct nat64lsn_pg_slist *portgroups) { struct nat64lsn_alias *alias; - struct nat64lsn_pg *pg, *tpg, *firstpg, **pgptr; + struct nat64lsn_pg *pg, *tpg; uint32_t *pgmask, *pgidx; int i, idx; @@ -827,45 +848,47 @@ if (pg->base_port == NAT64_MIN_PORT) continue; /* - * PG is expired, unlink it and schedule for - * deferred destroying. + * PG expires in two passes: + * 1. Reset bit in pgmask, mark it as DEAD. + * 2. Unlink it and schedule for deferred destroying. */ idx = (pg->base_port - NAT64_MIN_PORT) / 64; switch (pg->proto) { case IPPROTO_TCP: pgmask = alias->tcp_pgmask; - pgptr = &alias->tcp_pg; pgidx = &alias->tcp_pgidx; - firstpg = alias->tcp[0]->pgptr[0]; break; case IPPROTO_UDP: pgmask = alias->udp_pgmask; - pgptr = &alias->udp_pg; pgidx = &alias->udp_pgidx; - firstpg = alias->udp[0]->pgptr[0]; break; case IPPROTO_ICMP: pgmask = alias->icmp_pgmask; - pgptr = &alias->icmp_pg; pgidx = &alias->icmp_pgidx; - firstpg = alias->icmp[0]->pgptr[0]; break; } + if (pg->flags & NAT64LSN_DEADPG) { + /* Unlink PG from alias's chain */ + ALIAS_LOCK(alias); + CK_SLIST_REMOVE(&alias->portgroups, pg, + nat64lsn_pg, entries); + PGCOUNT_DEC(alias, pg->proto); + ALIAS_UNLOCK(alias); + /* + * Link it to job's chain for deferred + * destroying. + */ + NAT64STAT_INC(&cfg->base.stats, spgdeleted); + CK_SLIST_INSERT_HEAD(portgroups, pg, entries); + continue; + } + /* Reset the corresponding bit in pgmask array. */ ck_pr_btr_32(&pgmask[idx / 32], idx % 32); + pg->flags |= NAT64LSN_DEADPG; ck_pr_fence_store(); /* If last used PG points to this PG, reset it. */ - ck_pr_cas_ptr(pgptr, pg, firstpg); ck_pr_cas_32(pgidx, idx, 0); - /* Unlink PG from alias's chain */ - ALIAS_LOCK(alias); - CK_SLIST_REMOVE(&alias->portgroups, pg, - nat64lsn_pg, entries); - alias->portgroups_count--; - ALIAS_UNLOCK(alias); - /* And link to job's chain for deferred destroying */ - NAT64STAT_INC(&cfg->base.stats, spgdeleted); - CK_SLIST_INSERT_HEAD(portgroups, pg, entries); } } } @@ -882,7 +905,9 @@ entries, tmp) { /* Is host was marked in previous call? */ if (host->flags & NAT64LSN_DEADHOST) { - if (host->states_count > 0) { + if (host->states_count > 0 || + GET_AGE(host->timestamp) < + cfg->host_delete_delay) { host->flags &= ~NAT64LSN_DEADHOST; continue; } @@ -898,9 +923,8 @@ CK_SLIST_INSERT_HEAD(hosts, host, entries); continue; } - if (GET_AGE(host->timestamp) < cfg->host_delete_delay) - continue; - if (host->states_count > 0) + if (host->states_count > 0 || + GET_AGE(host->timestamp) < cfg->host_delete_delay) continue; /* Mark host as going to be expired in next pass */ host->flags |= NAT64LSN_DEADHOST; @@ -966,7 +990,7 @@ #endif /* - * This procedure is used to perform various maintenance + * This procedure is used to perform various maintance * on dynamic hash list. Currently it is called every 4 seconds. */ static void @@ -1044,14 +1068,11 @@ host->hval = ji->src6_hval; host->flags = 0; host->states_count = 0; - host->states_hashsize = NAT64LSN_HSIZE; CK_SLIST_INIT(&host->aliases); for (i = 0; i < host->states_hashsize; i++) CK_SLIST_INIT(&host->states_hash[i]); - /* Determine alias from flow hash. */ - hval = ALIASLINK_HVAL(cfg, &ji->f_id); - link->alias = &ALIAS_BYHASH(cfg, hval); + link->alias = nat64lsn_get_alias(cfg, &ji->f_id); CK_SLIST_INSERT_HEAD(&host->aliases, link, host_entries); ALIAS_LOCK(link->alias); @@ -1103,9 +1124,8 @@ static int nat64lsn_alloc_proto_pg(struct nat64lsn_cfg *cfg, - struct nat64lsn_alias *alias, uint32_t *chunkmask, - uint32_t *pgmask, struct nat64lsn_pgchunk **chunks, - struct nat64lsn_pg **pgptr, uint8_t proto) + struct nat64lsn_alias *alias, uint32_t *chunkmask, uint32_t *pgmask, + struct nat64lsn_pgchunk **chunks, uint32_t *pgidx, uint8_t proto) { struct nat64lsn_pg *pg; int i, pg_idx, chunk_idx; @@ -1163,17 +1183,20 @@ /* Initialize PG and hook it to pgchunk */ SET_AGE(pg->timestamp); + pg->flags = 0; pg->proto = proto; pg->base_port = NAT64_MIN_PORT + 64 * pg_idx; ck_pr_store_ptr(&chunks[chunk_idx]->pgptr[pg_idx % 32], pg); ck_pr_fence_store(); - ck_pr_bts_32(&pgmask[pg_idx / 32], pg_idx % 32); - ck_pr_store_ptr(pgptr, pg); + + /* Set bit in pgmask and set index of last used PG */ + ck_pr_bts_32(&pgmask[chunk_idx], pg_idx % 32); + ck_pr_store_32(pgidx, pg_idx); ALIAS_LOCK(alias); CK_SLIST_INSERT_HEAD(&alias->portgroups, pg, entries); SET_AGE(alias->timestamp); - alias->portgroups_count++; + PGCOUNT_INC(alias, proto); ALIAS_UNLOCK(alias); NAT64STAT_INC(&cfg->base.stats, spgcreated); return (PG_ERROR(0)); @@ -1210,17 +1233,17 @@ case IPPROTO_TCP: ret = nat64lsn_alloc_proto_pg(cfg, alias, &alias->tcp_chunkmask, alias->tcp_pgmask, - alias->tcp, &alias->tcp_pg, ji->proto); + alias->tcp, &alias->tcp_pgidx, ji->proto); break; case IPPROTO_UDP: ret = nat64lsn_alloc_proto_pg(cfg, alias, &alias->udp_chunkmask, alias->udp_pgmask, - alias->udp, &alias->udp_pg, ji->proto); + alias->udp, &alias->udp_pgidx, ji->proto); break; case IPPROTO_ICMP: ret = nat64lsn_alloc_proto_pg(cfg, alias, &alias->icmp_chunkmask, alias->icmp_pgmask, - alias->icmp, &alias->icmp_pg, ji->proto); + alias->icmp, &alias->icmp_pgidx, ji->proto); break; default: panic("%s: wrong proto %d", __func__, ji->proto); @@ -1362,14 +1385,115 @@ JQUEUE_UNLOCK(); } +/* + * This function is used to clean up the result of less likely possible + * race condition, when host object was deleted, but some translation + * state was created before it is destroyed. + * + * Since the state expiration removes state from host's hash table, + * we need to be sure, that there will not any states, that are linked + * with this host entry. + */ +static void +nat64lsn_host_cleanup(struct nat64lsn_host *host) +{ + struct nat64lsn_state *state, *ts; + int i; + + printf("NAT64LSN: %s: race condition has been detected for host %p\n", + __func__, host); + for (i = 0; i < host->states_hashsize; i++) { + CK_SLIST_FOREACH_SAFE(state, &host->states_hash[i], + entries, ts) { + /* + * We can remove the state without lock, + * because this host entry is unlinked and will + * be destroyed. + */ + CK_SLIST_REMOVE(&host->states_hash[i], state, + nat64lsn_state, entries); + host->states_count--; + nat64lsn_state_cleanup(state); + } + } + MPASS(host->states_count == 0); +} + +/* + * This function is used to clean up the result of less likely possible + * race condition, when portgroup was deleted, but some translation state + * was created before it is destroyed. + * + * Since states entries are accessible via host's hash table, we need + * to be sure, that there will not any states from this PG, that are + * linked with any host entries. + */ +static void +nat64lsn_pg_cleanup(struct nat64lsn_pg *pg) +{ + struct nat64lsn_state *state; + uint64_t usedmask; + int c, i; + + printf("NAT64LSN: %s: race condition has been detected for pg %p\n", + __func__, pg); + for (c = 0; c < pg->chunks_count; c++) { + /* + * Use inverted freemask to find what state was created. + */ + usedmask = ~(*FREEMASK_CHUNK(pg, c)); + if (usedmask == 0) + continue; + for (i = 0; i < 64; i++) { + if (!ISSET64(usedmask, i)) + continue; + state = &STATES_CHUNK(pg, c)->state[i]; + /* + * If we have STALE bit, this means that state + * is already unlinked from host's hash table. + * Thus we can just reset the bit in mask and + * schedule destroying in the next epoch call. + */ + if (ISSET32(state->flags, NAT64_BIT_STALE)) { + FREEMASK_BTS(pg, c, i); + continue; + } + /* + * There is small window, when we have bit + * grabbed from freemask, but state is not yet + * linked into host's hash table. + * Check for READY flag, it is set just after + * linking. If it is not set, defer cleanup + * for next call. + */ + if (ISSET32(state->flags, NAT64_BIT_READY_IPV4)) { + struct nat64lsn_host *host; + + host = state->host; + HOST_LOCK(host); + CK_SLIST_REMOVE(&STATE_HASH(host, + state->hval), state, nat64lsn_state, + entries); + host->states_count--; + HOST_UNLOCK(host); + nat64lsn_state_cleanup(state); + } + } + } +} + static void nat64lsn_job_destroy(epoch_context_t ctx) { + struct nat64lsn_hosts_slist hosts; + struct nat64lsn_pg_slist portgroups; struct nat64lsn_job_item *ji; struct nat64lsn_host *host; struct nat64lsn_pg *pg; int i; + CK_SLIST_INIT(&hosts); + CK_SLIST_INIT(&portgroups); ji = __containerof(ctx, struct nat64lsn_job_item, epoch_ctx); MPASS(ji->jtype == JTYPE_DESTROY); while (!CK_SLIST_EMPTY(&ji->hosts)) { @@ -1377,11 +1501,23 @@ CK_SLIST_REMOVE_HEAD(&ji->hosts, entries); if (host->states_count > 0) { /* - * XXX: The state has been created - * during host deletion. + * The state has been created during host deletion. */ printf("NAT64LSN: %s: destroying host with %d " "states\n", __func__, host->states_count); + /* + * We need to cleanup these states to avoid + * possible access to already deleted host in + * the state expiration code. + */ + nat64lsn_host_cleanup(host); + CK_SLIST_INSERT_HEAD(&hosts, host, entries); + /* + * Keep host entry for next deferred destroying. + * In the next epoch its states will be not + * accessible. + */ + continue; } nat64lsn_destroy_host(host); } @@ -1391,18 +1527,33 @@ for (i = 0; i < pg->chunks_count; i++) { if (FREEMASK_BITCOUNT(pg, i) != 64) { /* - * XXX: The state has been created during + * A state has been created during * PG deletion. */ printf("NAT64LSN: %s: destroying PG %p " "with non-empty chunk %d\n", __func__, pg, i); + nat64lsn_pg_cleanup(pg); + CK_SLIST_INSERT_HEAD(&portgroups, + pg, entries); + i = -1; + break; } } - nat64lsn_destroy_pg(pg); + if (i != -1) + nat64lsn_destroy_pg(pg); } - uma_zfree(nat64lsn_pgchunk_zone, ji->pgchunk); - uma_zfree(nat64lsn_job_zone, ji); + if (CK_SLIST_EMPTY(&hosts) && + CK_SLIST_EMPTY(&portgroups)) { + uma_zfree(nat64lsn_pgchunk_zone, ji->pgchunk); + uma_zfree(nat64lsn_job_zone, ji); + return; + } + + /* Schedule job item again */ + CK_SLIST_MOVE(&ji->hosts, &hosts, entries); + CK_SLIST_MOVE(&ji->portgroups, &portgroups, entries); + NAT64LSN_EPOCH_CALL(&ji->epoch_ctx, nat64lsn_job_destroy); } static int @@ -1569,40 +1720,40 @@ ipfw_nat64lsn(struct ip_fw_chain *ch, struct ip_fw_args *args, ipfw_insn *cmd, int *done) { - struct nat64lsn_cfg *cfg; + struct nat64lsn_instance *i; ipfw_insn *icmd; int ret; IPFW_RLOCK_ASSERT(ch); *done = 0; /* continue the search in case of failure */ - icmd = cmd + 1; + icmd = cmd + F_LEN(cmd); if (cmd->opcode != O_EXTERNAL_ACTION || - cmd->arg1 != V_nat64lsn_eid || + insntod(cmd, kidx)->kidx != V_nat64lsn_eid || icmd->opcode != O_EXTERNAL_INSTANCE || - (cfg = NAT64_LOOKUP(ch, icmd)) == NULL) + (i = NAT64_LOOKUP(ch, icmd)) == NULL) return (IP_FW_DENY); *done = 1; /* terminate the search */ switch (args->f_id.addr_type) { case 4: - ret = nat64lsn_translate4(cfg, &args->f_id, &args->m); + ret = nat64lsn_translate4(i->cfg, &args->f_id, &args->m); break; case 6: /* * Check that destination IPv6 address matches our prefix6. */ - if ((cfg->base.flags & NAT64LSN_ANYPREFIX) == 0 && - memcmp(&args->f_id.dst_ip6, &cfg->base.plat_prefix, - cfg->base.plat_plen / 8) != 0) { - ret = cfg->nomatch_verdict; + if ((i->cfg->base.flags & NAT64LSN_ANYPREFIX) == 0 && + memcmp(&args->f_id.dst_ip6, &i->cfg->base.plat_prefix, + i->cfg->base.plat_plen / 8) != 0) { + ret = i->cfg->nomatch_verdict; break; } - ret = nat64lsn_translate6(cfg, &args->f_id, &args->m); + ret = nat64lsn_translate6(i->cfg, &args->f_id, &args->m); break; default: - ret = cfg->nomatch_verdict; + ret = i->cfg->nomatch_verdict; } if (ret != IP_FW_PASS && args->m != NULL) { @@ -1674,7 +1825,7 @@ } struct nat64lsn_cfg * -nat64lsn_init_instance(struct ip_fw_chain *ch, in_addr_t prefix, int plen) +nat64lsn_init_config(struct ip_fw_chain *ch, in_addr_t prefix, int plen) { struct nat64lsn_cfg *cfg; struct nat64lsn_alias *alias; @@ -1777,7 +1928,7 @@ } void -nat64lsn_destroy_instance(struct nat64lsn_cfg *cfg) +nat64lsn_destroy_config(struct nat64lsn_cfg *cfg) { struct nat64lsn_host *host; int i; @@ -1805,3 +1956,4 @@ free(cfg->aliases, M_NAT64LSN); free(cfg, M_NAT64LSN); } + diff --git a/sys/netpfil/ipfw/nat64/nat64lsn_control.c b/sys/netpfil/ipfw/nat64/nat64lsn_control.c --- a/sys/netpfil/ipfw/nat64/nat64lsn_control.c +++ b/sys/netpfil/ipfw/nat64/nat64lsn_control.c @@ -55,17 +55,18 @@ #include "nat64lsn.h" -VNET_DEFINE(uint16_t, nat64lsn_eid) = 0; +VNET_DEFINE(uint32_t, nat64lsn_eid) = 0; -static struct nat64lsn_cfg * +static struct nat64lsn_instance * nat64lsn_find(struct namedobj_instance *ni, const char *name, uint8_t set) { - struct nat64lsn_cfg *cfg; + struct named_object *no; - cfg = (struct nat64lsn_cfg *)ipfw_objhash_lookup_name_type(ni, set, + no = ipfw_objhash_lookup_name_type(ni, set, IPFW_TLV_NAT64LSN_NAME, name); - - return (cfg); + if (no == NULL) + return (NULL); + return (__containerof(no, struct nat64lsn_instance, no)); } static void @@ -112,6 +113,7 @@ { ipfw_obj_lheader *olh; ipfw_nat64lsn_cfg *uc; + struct nat64lsn_instance *i; struct nat64lsn_cfg *cfg; struct namedobj_instance *ni; uint32_t addr4, mask4; @@ -157,12 +159,14 @@ } IPFW_UH_RUNLOCK(ch); - cfg = nat64lsn_init_instance(ch, addr4, uc->plen4); - strlcpy(cfg->name, uc->name, sizeof(cfg->name)); - cfg->no.name = cfg->name; - cfg->no.etlv = IPFW_TLV_NAT64LSN_NAME; - cfg->no.set = uc->set; + i = malloc(sizeof(struct nat64lsn_instance), M_NAT64LSN, + M_WAITOK | M_ZERO); + strlcpy(i->name, uc->name, sizeof(i->name)); + i->no.name = i->name; + i->no.etlv = IPFW_TLV_NAT64LSN_NAME; + i->no.set = uc->set; + cfg = nat64lsn_init_config(ch, addr4, uc->plen4); cfg->base.plat_prefix = uc->prefix6; cfg->base.plat_plen = uc->plen6; cfg->base.flags = (uc->flags & NAT64LSN_FLAGSMASK) | NAT64_PLATPFX; @@ -180,26 +184,28 @@ cfg->st_estab_ttl = uc->st_estab_ttl; cfg->st_udp_ttl = uc->st_udp_ttl; cfg->st_icmp_ttl = uc->st_icmp_ttl; - cfg->nomatch_verdict = IP_FW_DENY; IPFW_UH_WLOCK(ch); if (nat64lsn_find(ni, uc->name, uc->set) != NULL) { IPFW_UH_WUNLOCK(ch); - nat64lsn_destroy_instance(cfg); + nat64lsn_destroy_config(cfg); + free(i, M_NAT64LSN); return (EEXIST); } - if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0) { + if (ipfw_objhash_alloc_idx(ni, &i->no.kidx) != 0) { IPFW_UH_WUNLOCK(ch); - nat64lsn_destroy_instance(cfg); + nat64lsn_destroy_config(cfg); + free(i, M_NAT64LSN); return (ENOSPC); } - ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no); + ipfw_objhash_add(ni, &i->no); /* Okay, let's link data */ - SRV_OBJECT(ch, cfg->no.kidx) = cfg; + i->cfg = cfg; + SRV_OBJECT(ch, i->no.kidx) = i; nat64lsn_start_instance(cfg); IPFW_UH_WUNLOCK(ch); @@ -207,13 +213,14 @@ } static void -nat64lsn_detach_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg) +nat64lsn_detach_instance(struct ip_fw_chain *ch, + struct nat64lsn_instance *i) { IPFW_UH_WLOCK_ASSERT(ch); - - ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no); - ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx); + SRV_OBJECT(ch, i->no.kidx) = NULL; + ipfw_objhash_del(CHAIN_TO_SRV(ch), &i->no); + ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), i->no.kidx); } /* @@ -227,7 +234,7 @@ nat64lsn_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd) { - struct nat64lsn_cfg *cfg; + struct nat64lsn_instance *i; ipfw_obj_header *oh; if (sd->valsize != sizeof(*oh)) @@ -236,23 +243,23 @@ oh = (ipfw_obj_header *)op3; IPFW_UH_WLOCK(ch); - cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); - if (cfg == NULL) { + i = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); + if (i == NULL) { IPFW_UH_WUNLOCK(ch); return (ENOENT); } - if (cfg->no.refcnt > 0) { + if (i->no.refcnt > 0) { IPFW_UH_WUNLOCK(ch); return (EBUSY); } - ipfw_reset_eaction_instance(ch, V_nat64lsn_eid, cfg->no.kidx); - SRV_OBJECT(ch, cfg->no.kidx) = NULL; - nat64lsn_detach_config(ch, cfg); + ipfw_reset_eaction_instance(ch, V_nat64lsn_eid, i->no.kidx); + nat64lsn_detach_instance(ch, i); IPFW_UH_WUNLOCK(ch); - nat64lsn_destroy_instance(cfg); + nat64lsn_destroy_config(i->cfg); + free(i, M_NAT64LSN); return (0); } @@ -263,7 +270,7 @@ struct ipfw_nat64lsn_stats *stats) { struct nat64lsn_alias *alias; - int i, j; + int i; __COPY_STAT_FIELD(cfg, stats, opcnt64); __COPY_STAT_FIELD(cfg, stats, opcnt46); @@ -294,20 +301,22 @@ stats->hostcount = cfg->hosts_count; for (i = 0; i < (1 << (32 - cfg->plen4)); i++) { alias = &cfg->aliases[i]; - for (j = 0; j < 32 && ISSET32(alias->tcp_chunkmask, j); j++) - stats->tcpchunks += bitcount32(alias->tcp_pgmask[j]); - for (j = 0; j < 32 && ISSET32(alias->udp_chunkmask, j); j++) - stats->udpchunks += bitcount32(alias->udp_pgmask[j]); - for (j = 0; j < 32 && ISSET32(alias->icmp_chunkmask, j); j++) - stats->icmpchunks += bitcount32(alias->icmp_pgmask[j]); + stats->tcpchunks += alias->tcp_pgcount; + stats->udpchunks += alias->udp_pgcount; + stats->icmpchunks += alias->icmp_pgcount; } } #undef __COPY_STAT_FIELD static void -nat64lsn_export_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg, +nat64lsn_export_config(struct ip_fw_chain *ch, struct nat64lsn_instance *i, ipfw_nat64lsn_cfg *uc) { + struct nat64lsn_cfg *cfg; + + strlcpy(uc->name, i->no.name, sizeof(uc->name)); + uc->set = i->no.set; + cfg = i->cfg; uc->flags = cfg->base.flags & NAT64LSN_FLAGSMASK; uc->states_chunks = cfg->states_chunks; @@ -323,8 +332,6 @@ uc->prefix6 = cfg->base.plat_prefix; uc->plen4 = cfg->plen4; uc->plen6 = cfg->base.plat_plen; - uc->set = cfg->no.set; - strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); } struct nat64_dump_arg { @@ -336,12 +343,14 @@ export_config_cb(struct namedobj_instance *ni, struct named_object *no, void *arg) { - struct nat64_dump_arg *da = (struct nat64_dump_arg *)arg; + struct nat64_dump_arg *da; ipfw_nat64lsn_cfg *uc; + da = (struct nat64_dump_arg *)arg; uc = (struct _ipfw_nat64lsn_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc)); - nat64lsn_export_config(da->ch, (struct nat64lsn_cfg *)no, uc); + nat64lsn_export_config(da->ch, + __containerof(no, struct nat64lsn_instance, no), uc); return (0); } @@ -400,6 +409,7 @@ { ipfw_obj_header *oh; ipfw_nat64lsn_cfg *uc; + struct nat64lsn_instance *i; struct nat64lsn_cfg *cfg; struct namedobj_instance *ni; @@ -417,12 +427,12 @@ ni = CHAIN_TO_SRV(ch); if (sd->sopt->sopt_dir == SOPT_GET) { IPFW_UH_RLOCK(ch); - cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); - if (cfg == NULL) { + i = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); + if (i == NULL) { IPFW_UH_RUNLOCK(ch); return (ENOENT); } - nat64lsn_export_config(ch, cfg, uc); + nat64lsn_export_config(ch, i, uc); IPFW_UH_RUNLOCK(ch); return (0); } @@ -430,8 +440,8 @@ nat64lsn_default_config(uc); IPFW_UH_WLOCK(ch); - cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); - if (cfg == NULL) { + i = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); + if (i == NULL) { IPFW_UH_WUNLOCK(ch); return (ENOENT); } @@ -441,7 +451,7 @@ * jmaxlen, nh_del_age, pg_del_age, tcp_syn_age, tcp_close_age, * tcp_est_age, udp_age, icmp_age, flags, states_chunks. */ - + cfg = i->cfg; cfg->states_chunks = uc->states_chunks; cfg->jmaxlen = uc->jmaxlen; cfg->host_delete_delay = uc->nh_delete_delay; @@ -472,7 +482,7 @@ struct sockopt_data *sd) { struct ipfw_nat64lsn_stats stats; - struct nat64lsn_cfg *cfg; + struct nat64lsn_instance *i; ipfw_obj_header *oh; ipfw_obj_ctlv *ctlv; size_t sz; @@ -488,13 +498,13 @@ memset(&stats, 0, sizeof(stats)); IPFW_UH_RLOCK(ch); - cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); - if (cfg == NULL) { + i = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); + if (i == NULL) { IPFW_UH_RUNLOCK(ch); return (ENOENT); } - export_stats(ch, cfg, &stats); + export_stats(ch, i->cfg, &stats); IPFW_UH_RUNLOCK(ch); ctlv = (ipfw_obj_ctlv *)(oh + 1); @@ -519,7 +529,7 @@ nat64lsn_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, struct sockopt_data *sd) { - struct nat64lsn_cfg *cfg; + struct nat64lsn_instance *i; ipfw_obj_header *oh; if (sd->valsize != sizeof(*oh)) @@ -530,12 +540,12 @@ return (EINVAL); IPFW_UH_WLOCK(ch); - cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); - if (cfg == NULL) { + i = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); + if (i == NULL) { IPFW_UH_WUNLOCK(ch); return (ENOENT); } - COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS); + COUNTER_ARRAY_ZERO(i->cfg->base.stats.cnt, NAT64STATS); IPFW_UH_WUNLOCK(ch); return (0); } @@ -551,7 +561,7 @@ * ipfw_nat64lsn_state x count, ... ] ] */ static int -nat64lsn_export_states_v1(struct nat64lsn_cfg *cfg, union nat64lsn_pgidx *idx, +nat64lsn_export_states(struct nat64lsn_cfg *cfg, union nat64lsn_pgidx *idx, struct nat64lsn_pg *pg, struct sockopt_data *sd, uint32_t *ret_count) { ipfw_nat64lsn_state_v1 *s; @@ -663,24 +673,6 @@ return (NULL); } -/* - * Lists nat64lsn states. - * Data layout (v0): - * Request: [ ipfw_obj_header ipfw_obj_data [ uint64_t ]] - * Reply: [ ipfw_obj_header ipfw_obj_data [ - * ipfw_nat64lsn_stg ipfw_nat64lsn_state x N] ] - * - * Returns 0 on success - */ -static int -nat64lsn_states_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, - struct sockopt_data *sd) -{ - - /* TODO: implement states listing for old ipfw(8) binaries */ - return (EOPNOTSUPP); -} - /* * Lists nat64lsn states. * Data layout (v1)(current): @@ -691,12 +683,13 @@ * Returns 0 on success */ static int -nat64lsn_states_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, +nat64lsn_states(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd) { ipfw_obj_header *oh; ipfw_obj_data *od; ipfw_nat64lsn_stg_v1 *stg; + struct nat64lsn_instance *i; struct nat64lsn_cfg *cfg; struct nat64lsn_pg *pg; union nat64lsn_pgidx idx; @@ -724,11 +717,12 @@ return (EINVAL); IPFW_UH_RLOCK(ch); - cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); - if (cfg == NULL) { + i = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); + if (i == NULL) { IPFW_UH_RUNLOCK(ch); return (ENOENT); } + cfg = i->cfg; if (idx.index == 0) { /* Fill in starting point */ idx.addr = cfg->prefix4; idx.proto = IPPROTO_ICMP; @@ -762,7 +756,7 @@ pg = nat64lsn_get_pg_byidx(cfg, &idx); if (pg != NULL) { count = 0; - ret = nat64lsn_export_states_v1(cfg, &idx, pg, + ret = nat64lsn_export_states(cfg, &idx, pg, sd, &count); if (ret != 0) break; @@ -809,96 +803,113 @@ } static struct ipfw_sopt_handler scodes[] = { - { IP_FW_NAT64LSN_CREATE, 0, HDIR_BOTH, nat64lsn_create }, - { IP_FW_NAT64LSN_DESTROY,0, HDIR_SET, nat64lsn_destroy }, - { IP_FW_NAT64LSN_CONFIG, 0, HDIR_BOTH, nat64lsn_config }, - { IP_FW_NAT64LSN_LIST, 0, HDIR_GET, nat64lsn_list }, - { IP_FW_NAT64LSN_STATS, 0, HDIR_GET, nat64lsn_stats }, - { IP_FW_NAT64LSN_RESET_STATS,0, HDIR_SET, nat64lsn_reset_stats }, - { IP_FW_NAT64LSN_LIST_STATES,0, HDIR_GET, nat64lsn_states_v0 }, - { IP_FW_NAT64LSN_LIST_STATES,1, HDIR_GET, nat64lsn_states_v1 }, + { IP_FW_NAT64LSN_CREATE, IP_FW3_OPVER, HDIR_BOTH, nat64lsn_create }, + { IP_FW_NAT64LSN_DESTROY, IP_FW3_OPVER, HDIR_SET, nat64lsn_destroy }, + { IP_FW_NAT64LSN_CONFIG, IP_FW3_OPVER, HDIR_BOTH, nat64lsn_config }, + { IP_FW_NAT64LSN_LIST, IP_FW3_OPVER, HDIR_GET, nat64lsn_list }, + { IP_FW_NAT64LSN_STATS, IP_FW3_OPVER, HDIR_GET, nat64lsn_stats }, + { IP_FW_NAT64LSN_RESET_STATS, IP_FW3_OPVER, HDIR_SET, nat64lsn_reset_stats }, + { IP_FW_NAT64LSN_LIST_STATES, IP_FW3_OPVER, HDIR_GET, nat64lsn_states }, }; +#define NAT64LSN_ARE_EQUAL(v) (cfg0->v == cfg1->v) static int -nat64lsn_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +nat64lsn_cmp_configs(struct nat64lsn_cfg *cfg0, struct nat64lsn_cfg *cfg1) { - ipfw_insn *icmd; - - icmd = cmd - 1; - if (icmd->opcode != O_EXTERNAL_ACTION || - icmd->arg1 != V_nat64lsn_eid) - return (1); - *puidx = cmd->arg1; - *ptype = 0; - return (0); + if ((cfg0->base.flags & cfg1->base.flags & NAT64LSN_ALLOW_SWAPCONF) && + NAT64LSN_ARE_EQUAL(prefix4) && + NAT64LSN_ARE_EQUAL(pmask4) && + NAT64LSN_ARE_EQUAL(plen4) && + NAT64LSN_ARE_EQUAL(base.plat_plen) && + IN6_ARE_ADDR_EQUAL(&cfg0->base.plat_prefix, + &cfg1->base.plat_prefix)) + return (0); + return (1); } +#undef NAT64LSN_ARE_EQUAL static void -nat64lsn_update_arg1(ipfw_insn *cmd, uint16_t idx) +nat64lsn_swap_configs(struct nat64lsn_instance *i0, + struct nat64lsn_instance *i1) { + struct nat64lsn_cfg *cfg; - cmd->arg1 = idx; + cfg = i0->cfg; + i0->cfg = i1->cfg; + i1->cfg = cfg; } +/* + * NAT64LSN sets swap handler. + * + * When two sets have NAT64LSN instance with the same name, we check + * most important configuration parameters, and if there are no difference, + * and both instances have NAT64LSN_ALLOW_SWAPCONF flag, we will exchange + * configs between instances. This allows to keep NAT64 states when ipfw's + * rules are reloaded using new set. + * + * XXX: since manage_sets caller doesn't hold IPFW_WLOCK(), it is possible + * that some states will be created during switching, because set of rules + * is changed a bit earley than named objects. + */ static int -nat64lsn_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, - struct named_object **pno) -{ - int err; - - err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, - IPFW_TLV_NAT64LSN_NAME, pno); - return (err); -} - -static struct named_object * -nat64lsn_findbykidx(struct ip_fw_chain *ch, uint16_t idx) +nat64lsn_swap_sets_cb(struct namedobj_instance *ni, struct named_object *no, + void *arg) { - struct namedobj_instance *ni; - struct named_object *no; - - IPFW_UH_WLOCK_ASSERT(ch); - ni = CHAIN_TO_SRV(ch); - no = ipfw_objhash_lookup_kidx(ni, idx); - KASSERT(no != NULL, ("NAT64LSN with index %d not found", idx)); - - return (no); + struct nat64lsn_instance *i0, *i1; + uint8_t *sets; + + sets = arg; + if (no->set == sets[0]) { + /* + * Check if we have instance in new set with the same + * config that is sets aware and ready to swap configs. + */ + i0 = __containerof(no, struct nat64lsn_instance, no); + if ((i0->cfg->base.flags & NAT64LSN_ALLOW_SWAPCONF) && + (i1 = nat64lsn_find(ni, no->name, sets[1])) != NULL) { + /* Compare configs */ + if (nat64lsn_cmp_configs(i0->cfg, i1->cfg) == 0) { + IPFW_UH_WLOCK_ASSERT(&V_layer3_chain); + IPFW_WLOCK(&V_layer3_chain); + nat64lsn_swap_configs(i0, i1); + IPFW_WUNLOCK(&V_layer3_chain); + } + } + } + return (0); } static int -nat64lsn_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, +nat64lsn_manage_sets(struct ip_fw_chain *ch, uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) { + uint8_t sets[2]; + if (cmd == SWAP_ALL) { + sets[0] = (uint8_t)set; + sets[1] = new_set; + ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), + nat64lsn_swap_sets_cb, &sets, IPFW_TLV_NAT64LSN_NAME); + } return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64LSN_NAME, set, new_set, cmd)); } - -static struct opcode_obj_rewrite opcodes[] = { - { - .opcode = O_EXTERNAL_INSTANCE, - .etlv = IPFW_TLV_EACTION /* just show it isn't table */, - .classifier = nat64lsn_classify, - .update = nat64lsn_update_arg1, - .find_byname = nat64lsn_findbyname, - .find_bykidx = nat64lsn_findbykidx, - .manage_sets = nat64lsn_manage_sets, - }, -}; +NAT64_DEFINE_OPCODE_REWRITER(nat64lsn, NAT64LSN, opcodes); static int destroy_config_cb(struct namedobj_instance *ni, struct named_object *no, void *arg) { - struct nat64lsn_cfg *cfg; + struct nat64lsn_instance *i; struct ip_fw_chain *ch; ch = (struct ip_fw_chain *)arg; - cfg = (struct nat64lsn_cfg *)SRV_OBJECT(ch, no->kidx); - SRV_OBJECT(ch, no->kidx) = NULL; - nat64lsn_detach_config(ch, cfg); - nat64lsn_destroy_instance(cfg); + i = (struct nat64lsn_instance *)SRV_OBJECT(ch, no->kidx); + nat64lsn_detach_instance(ch, i); + nat64lsn_destroy_config(i->cfg); + free(i, M_NAT64LSN); return (0); } diff --git a/sys/netpfil/ipfw/nat64/nat64stl.h b/sys/netpfil/ipfw/nat64/nat64stl.h --- a/sys/netpfil/ipfw/nat64/nat64stl.h +++ b/sys/netpfil/ipfw/nat64/nat64stl.h @@ -35,8 +35,8 @@ struct nat64stl_cfg { struct named_object no; - uint16_t map64; /* table with 6to4 mapping */ - uint16_t map46; /* table with 4to6 mapping */ + uint32_t map64; /* table with 6to4 mapping */ + uint32_t map46; /* table with 4to6 mapping */ struct nat64_config base; #define NAT64STL_KIDX 0x0100 @@ -47,7 +47,7 @@ char name[64]; }; -VNET_DECLARE(uint16_t, nat64stl_eid); +VNET_DECLARE(uint32_t, nat64stl_eid); #define V_nat64stl_eid VNET(nat64stl_eid) #define IPFW_TLV_NAT64STL_NAME IPFW_TLV_EACTION_NAME(V_nat64stl_eid) diff --git a/sys/netpfil/ipfw/nat64/nat64stl.c b/sys/netpfil/ipfw/nat64/nat64stl.c --- a/sys/netpfil/ipfw/nat64/nat64stl.c +++ b/sys/netpfil/ipfw/nat64/nat64stl.c @@ -58,7 +58,7 @@ #include "nat64stl.h" #define NAT64_LOOKUP(chain, cmd) \ - (struct nat64stl_cfg *)SRV_OBJECT((chain), (cmd)->arg1) + (struct nat64stl_cfg *)SRV_OBJECT((chain), insntod(cmd, kidx)->kidx) static void nat64stl_log(struct pfloghdr *plog, struct mbuf *m, sa_family_t family, @@ -67,7 +67,7 @@ static uint32_t pktid = 0; memset(plog, 0, sizeof(*plog)); - plog->length = PFLOG_HDRLEN; + plog->length = PFLOG_REAL_HDRLEN; plog->af = family; plog->action = PF_NAT; plog->dir = PF_IN; @@ -204,8 +204,8 @@ ipfw_nat64stl(struct ip_fw_chain *chain, struct ip_fw_args *args, ipfw_insn *cmd, int *done) { - ipfw_insn *icmd; struct nat64stl_cfg *cfg; + ipfw_insn *icmd; in_addr_t dst4; uint32_t tablearg; int ret; @@ -213,9 +213,9 @@ IPFW_RLOCK_ASSERT(chain); *done = 0; /* try next rule if not matched */ - icmd = cmd + 1; + icmd = cmd + F_LEN(cmd); if (cmd->opcode != O_EXTERNAL_ACTION || - cmd->arg1 != V_nat64stl_eid || + insntod(cmd, kidx)->kidx != V_nat64stl_eid || icmd->opcode != O_EXTERNAL_INSTANCE || (cfg = NAT64_LOOKUP(chain, icmd)) == NULL) return (0); diff --git a/sys/netpfil/ipfw/nat64/nat64stl_control.c b/sys/netpfil/ipfw/nat64/nat64stl_control.c --- a/sys/netpfil/ipfw/nat64/nat64stl_control.c +++ b/sys/netpfil/ipfw/nat64/nat64stl_control.c @@ -61,7 +61,7 @@ #include "nat64stl.h" -VNET_DEFINE(uint16_t, nat64stl_eid) = 0; +VNET_DEFINE(uint32_t, nat64stl_eid) = 0; static struct nat64stl_cfg *nat64stl_alloc_config(const char *name, uint8_t set); @@ -489,81 +489,23 @@ } static struct ipfw_sopt_handler scodes[] = { - { IP_FW_NAT64STL_CREATE, 0, HDIR_SET, nat64stl_create }, - { IP_FW_NAT64STL_DESTROY,0, HDIR_SET, nat64stl_destroy }, - { IP_FW_NAT64STL_CONFIG, 0, HDIR_BOTH, nat64stl_config }, - { IP_FW_NAT64STL_LIST, 0, HDIR_GET, nat64stl_list }, - { IP_FW_NAT64STL_STATS, 0, HDIR_GET, nat64stl_stats }, - { IP_FW_NAT64STL_RESET_STATS,0, HDIR_SET, nat64stl_reset_stats }, + { IP_FW_NAT64STL_CREATE, IP_FW3_OPVER, HDIR_SET, nat64stl_create }, + { IP_FW_NAT64STL_DESTROY, IP_FW3_OPVER, HDIR_SET, nat64stl_destroy }, + { IP_FW_NAT64STL_CONFIG, IP_FW3_OPVER, HDIR_BOTH,nat64stl_config }, + { IP_FW_NAT64STL_LIST, IP_FW3_OPVER, HDIR_GET, nat64stl_list }, + { IP_FW_NAT64STL_STATS, IP_FW3_OPVER, HDIR_GET, nat64stl_stats }, + { IP_FW_NAT64STL_RESET_STATS, IP_FW3_OPVER, HDIR_SET, nat64stl_reset_stats }, }; static int -nat64stl_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) -{ - ipfw_insn *icmd; - - icmd = cmd - 1; - if (icmd->opcode != O_EXTERNAL_ACTION || - icmd->arg1 != V_nat64stl_eid) - return (1); - - *puidx = cmd->arg1; - *ptype = 0; - return (0); -} - -static void -nat64stl_update_arg1(ipfw_insn *cmd, uint16_t idx) -{ - - cmd->arg1 = idx; -} - -static int -nat64stl_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, - struct named_object **pno) -{ - int err; - - err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, - IPFW_TLV_NAT64STL_NAME, pno); - return (err); -} - -static struct named_object * -nat64stl_findbykidx(struct ip_fw_chain *ch, uint16_t idx) -{ - struct namedobj_instance *ni; - struct named_object *no; - - IPFW_UH_WLOCK_ASSERT(ch); - ni = CHAIN_TO_SRV(ch); - no = ipfw_objhash_lookup_kidx(ni, idx); - KASSERT(no != NULL, ("NAT with index %d not found", idx)); - - return (no); -} - -static int -nat64stl_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, +nat64stl_manage_sets(struct ip_fw_chain *ch, uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) { return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64STL_NAME, set, new_set, cmd)); } - -static struct opcode_obj_rewrite opcodes[] = { - { - .opcode = O_EXTERNAL_INSTANCE, - .etlv = IPFW_TLV_EACTION /* just show it isn't table */, - .classifier = nat64stl_classify, - .update = nat64stl_update_arg1, - .find_byname = nat64stl_findbyname, - .find_bykidx = nat64stl_findbykidx, - .manage_sets = nat64stl_manage_sets, - }, -}; +NAT64_DEFINE_OPCODE_REWRITER(nat64stl, NAT64STL, opcodes); static int destroy_config_cb(struct namedobj_instance *ni, struct named_object *no, diff --git a/sys/netpfil/ipfw/nptv6/nptv6.c b/sys/netpfil/ipfw/nptv6/nptv6.c --- a/sys/netpfil/ipfw/nptv6/nptv6.c +++ b/sys/netpfil/ipfw/nptv6/nptv6.c @@ -60,7 +60,7 @@ #include #include -VNET_DEFINE_STATIC(uint16_t, nptv6_eid) = 0; +VNET_DEFINE_STATIC(uint32_t, nptv6_eid) = 0; #define V_nptv6_eid VNET(nptv6_eid) #define IPFW_TLV_NPTV6_NAME IPFW_TLV_EACTION_NAME(V_nptv6_eid) @@ -76,7 +76,7 @@ int offset); #define NPTV6_LOOKUP(chain, cmd) \ - (struct nptv6_cfg *)SRV_OBJECT((chain), (cmd)->arg1) + (struct nptv6_cfg *)SRV_OBJECT((chain), insntod(cmd, kidx)->kidx) #ifndef IN6_MASK_ADDR #define IN6_MASK_ADDR(a, m) do { \ @@ -354,9 +354,9 @@ *done = 0; /* try next rule if not matched */ ret = IP_FW_DENY; - icmd = cmd + 1; + icmd = cmd + F_LEN(cmd); if (cmd->opcode != O_EXTERNAL_ACTION || - cmd->arg1 != V_nptv6_eid || + insntod(cmd, kidx)->kidx != V_nptv6_eid || icmd->opcode != O_EXTERNAL_INSTANCE || (cfg = NPTV6_LOOKUP(chain, icmd)) == NULL || (cfg->flags & NPTV6_READY) == 0) @@ -374,7 +374,7 @@ */ ip6 = mtod(args->m, struct ip6_hdr *); NPTV6_IPDEBUG("eid %u, oid %u, %s -> %s %d", - cmd->arg1, icmd->arg1, + insntod(cmd, kidx)->kidx, insntod(icmd, kidx)->kidx, inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)), inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)), ip6->ip6_nxt); @@ -904,37 +904,38 @@ } static struct ipfw_sopt_handler scodes[] = { - { IP_FW_NPTV6_CREATE, 0, HDIR_SET, nptv6_create }, - { IP_FW_NPTV6_DESTROY,0, HDIR_SET, nptv6_destroy }, - { IP_FW_NPTV6_CONFIG, 0, HDIR_BOTH, nptv6_config }, - { IP_FW_NPTV6_LIST, 0, HDIR_GET, nptv6_list }, - { IP_FW_NPTV6_STATS, 0, HDIR_GET, nptv6_stats }, - { IP_FW_NPTV6_RESET_STATS,0, HDIR_SET, nptv6_reset_stats }, + { IP_FW_NPTV6_CREATE, IP_FW3_OPVER, HDIR_SET, nptv6_create }, + { IP_FW_NPTV6_DESTROY, IP_FW3_OPVER, HDIR_SET, nptv6_destroy }, + { IP_FW_NPTV6_CONFIG, IP_FW3_OPVER, HDIR_BOTH,nptv6_config }, + { IP_FW_NPTV6_LIST, IP_FW3_OPVER, HDIR_GET, nptv6_list }, + { IP_FW_NPTV6_STATS, IP_FW3_OPVER, HDIR_GET, nptv6_stats }, + { IP_FW_NPTV6_RESET_STATS, IP_FW3_OPVER, HDIR_SET, nptv6_reset_stats }, }; static int -nptv6_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +nptv6_classify(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) { ipfw_insn *icmd; - icmd = cmd - 1; - NPTV6_DEBUG("opcode %d, arg1 %d, opcode0 %d, arg1 %d", - cmd->opcode, cmd->arg1, icmd->opcode, icmd->arg1); + icmd = cmd0 - F_LEN(cmd0); + NPTV6_DEBUG("opcode %u, kidx %u, opcode0 %u, kidx %u", + cmd->opcode, insntod(cmd, kidx)->kidx, + icmd->opcode, insntod(icmd, kidx)->kidx); if (icmd->opcode != O_EXTERNAL_ACTION || - icmd->arg1 != V_nptv6_eid) + insntod(icmd, kidx)->kidx != V_nptv6_eid) return (1); - *puidx = cmd->arg1; + *puidx = insntod(cmd0, kidx)->kidx; *ptype = 0; return (0); } static void -nptv6_update_arg1(ipfw_insn *cmd, uint16_t idx) +nptv6_update_kidx(ipfw_insn *cmd0, uint32_t idx) { - cmd->arg1 = idx; - NPTV6_DEBUG("opcode %d, arg1 -> %d", cmd->opcode, cmd->arg1); + insntod(cmd0, kidx)->kidx = idx; + NPTV6_DEBUG("opcode %u, kidx -> %u", cmd->opcode, idx); } static int @@ -950,7 +951,7 @@ } static struct named_object * -nptv6_findbykidx(struct ip_fw_chain *ch, uint16_t idx) +nptv6_findbykidx(struct ip_fw_chain *ch, uint32_t idx) { struct namedobj_instance *ni; struct named_object *no; @@ -958,14 +959,14 @@ IPFW_UH_WLOCK_ASSERT(ch); ni = CHAIN_TO_SRV(ch); no = ipfw_objhash_lookup_kidx(ni, idx); - KASSERT(no != NULL, ("NPT with index %d not found", idx)); + KASSERT(no != NULL, ("NPT with index %u not found", idx)); NPTV6_DEBUG("kidx %u -> %s", idx, no->name); return (no); } static int -nptv6_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, +nptv6_manage_sets(struct ip_fw_chain *ch, uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) { @@ -978,7 +979,7 @@ .opcode = O_EXTERNAL_INSTANCE, .etlv = IPFW_TLV_EACTION /* just show it isn't table */, .classifier = nptv6_classify, - .update = nptv6_update_arg1, + .update = nptv6_update_kidx, .find_byname = nptv6_findbyname, .find_bykidx = nptv6_findbykidx, .manage_sets = nptv6_manage_sets, diff --git a/sys/netpfil/ipfw/pmod/tcpmod.c b/sys/netpfil/ipfw/pmod/tcpmod.c --- a/sys/netpfil/ipfw/pmod/tcpmod.c +++ b/sys/netpfil/ipfw/pmod/tcpmod.c @@ -53,7 +53,7 @@ #include -VNET_DEFINE_STATIC(uint16_t, tcpmod_setmss_eid) = 0; +VNET_DEFINE_STATIC(uint32_t, tcpmod_setmss_eid) = 0; #define V_tcpmod_setmss_eid VNET(tcpmod_setmss_eid) static int @@ -178,9 +178,9 @@ *done = 0; /* try next rule if not matched */ ret = IP_FW_DENY; - icmd = cmd + 1; + icmd = cmd + F_LEN(cmd); if (cmd->opcode != O_EXTERNAL_ACTION || - cmd->arg1 != V_tcpmod_setmss_eid || + insntod(cmd, kidx)->kidx != V_tcpmod_setmss_eid || icmd->opcode != O_EXTERNAL_DATA || icmd->len != F_INSN_SIZE(ipfw_insn)) return (ret); diff --git a/sys/sys/socket.h b/sys/sys/socket.h --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -266,7 +266,8 @@ #define AF_INET6_SDP 42 /* OFED Socket Direct Protocol ipv6 */ #define AF_HYPERV 43 /* HyperV sockets */ #define AF_DIVERT 44 /* divert(4) */ -#define AF_MAX 44 +#define AF_IPFWLOG 46 +#define AF_MAX 46 /* * When allocating a new AF_ constant, please only allocate * even numbered constants for FreeBSD until 134 as odd numbered AF_ @@ -392,6 +393,7 @@ #define PF_INET_SDP AF_INET_SDP #define PF_INET6_SDP AF_INET6_SDP #define PF_DIVERT AF_DIVERT +#define PF_IPFWLOG AF_IPFWLOG #define PF_MAX AF_MAX