git: 4a77657cbc01 - main - ipfw: migrate ipfw to 32-bit size rule numbers

From: Andrey V. Elsukov <ae_at_FreeBSD.org>
Date: Mon, 03 Mar 2025 19:00:47 UTC
The branch main has been updated by ae:

URL: https://cgit.FreeBSD.org/src/commit/?id=4a77657cbc011ea657ccb079fff6b58b295eccb0

commit 4a77657cbc011ea657ccb079fff6b58b295eccb0
Author:     Andrey V. Elsukov <ae@FreeBSD.org>
AuthorDate: 2025-03-03 18:15:17 +0000
Commit:     Andrey V. Elsukov <ae@FreeBSD.org>
CommitDate: 2025-03-03 18:15:17 +0000

    ipfw: migrate ipfw to 32-bit size rule numbers
    
    This changes ABI due to the changed opcodes and includes the
    following:
     * rule numbers and named object indexes converted to 32-bits
     * all hardcoded maximum rule number was replaced with
       IPFW_DEFAULT_RULE macro
     * now it is possible to grow maximum numbers or rules in
       build time
     * several opcodes converted to ipfw_insn_u32 to keep rulenum:
       O_CALL, O_SKIPTO
     * call stack modified to keep u32 rulenum. The behaviour of
       O_CALL opcode was changed to avoid possible packets looping.
       Now when call stack is overflowed or mbuf tag allocation
       failed, a packet will be dropped instead of skipping to next
       rule.
     * 'return' action now have two modes to specify return point:
       'next-rulenum' and 'next-rule'
     * new lookup key added for O_IP_DST_LOOKUP opcode 'lookup rulenum'
     * several opcodes converted to keep u32 named object indexes
       in special structure ipfw_insn_kidx
     * tables related opcodes modified to use two structures:
       ipfw_insn_kidx and ipfw_insn_table
     * added ability for table value matching for specific value type
       in 'table(name,valtype=value)' opcode
     * dynamic states and eaction code converted to use u32 rulenum
       and named objects indexes
     * added insntod() and insntoc() macros to cast to specific
       ipfw instruction type
     * default sockopt version was changed to IP_FW3_OPVER=1
     * FreeBSD 7-11 rule format support was removed
     * added ability to generate special rtsock messages via log opcode
     * added IP_FW_SKIPTO_CACHE sockopt to enable/disable skipto cache.
       It helps to reduce overhead when many rules are modified in batch.
     * added ability to keep NAT64LSN states during sets swapping
    
    Obtained from:  Yandex LLC
    Relnotes:       yes
    Sponsored by:   Yandex LLC
    Differential Revision:  https://reviews.freebsd.org/D46183
---
 sbin/ipfw/ipfw.8                           |   18 +-
 sbin/ipfw/ipfw2.c                          |  593 +++++---
 sbin/ipfw/ipfw2.h                          |    4 +-
 sbin/ipfw/nat64lsn.c                       |   16 +
 sbin/ipfw/tables.c                         |    2 +-
 sbin/route/route.c                         |    1 +
 sys/net/route.h                            |    1 +
 sys/netinet/ip_fw.h                        |  227 ++-
 sys/netinet6/ip_fw_nat64.h                 |   10 +-
 sys/netpfil/ipfw/ip_fw2.c                  |  471 +++---
 sys/netpfil/ipfw/ip_fw_compat.c            |  714 +++++++++
 sys/netpfil/ipfw/ip_fw_dynamic.c           |  257 ++--
 sys/netpfil/ipfw/ip_fw_eaction.c           |   71 +-
 sys/netpfil/ipfw/ip_fw_iface.c             |   10 +-
 sys/netpfil/ipfw/ip_fw_log.c               |  315 +++-
 sys/netpfil/ipfw/ip_fw_nat.c               |   10 +-
 sys/netpfil/ipfw/ip_fw_private.h           |  136 +-
 sys/netpfil/ipfw/ip_fw_sockopt.c           | 2171 ++++++++--------------------
 sys/netpfil/ipfw/ip_fw_table.c             |  483 ++-----
 sys/netpfil/ipfw/ip_fw_table.h             |   16 +-
 sys/netpfil/ipfw/ip_fw_table_algo.c        |   11 +-
 sys/netpfil/ipfw/ip_fw_table_value.c       |   52 +-
 sys/netpfil/ipfw/nat64/ip_fw_nat64.h       |   48 +
 sys/netpfil/ipfw/nat64/nat64clat.c         |    8 +-
 sys/netpfil/ipfw/nat64/nat64clat.h         |    2 +-
 sys/netpfil/ipfw/nat64/nat64clat_control.c |   80 +-
 sys/netpfil/ipfw/nat64/nat64lsn.c          |  390 +++--
 sys/netpfil/ipfw/nat64/nat64lsn.h          |   68 +-
 sys/netpfil/ipfw/nat64/nat64lsn_control.c  |  285 ++--
 sys/netpfil/ipfw/nat64/nat64stl.c          |   10 +-
 sys/netpfil/ipfw/nat64/nat64stl.h          |    6 +-
 sys/netpfil/ipfw/nat64/nat64stl_control.c  |   76 +-
 sys/netpfil/ipfw/nptv6/nptv6.c             |   49 +-
 sys/netpfil/ipfw/pmod/tcpmod.c             |    6 +-
 sys/sys/socket.h                           |    4 +-
 35 files changed, 3389 insertions(+), 3232 deletions(-)

diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index bc78ae1c655b..348e9a58f2ce 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,5 +1,5 @@
 .\"
-.Dd December 6, 2024
+.Dd March 3, 2025
 .Dt IPFW 8
 .Os
 .Sh NAME
@@ -40,10 +40,10 @@ in-kernel NAT.
 .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 @@ Matches any IPv4 or IPv6 address for which an entry exists in the lookup table
 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 @@ One or more
 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.c b/sbin/ipfw/ipfw2.c
index cc0970207749..be796808380f 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -290,6 +290,12 @@ static struct _s_x rule_actions[] = {
 	{ 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 @@ static struct _s_x rule_action_params[] = {
 
 /*
  * 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 @@ static struct _s_x lookup_keys[] = {
 	{ "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 int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
 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 @@ do_set3(int optname, ip_fw3_opheader *op3, size_t optlen)
 {
 
 	op3->opcode = optname;
+	op3->version = IP_FW3_OPVER; /* use last version */
 
 	if (g_co.debug_only) {
 		struct debug_header dbg = {
@@ -690,6 +717,7 @@ do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
 	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 @@ print_flags(struct buf_pr *bp, char const *name, const ipfw_insn *cmd,
 	}
 }
 
+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: "<invalid>", cmd->value);
+}
+
 
 /*
  * Print the ip address contained in a command.
@@ -1295,33 +1332,46 @@ print_ip(struct buf_pr *bp, const struct format_opts *fo,
 	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 : "<invalid>"),
+				    insntoc(&cmd->o, table)->value, t);
+			} else
+				bprintf(bp, "lookup %s %s", key != NULL ? key:
+				    "<invalid>", 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 @@ print_mac_lookup(struct buf_pr *bp, const struct format_opts *fo,
 
 	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 @@ print_dscp(struct buf_pr *bp, const ipfw_insn_u32 *cmd)
 	}
 }
 
-#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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 
 	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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 	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_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 			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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 			}
 			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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 			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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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 @@ print_fwd(struct buf_pr *bp, const ipfw_insn *cmd)
 	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 @@ print_fwd(struct buf_pr *bp, const ipfw_insn *cmd)
 		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 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
 	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 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
 			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_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		 * 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 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		 * 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 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
 		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: "<invalid>");
+		} 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 @@ print_address(struct buf_pr *bp, struct format_opts *fo,
 	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 @@ static const int dst_opcodes[] = {
 	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 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo,
 		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) {
@@ -2423,23 +2518,21 @@ show_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
 {
 	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");
@@ -2531,9 +2624,8 @@ ipfw_sets_handler(char *av[])
 	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++;
@@ -2584,7 +2676,7 @@ ipfw_sets_handler(char *av[])
 		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;
@@ -2637,6 +2729,18 @@ ipfw_sets_handler(char *av[])
 		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)
 {
@@ -2661,6 +2765,8 @@ ipfw_sysctl_handler(char *av[], int which)
 	} 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);
@@ -2679,7 +2785,6 @@ prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo,
 {
 	ipfw_dyn_rule *d;
 	int width;
-	uint8_t set;
 
 	d = (ipfw_dyn_rule *)_state;
 	/* Count _ALL_ states */
@@ -2688,13 +2793,9 @@ prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo,
 	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)
@@ -2822,24 +2923,17 @@ static void
 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);
@@ -3165,7 +3259,7 @@ eaction_check_name(const char *name)
 	return (0);
 }
 
-static uint16_t
+static uint32_t
 pack_object(struct tidx *tstate, const char *name, int otype)
 {
 	ipfw_obj_ntlv *ntlv;
@@ -3202,7 +3296,7 @@ pack_object(struct tidx *tstate, const char *name, int otype)
 	return (ntlv->idx);
 }
 
-static uint16_t
+static uint32_t
 pack_table(struct tidx *tstate, const char *name)
 {
 
@@ -3212,12 +3306,32 @@ pack_table(struct tidx *tstate, const char *name)
 	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)
@@ -3227,19 +3341,19 @@ fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode,
 	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.
@@ -3470,7 +3584,7 @@ ipfw_delete(char *av[])
 {
 	ipfw_range_tlv rt;
 	char *sep;
-	int i, j;
+	uint32_t i, j;
 	int exitval = EX_OK;
 	int do_set = 0;
 
@@ -3503,8 +3617,8 @@ ipfw_delete(char *av[])
 				rt.set = i & 31;
 				rt.flags = IPFW_RCFLAG_SET;
 			} else {
-				rt.start_rule = i & 0xffff;
*** 9719 LINES SKIPPED ***