Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 11 Apr 2018 10:24:47 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r332400 - stable/11/sbin/ipfw
Message-ID:  <201804111024.w3BAOl1j004671@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Wed Apr 11 10:24:47 2018
New Revision: 332400
URL: https://svnweb.freebsd.org/changeset/base/332400

Log:
  MFC r331668:
    Rework ipfw rules parsing and printing code.
  
    Introduce show_state structure to keep information about printed opcodes.
    Split show_static_rule() function into several smaller functions. Make
    parsing and printing opcodes into several passes. Each printed opcode
    is marked in show_state structure and will be skipped in next passes.
    Now show_static_rule() function is simple, it just prints each part
    of rule separately: action, modifiers, proto, src and dst addresses,
    options. The main goal of this change is avoiding occurrence of wrong
    result of `ifpw show` command, that can not be parsed by ipfw(8).
    Also now it is possible to make some simple static optimizations
    by reordering of opcodes in the rule.
  
    PR:		222705

Modified:
  stable/11/sbin/ipfw/ipfw2.c
  stable/11/sbin/ipfw/ipfw2.h
  stable/11/sbin/ipfw/main.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sbin/ipfw/ipfw2.c
==============================================================================
--- stable/11/sbin/ipfw/ipfw2.c	Wed Apr 11 07:15:30 2018	(r332399)
+++ stable/11/sbin/ipfw/ipfw2.c	Wed Apr 11 10:24:47 2018	(r332400)
@@ -1176,7 +1176,7 @@ print_flags(struct buf_pr *bp, char const *name, ipfw_
  * Print the ip address contained in a command.
  */
 static void
-print_ip(struct buf_pr *bp, struct format_opts *fo, ipfw_insn_ip *cmd,
+print_ip(struct buf_pr *bp, const struct format_opts *fo, ipfw_insn_ip *cmd,
     char const *s)
 {
 	struct hostent *he = NULL;
@@ -1277,7 +1277,7 @@ print_ip(struct buf_pr *bp, struct format_opts *fo, ip
  * prints a MAC address/mask pair
  */
 static void
-print_mac(struct buf_pr *bp, uint8_t *addr, uint8_t *mask)
+format_mac(struct buf_pr *bp, uint8_t *addr, uint8_t *mask)
 {
 	int l = contigmask(mask, 48);
 
@@ -1296,6 +1296,15 @@ print_mac(struct buf_pr *bp, uint8_t *addr, uint8_t *m
 }
 
 static void
+print_mac(struct buf_pr *bp, ipfw_insn_mac *mac)
+{
+
+	bprintf(bp, " MAC");
+	format_mac(bp, mac->addr, mac->mask);
+	format_mac(bp, mac->addr + 6, mac->mask + 6);
+}
+
+static void
 fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
 {
 	uint8_t type;
@@ -1358,817 +1367,843 @@ print_dscp(struct buf_pr *bp, ipfw_insn_u32 *cmd)
 	}
 }
 
-/*
- * show_ipfw() prints the body of an ipfw rule.
- * Because the standard rule has at least proto src_ip dst_ip, we use
- * a helper function to produce these entries if not provided explicitly.
- * The first argument is the list of fields we have, the second is
- * the list of fields we want to be printed.
- *
- * Special cases if we have provided a MAC header:
- *   + if the rule does not contain IP addresses/ports, do not print them;
- *   + if the rule does not contain an IP proto, print "all" instead of "ip";
- *
- * Once we have 'have_options', IP header fields are printed as options.
- */
+#define	insntod(cmd, type)	((ipfw_insn_ ## type *)(cmd))
+struct show_state {
+	struct ip_fw_rule	*rule;
+	const ipfw_insn		*eaction;
+	uint8_t			*printed;
+	int			flags;
 #define	HAVE_PROTO	0x0001
 #define	HAVE_SRCIP	0x0002
 #define	HAVE_DSTIP	0x0004
-#define	HAVE_PROTO4	0x0008
-#define	HAVE_PROTO6	0x0010
-#define	HAVE_IP		0x0100
-#define	HAVE_OPTIONS	0x8000
+	int			proto;
+	int			or_block;
+};
 
-static void
-show_prerequisites(struct buf_pr *bp, int *flags, int want, int cmd)
+static int
+init_show_state(struct show_state *state, struct ip_fw_rule *rule)
 {
-	(void)cmd;	/* UNUSED */
-	if (co.comment_only)
-		return;
-	if ( (*flags & HAVE_IP) == HAVE_IP)
-		*flags |= HAVE_OPTIONS;
 
-	if ( !(*flags & HAVE_OPTIONS)) {
-		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) {
-			if ( (*flags & HAVE_PROTO4))
-				bprintf(bp, " ip4");
-			else if ( (*flags & HAVE_PROTO6))
-				bprintf(bp, " ip6");
-			else
-				bprintf(bp, " ip");
-		}
-		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
-			bprintf(bp, " from any");
-		if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
-			bprintf(bp, " to any");
-	}
-	*flags |= want;
+	state->printed = calloc(rule->cmd_len, sizeof(uint8_t));
+	if (state->printed == NULL)
+		return (ENOMEM);
+	state->rule = rule;
+	state->eaction = NULL;
+	state->flags = 0;
+	state->proto = 0;
+	state->or_block = 0;
+	return (0);
 }
 
 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)
+free_show_state(struct show_state *state)
 {
-	static int twidth = 0;
-	int l;
-	ipfw_insn *cmd, *has_eaction = NULL, *tagptr = NULL;
-	const char *comment = NULL;	/* ptr to comment if we have one */
-	const char *ename;
-	int proto = 0;		/* default */
-	int flags = 0;	/* prerequisites */
-	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
-	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
-	int or_block = 0;	/* we are in an or block */
-	uint32_t uval;
 
-	if ((fo->set_mask & (1 << rule->set)) == 0) {
-		/* disabled mask */
-		if (!co->show_sets)
-			return;
-		else
-			bprintf(bp, "# DISABLED ");
-	}
-	bprintf(bp, "%05u ", rule->rulenum);
+	free(state->printed);
+}
 
-	/* Print counters if enabled */
-	if (fo->pcwidth > 0 || fo->bcwidth > 0) {
-		pr_u64(bp, &cntr->pcnt, fo->pcwidth);
-		pr_u64(bp, &cntr->bcnt, fo->bcwidth);
-	}
+static uint8_t
+is_printed_opcode(struct show_state *state, const ipfw_insn *cmd)
+{
 
-	if (co->do_time == 2)
-		bprintf(bp, "%10u ", cntr->timestamp);
-	else if (co->do_time == 1) {
-		char timestr[30];
-		time_t t = (time_t)0;
+	return (state->printed[cmd - state->rule->cmd]);
+}
 
-		if (twidth == 0) {
-			strcpy(timestr, ctime(&t));
-			*strchr(timestr, '\n') = '\0';
-			twidth = strlen(timestr);
-		}
-		if (cntr->timestamp > 0) {
-			t = _long_to_time(cntr->timestamp);
+static void
+mark_printed(struct show_state *state, const ipfw_insn *cmd)
+{
 
-			strcpy(timestr, ctime(&t));
-			*strchr(timestr, '\n') = '\0';
-			bprintf(bp, "%s ", timestr);
-		} else {
-			bprintf(bp, "%*s", twidth, " ");
-		}
-	}
+	state->printed[cmd - state->rule->cmd] = 1;
+}
 
-	if (co->show_sets)
-		bprintf(bp, "set %d ", rule->set);
+static void
+print_limit(struct buf_pr *bp, const ipfw_insn_limit *limit)
+{
+	struct _s_x *p = limit_masks;
+	char const *comma = " ";
+	uint8_t x;
 
-	/*
-	 * print the optional "match probability"
-	 */
-	if (rule->cmd_len > 0) {
-		cmd = rule->cmd ;
-		if (cmd->opcode == O_PROB) {
-			ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
-			double d = 1.0 * p->d[0];
-
-			d = (d / 0x7fffffff);
-			bprintf(bp, "prob %f ", d);
+	bprintf(bp, " limit");
+	for (x = limit->limit_mask; p->x != 0; p++) {
+		if ((x & p->x) == p->x) {
+			x &= ~p->x;
+			bprintf(bp, "%s%s", comma, p->s);
+			comma = ",";
 		}
 	}
+	bprint_uint_arg(bp, " ", limit->conn_limit);
+}
 
-	/*
-	 * first print actions
-	 */
-	for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
-			l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
-		switch(cmd->opcode) {
-		case O_CHECK_STATE:
-			bprintf(bp, "check-state");
-			if (cmd->arg1 != 0)
-				ename = object_search_ctlv(fo->tstate,
-				    cmd->arg1, IPFW_TLV_STATE_NAME);
-			else
-				ename = NULL;
-			bprintf(bp, " :%s", ename ? ename: "any");
-			/* avoid printing anything else */
-			flags = HAVE_PROTO | HAVE_SRCIP |
-				HAVE_DSTIP | HAVE_IP;
-			break;
+static int
+print_instruction(struct buf_pr *bp, const struct format_opts *fo,
+    struct show_state *state, ipfw_insn *cmd)
+{
+	struct protoent *pe;
+	struct passwd *pwd;
+	struct group *grp;
+	const char *s;
+	double d;
 
-		case O_ACCEPT:
-			bprintf(bp, "allow");
-			break;
+	if (is_printed_opcode(state, cmd))
+		return (0);
+	if ((cmd->len & F_OR) != 0 && state->or_block == 0)
+		bprintf(bp, " {");
+	if (cmd->opcode != O_IN && (cmd->len & F_NOT) != 0)
+		bprintf(bp, " not");
 
-		case O_COUNT:
-			bprintf(bp, "count");
+	switch (cmd->opcode) {
+	case O_PROB:
+		d = 1.0 * insntod(cmd, u32)->d[0] / 0x7fffffff;
+		bprintf(bp, "prob %f ", d);
+		break;
+	case O_PROBE_STATE: /* no need to print anything here */
+		break;
+	case O_IP_SRC:
+	case O_IP_SRC_LOOKUP:
+	case O_IP_SRC_MASK:
+	case O_IP_SRC_ME:
+	case O_IP_SRC_SET:
+	case O_IP_DST:
+	case O_IP_DST_LOOKUP:
+	case O_IP_DST_MASK:
+	case O_IP_DST_ME:
+	case O_IP_DST_SET:
+		print_ip(bp, fo, insntod(cmd, ip), "");
+		break;
+	case O_IP6_SRC:
+	case O_IP6_SRC_MASK:
+	case O_IP6_SRC_ME:
+	case O_IP6_DST:
+	case O_IP6_DST_MASK:
+	case O_IP6_DST_ME:
+		print_ip6(bp, insntod(cmd, ip6), "");
+		break;
+	case O_FLOW6ID:
+		print_flow6id(bp, insntod(cmd, u32));
+		break;
+	case O_IP_DSTPORT:
+	case O_IP_SRCPORT:
+		print_newports(bp, insntod(cmd, u16), state->proto,
+		    (state->flags & (HAVE_SRCIP | HAVE_DSTIP)) ==
+		    (HAVE_SRCIP | HAVE_DSTIP) ?  cmd->opcode: 0);
+		break;
+	case O_PROTO:
+		pe = getprotobynumber(cmd->arg1);
+		if (state->flags & HAVE_PROTO)
+			bprintf(bp, " proto");
+		if (pe != NULL)
+			bprintf(bp, " %s", pe->p_name);
+		else
+			bprintf(bp, " %u", cmd->arg1);
+		break;
+	case O_MACADDR2:
+		print_mac(bp, insntod(cmd, mac));
+		break;
+	case O_MAC_TYPE:
+		print_newports(bp, insntod(cmd, u16),
+		    IPPROTO_ETHERTYPE, cmd->opcode);
+		break;
+	case O_FRAG:
+		bprintf(bp, " frag");
+		break;
+	case O_FIB:
+		bprintf(bp, " fib %u", cmd->arg1);
+		break;
+	case O_SOCKARG:
+		bprintf(bp, " sockarg");
+		break;
+	case O_IN:
+		bprintf(bp, cmd->len & F_NOT ? " out" : " in");
+		break;
+	case O_DIVERTED:
+		switch (cmd->arg1) {
+		case 3:
+			bprintf(bp, " diverted");
 			break;
-
-		case O_DENY:
-			bprintf(bp, "deny");
+		case 2:
+			bprintf(bp, " diverted-output");
 			break;
-
-		case O_REJECT:
-			if (cmd->arg1 == ICMP_REJECT_RST)
-				bprintf(bp, "reset");
-			else if (cmd->arg1 == ICMP_REJECT_ABORT)
-				bprintf(bp, "abort");
-			else if (cmd->arg1 == ICMP_UNREACH_HOST)
-				bprintf(bp, "reject");
-			else
-				print_reject_code(bp, cmd->arg1);
+		case 1:
+			bprintf(bp, " diverted-loopback");
 			break;
-
-		case O_UNREACH6:
-			if (cmd->arg1 == ICMP6_UNREACH_RST)
-				bprintf(bp, "reset6");
-			else if (cmd->arg1 == ICMP6_UNREACH_ABORT)
-				bprintf(bp, "abort6");
-			else
-				print_unreach6_code(bp, cmd->arg1);
+		default:
+			bprintf(bp, " diverted-?<%u>", cmd->arg1);
 			break;
-
-		case O_SKIPTO:
-			bprint_uint_arg(bp, "skipto ", cmd->arg1);
+		}
+		break;
+	case O_LAYER2:
+		bprintf(bp, " layer2");
+		break;
+	case O_XMIT:
+	case O_RECV:
+	case O_VIA:
+		if (cmd->opcode == O_XMIT)
+			s = "xmit";
+		else if (cmd->opcode == O_RECV)
+			s = "recv";
+		else /* if (cmd->opcode == O_VIA) */
+			s = "via";
+		switch (insntod(cmd, if)->name[0]) {
+		case '\0':
+			bprintf(bp, " %s %s", s,
+			    inet_ntoa(insntod(cmd, if)->p.ip));
 			break;
-
-		case O_PIPE:
-			bprint_uint_arg(bp, "pipe ", cmd->arg1);
+		case '\1':
+			bprintf(bp, " %s table(%s)", s,
+			    table_search_ctlv(fo->tstate,
+			    insntod(cmd, if)->p.kidx));
 			break;
-
-		case O_QUEUE:
-			bprint_uint_arg(bp, "queue ", cmd->arg1);
-			break;
-
-		case O_DIVERT:
-			bprint_uint_arg(bp, "divert ", cmd->arg1);
-			break;
-
-		case O_TEE:
-			bprint_uint_arg(bp, "tee ", cmd->arg1);
-			break;
-
-		case O_NETGRAPH:
-			bprint_uint_arg(bp, "netgraph ", cmd->arg1);
-			break;
-
-		case O_NGTEE:
-			bprint_uint_arg(bp, "ngtee ", cmd->arg1);
-			break;
-
-		case O_FORWARD_IP:
-		    {
-			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
-
-			if (s->sa.sin_addr.s_addr == INADDR_ANY) {
-				bprintf(bp, "fwd tablearg");
-			} else {
-				bprintf(bp, "fwd %s",inet_ntoa(s->sa.sin_addr));
-			}
-			if (s->sa.sin_port)
-				bprintf(bp, ",%d", s->sa.sin_port);
-		    }
-			break;
-
-		case O_FORWARD_IP6:
-		    {
-			char buf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2];
-			ipfw_insn_sa6 *s = (ipfw_insn_sa6 *)cmd;
-
-			bprintf(bp, "fwd ");
-			if (getnameinfo((const struct sockaddr *)&s->sa,
-			    sizeof(struct sockaddr_in6), buf, sizeof(buf),
-			    NULL, 0, NI_NUMERICHOST) == 0)
-				bprintf(bp, "%s", buf);
-			if (s->sa.sin6_port)
-				bprintf(bp, ",%d", s->sa.sin6_port);
-		    }
-			break;
-
-		case O_LOG: /* O_LOG is printed last */
-			logptr = (ipfw_insn_log *)cmd;
-			break;
-
-		case O_ALTQ: /* O_ALTQ is printed after O_LOG */
-			altqptr = (ipfw_insn_altq *)cmd;
-			break;
-
-		case O_TAG:
-			tagptr = cmd;
-			break;
-
-		case O_NAT:
-			if (cmd->arg1 != IP_FW_NAT44_GLOBAL)
-				bprint_uint_arg(bp, "nat ", cmd->arg1);
-			else
-				bprintf(bp, "nat global");
-			break;
-
-		case O_SETFIB:
-			if (cmd->arg1 == IP_FW_TARG)
-				bprint_uint_arg(bp, "setfib ", cmd->arg1);
-			else
-				bprintf(bp, "setfib %u", cmd->arg1 & 0x7FFF);
-			break;
-
-		case O_EXTERNAL_ACTION: {
-			/*
-			 * The external action can consists of two following
-			 * each other opcodes - O_EXTERNAL_ACTION and
-			 * O_EXTERNAL_INSTANCE. The first contains the ID of
-			 * name of external action. The second contains the ID
-			 * of name of external action instance.
-			 * NOTE: in case when external action has no named
-			 * instances support, the second opcode isn't needed.
-			 */
-			has_eaction = cmd;
-			ename = object_search_ctlv(fo->tstate, cmd->arg1,
-			    IPFW_TLV_EACTION);
-			if (match_token(rule_eactions, ename) != -1)
-				bprintf(bp, "%s", ename);
-			else
-				bprintf(bp, "eaction %s", ename);
-			break;
+		default:
+			bprintf(bp, " %s %s", s,
+			    insntod(cmd, if)->name);
 		}
-
-		case O_EXTERNAL_INSTANCE: {
-			if (has_eaction == NULL)
+		break;
+	case O_IP_FLOW_LOOKUP:
+		s = table_search_ctlv(fo->tstate, cmd->arg1);
+		bprintf(bp, " flow table(%s", s);
+		if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))
+			bprintf(bp, ",%u", insntod(cmd, u32)->d[0]);
+		bprintf(bp, ")");
+		break;
+	case O_IPID:
+	case O_IPTTL:
+	case O_IPLEN:
+	case O_TCPDATALEN:
+	case O_TCPWIN:
+		if (F_LEN(cmd) == 1) {
+			switch (cmd->opcode) {
+			case O_IPID:
+				s = "ipid";
 				break;
-			/*
-			 * XXX: we need to teach ipfw(9) to rewrite opcodes
-			 * in the user buffer on rule addition. When we add
-			 * the rule, we specify zero TLV type for
-			 * O_EXTERNAL_INSTANCE object. To show correct
-			 * rule after `ipfw add` we need to search instance
-			 * name with zero type. But when we do `ipfw show`
-			 * we calculate TLV type using IPFW_TLV_EACTION_NAME()
-			 * macro.
-			 */
-			ename = object_search_ctlv(fo->tstate, cmd->arg1, 0);
-			if (ename == NULL)
-				ename = object_search_ctlv(fo->tstate,
-				    cmd->arg1,
-				    IPFW_TLV_EACTION_NAME(has_eaction->arg1));
-			bprintf(bp, " %s", ename);
-			break;
-		}
-
-		case O_EXTERNAL_DATA: {
-			if (has_eaction == NULL)
+			case O_IPTTL:
+				s = "ipttl";
 				break;
-			/*
-			 * Currently we support data formatting only for
-			 * external data with datalen u16. For unknown data
-			 * print its size in bytes.
-			 */
-			if (cmd->len == F_INSN_SIZE(ipfw_insn))
-				bprintf(bp, " %u", cmd->arg1);
-			else
-				bprintf(bp, " %ubytes",
-				    cmd->len * sizeof(uint32_t));
-			break;
-		}
-
-		case O_SETDSCP:
-		    {
-			const char *code;
-
-			if (cmd->arg1 == IP_FW_TARG) {
-				bprint_uint_arg(bp, "setdscp ", cmd->arg1);
+			case O_IPLEN:
+				s = "iplen";
 				break;
+			case O_TCPDATALEN:
+				s = "tcpdatalen";
+				break;
+			case O_TCPWIN:
+				s = "tcpwin";
+				break;
 			}
-			uval = cmd->arg1 & 0x3F;
-			if ((code = match_value(f_ipdscp, uval)) != NULL)
-				bprintf(bp, "setdscp %s", code);
-			else
-				bprint_uint_arg(bp, "setdscp ", uval);
-		    }
- 			break;
+			bprintf(bp, " %s %u", s, cmd->arg1);
+		} else
+			print_newports(bp, insntod(cmd, u16), 0,
+			    cmd->opcode);
+		break;
+	case O_IPVER:
+		bprintf(bp, " ipver %u", cmd->arg1);
+		break;
+	case O_IPPRECEDENCE:
+		bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5);
+		break;
+	case O_DSCP:
+		print_dscp(bp, insntod(cmd, u32));
+		break;
+	case O_IPOPT:
+		print_flags(bp, "ipoptions", cmd, f_ipopts);
+		break;
+	case O_IPTOS:
+		print_flags(bp, "iptos", cmd, f_iptos);
+		break;
+	case O_ICMPTYPE:
+		print_icmptypes(bp, insntod(cmd, u32));
+		break;
+	case O_ESTAB:
+		bprintf(bp, " established");
+		break;
+	case O_TCPFLAGS:
+		print_flags(bp, "tcpflags", cmd, f_tcpflags);
+		break;
+	case O_TCPOPTS:
+		print_flags(bp, "tcpoptions", cmd, f_tcpopts);
+		break;
+	case O_TCPACK:
+		bprintf(bp, " tcpack %d",
+		    ntohl(insntod(cmd, u32)->d[0]));
+		break;
+	case O_TCPSEQ:
+		bprintf(bp, " tcpseq %d",
+		    ntohl(insntod(cmd, u32)->d[0]));
+		break;
+	case O_UID:
+		pwd = getpwuid(insntod(cmd, u32)->d[0]);
+		if (pwd != NULL)
+			bprintf(bp, " uid %s", pwd->pw_name);
+		else
+			bprintf(bp, " uid %u",
+			    insntod(cmd, u32)->d[0]);
+		break;
+	case O_GID:
+		grp = getgrgid(insntod(cmd, u32)->d[0]);
+		if (grp != NULL)
+			bprintf(bp, " gid %s", grp->gr_name);
+		else
+			bprintf(bp, " gid %u",
+			    insntod(cmd, u32)->d[0]);
+		break;
+	case O_JAIL:
+		bprintf(bp, " jail %d", insntod(cmd, u32)->d[0]);
+		break;
+	case O_VERREVPATH:
+		bprintf(bp, " verrevpath");
+		break;
+	case O_VERSRCREACH:
+		bprintf(bp, " versrcreach");
+		break;
+	case O_ANTISPOOF:
+		bprintf(bp, " antispoof");
+		break;
+	case O_IPSEC:
+		bprintf(bp, " ipsec");
+		break;
+	case O_NOP:
+		bprintf(bp, " // %s", (char *)(cmd + 1));
+		break;
+	case O_KEEP_STATE:
+		bprintf(bp, " keep-state");
+		bprintf(bp, " :%s",
+		    object_search_ctlv(fo->tstate, cmd->arg1,
+		    IPFW_TLV_STATE_NAME));
+		break;
+	case O_LIMIT:
+		print_limit(bp, insntod(cmd, limit));
+		bprintf(bp, " :%s",
+		    object_search_ctlv(fo->tstate, cmd->arg1,
+		    IPFW_TLV_STATE_NAME));
+		break;
+	case O_IP6:
+		bprintf(bp, " ip6");
+		break;
+	case O_IP4:
+		bprintf(bp, " ip4");
+		break;
+	case O_ICMP6TYPE:
+		print_icmp6types(bp, insntod(cmd, u32));
+		break;
+	case O_EXT_HDR:
+		print_ext6hdr(bp, cmd);
+		break;
+	case O_TAGGED:
+		if (F_LEN(cmd) == 1)
+			bprint_uint_arg(bp, " tagged ", cmd->arg1);
+		else
+			print_newports(bp, insntod(cmd, u16),
+				    0, O_TAGGED);
+		break;
+	default:
+		bprintf(bp, " [opcode %d len %d]", cmd->opcode,
+		    cmd->len);
+	}
+	if (cmd->len & F_OR) {
+		bprintf(bp, " or");
+		state->or_block = 1;
+	} else if (state->or_block != 0) {
+		bprintf(bp, " }");
+		state->or_block = 0;
+	}
+	mark_printed(state, cmd);
 
-		case O_REASS:
-			bprintf(bp, "reass");
-			break;
+	return (1);
+}
 
-		case O_CALLRETURN:
-			if (cmd->len & F_NOT)
-				bprintf(bp, "return");
-			else
-				bprint_uint_arg(bp, "call ", cmd->arg1);
-			break;
+static ipfw_insn *
+print_opcode(struct buf_pr *bp, struct format_opts *fo,
+    struct show_state *state, uint8_t opcode)
+{
+	ipfw_insn *cmd;
+	int l;
 
-		default:
-			bprintf(bp, "** unrecognized action %d len %d ",
-				cmd->opcode, cmd->len);
-		}
+	for (l = state->rule->act_ofs, cmd = state->rule->cmd;
+	    l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
+		/* We use zero opcode to print the rest of options */
+		if (opcode != 0 && cmd->opcode != opcode)
+			continue;
+		/*
+		 * Skip O_NOP, when we printing the rest
+		 * of options, it will be handled separately.
+		 */
+		if (cmd->opcode == O_NOP && opcode != O_NOP)
+			continue;
+		if (!print_instruction(bp, fo, state, cmd))
+			continue;
+		return (cmd);
 	}
-	if (logptr) {
-		if (logptr->max_log > 0)
-			bprintf(bp, " log logamount %d", logptr->max_log);
+	return (NULL);
+}
+
+static void
+print_fwd(struct buf_pr *bp, const ipfw_insn *cmd)
+{
+	char buf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2];
+	ipfw_insn_sa6 *sa6;
+	ipfw_insn_sa *sa;
+	uint16_t port;
+
+	if (cmd->opcode == O_FORWARD_IP) {
+		sa = insntod(cmd, sa);
+		port = sa->sa.sin_port;
+		if (sa->sa.sin_addr.s_addr == INADDR_ANY)
+			bprintf(bp, "fwd tablearg");
 		else
-			bprintf(bp, " log");
+			bprintf(bp, "fwd %s", inet_ntoa(sa->sa.sin_addr));
+	} else {
+		sa6 = insntod(cmd, sa6);
+		port = sa6->sa.sin6_port;
+		bprintf(bp, "fwd ");
+		if (getnameinfo((const struct sockaddr *)&sa6->sa,
+		    sizeof(struct sockaddr_in6), buf, sizeof(buf), NULL, 0,
+		    NI_NUMERICHOST) == 0)
+			bprintf(bp, "%s", buf);
 	}
+	if (port != 0)
+		bprintf(bp, ",%u", port);
+}
+
+static int
+print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
+    struct show_state *state, const ipfw_insn *cmd)
+{
+	const char *s;
+
+	if (is_printed_opcode(state, cmd))
+		return (0);
+	switch (cmd->opcode) {
+	case O_CHECK_STATE:
+		bprintf(bp, "check-state");
+		if (cmd->arg1 != 0)
+			s = object_search_ctlv(fo->tstate, cmd->arg1,
+			    IPFW_TLV_STATE_NAME);
+		else
+			s = NULL;
+		bprintf(bp, " :%s", s ? s: "any");
+		break;
+	case O_ACCEPT:
+		bprintf(bp, "allow");
+		break;
+	case O_COUNT:
+		bprintf(bp, "count");
+		break;
+	case O_DENY:
+		bprintf(bp, "deny");
+		break;
+	case O_REJECT:
+		if (cmd->arg1 == ICMP_REJECT_RST)
+			bprintf(bp, "reset");
+		else if (cmd->arg1 == ICMP_REJECT_ABORT)
+			bprintf(bp, "abort");
+		else if (cmd->arg1 == ICMP_UNREACH_HOST)
+			bprintf(bp, "reject");
+		else
+			print_reject_code(bp, cmd->arg1);
+		break;
+	case O_UNREACH6:
+		if (cmd->arg1 == ICMP6_UNREACH_RST)
+			bprintf(bp, "reset6");
+		else if (cmd->arg1 == ICMP6_UNREACH_ABORT)
+			bprintf(bp, "abort6");
+		else
+			print_unreach6_code(bp, cmd->arg1);
+		break;
+	case O_SKIPTO:
+		bprint_uint_arg(bp, "skipto ", cmd->arg1);
+		break;
+	case O_PIPE:
+		bprint_uint_arg(bp, "pipe ", cmd->arg1);
+		break;
+	case O_QUEUE:
+		bprint_uint_arg(bp, "queue ", cmd->arg1);
+		break;
+	case O_DIVERT:
+		bprint_uint_arg(bp, "divert ", cmd->arg1);
+		break;
+	case O_TEE:
+		bprint_uint_arg(bp, "tee ", cmd->arg1);
+		break;
+	case O_NETGRAPH:
+		bprint_uint_arg(bp, "netgraph ", cmd->arg1);
+		break;
+	case O_NGTEE:
+		bprint_uint_arg(bp, "ngtee ", cmd->arg1);
+		break;
+	case O_FORWARD_IP:
+	case O_FORWARD_IP6:
+		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");
+		break;
+	case O_ALTQ:
 #ifndef NO_ALTQ
-	if (altqptr) {
-		print_altq_cmd(bp, altqptr);
-	}
+		print_altq_cmd(bp, insntod(cmd, altq));
 #endif
-	if (tagptr) {
-		if (tagptr->len & F_NOT)
-			bprint_uint_arg(bp, " untag ", tagptr->arg1);
+		break;
+	case O_TAG:
+		bprint_uint_arg(bp, cmd->len & F_NOT ? " untag ":
+		    " tag ", cmd->arg1);
+		break;
+	case O_NAT:
+		if (cmd->arg1 != IP_FW_NAT44_GLOBAL)
+			bprint_uint_arg(bp, "nat ", cmd->arg1);
 		else
-			bprint_uint_arg(bp, " tag ", tagptr->arg1);
-	}
-
-	/*
-	 * then print the body.
-	 */
-	for (l = rule->act_ofs, cmd = rule->cmd;
-			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
-		if ((cmd->len & F_OR) || (cmd->len & F_NOT))
-			continue;
-		if (cmd->opcode == O_IP4) {
-			flags |= HAVE_PROTO4;
+			bprintf(bp, "nat global");
+		break;
+	case O_SETFIB:
+		if (cmd->arg1 == IP_FW_TARG)
+			bprint_uint_arg(bp, "setfib ", cmd->arg1);
+		else
+			bprintf(bp, "setfib %u", cmd->arg1 & 0x7FFF);
+		break;
+	case O_EXTERNAL_ACTION:
+		/*
+		 * The external action can consists of two following
+		 * each other opcodes - O_EXTERNAL_ACTION and
+		 * O_EXTERNAL_INSTANCE. The first contains the ID of
+		 * name of external action. The second contains the ID
+		 * of name of external action instance.
+		 * 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,
+		    IPFW_TLV_EACTION);
+		if (match_token(rule_eactions, s) != -1)
+			bprintf(bp, "%s", s);
+		else
+			bprintf(bp, "eaction %s", s);
+		break;
+	case O_EXTERNAL_INSTANCE:
+		if (state->eaction == NULL)
 			break;
-		} else if (cmd->opcode == O_IP6) {
-			flags |= HAVE_PROTO6;
+		/*
+		 * XXX: we need to teach ipfw(9) to rewrite opcodes
+		 * in the user buffer on rule addition. When we add
+		 * the rule, we specify zero TLV type for
+		 * O_EXTERNAL_INSTANCE object. To show correct
+		 * rule after `ipfw add` we need to search instance
+		 * name with zero type. But when we do `ipfw show`
+		 * we calculate TLV type using IPFW_TLV_EACTION_NAME()
+		 * macro.
+		 */
+		s = object_search_ctlv(fo->tstate, cmd->arg1, 0);
+		if (s == NULL)
+			s = object_search_ctlv(fo->tstate,
+			    cmd->arg1, IPFW_TLV_EACTION_NAME(
+			    state->eaction->arg1));
+		bprintf(bp, " %s", s);
+		break;
+	case O_EXTERNAL_DATA:
+		if (state->eaction == NULL)
 			break;
+		/*
+		 * Currently we support data formatting only for
+		 * external data with datalen u16. For unknown data
+		 * print its size in bytes.
+		 */
+		if (cmd->len == F_INSN_SIZE(ipfw_insn))
+			bprintf(bp, " %u", cmd->arg1);
+		else
+			bprintf(bp, " %ubytes",
+			    cmd->len * sizeof(uint32_t));
+		break;
+	case O_SETDSCP:
+		if (cmd->arg1 == IP_FW_TARG) {
+			bprintf(bp, "setdscp tablearg");
+			break;
 		}
+		s = match_value(f_ipdscp, cmd->arg1 & 0x3F);
+		if (s != NULL)
+			bprintf(bp, "setdscp %s", s);
+		else
+			bprintf(bp, "setdscp %s", cmd->arg1 & 0x3F);
+		break;
+	case O_REASS:
+		bprintf(bp, "reass");
+		break;
+	case O_CALLRETURN:
+		if (cmd->len & F_NOT)
+			bprintf(bp, "return");
+		else
+			bprint_uint_arg(bp, "call ", cmd->arg1);
+		break;
+	default:
+		bprintf(bp, "** unrecognized action %d len %d ",
+			cmd->opcode, cmd->len);
 	}
-	if (rule->flags & IPFW_RULE_NOOPT) {	/* empty rules before options */
-		if (!co->do_compact) {
-			show_prerequisites(bp, &flags, HAVE_PROTO, 0);
-			bprintf(bp, " from any to any");
-		}
-		flags |= HAVE_IP | HAVE_OPTIONS | HAVE_PROTO |
-			 HAVE_SRCIP | HAVE_DSTIP;
-	}
+	mark_printed(state, cmd);
 
-	if (co->comment_only)
-		comment = "...";
+	return (1);
+}
 
-	for (l = rule->act_ofs, cmd = rule->cmd;
-			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
-		/* useful alias */
-		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
 
-		if (co->comment_only) {
-			if (cmd->opcode != O_NOP)
-				continue;
-			bprintf(bp, " // %s\n", (char *)(cmd + 1));
-			return;
-		}
+static ipfw_insn *
+print_action(struct buf_pr *bp, struct format_opts *fo,
+    struct show_state *state, uint8_t opcode)
+{
+	ipfw_insn *cmd;
+	int l;
 
-		show_prerequisites(bp, &flags, 0, cmd->opcode);
+	for (l = state->rule->cmd_len - state->rule->act_ofs,
+	    cmd = ACTION_PTR(state->rule); l > 0;
+	    l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
+		if (cmd->opcode != opcode)
+			continue;
+		if (!print_action_instruction(bp, fo, state, cmd))
+			continue;
+		return (cmd);
+	}
+	return (NULL);
+}
 
-		switch(cmd->opcode) {
-		case O_PROB:
-			break;	/* done already */
+static void
+print_proto(struct buf_pr *bp, struct format_opts *fo,
+    struct show_state *state)
+{
+	ipfw_insn *cmd;
+	int l, proto, ip4, ip6, tmp;
 
-		case O_PROBE_STATE:
-			break; /* no need to print anything here */
-
-		case O_IP_SRC:
-		case O_IP_SRC_LOOKUP:
-		case O_IP_SRC_MASK:
-		case O_IP_SRC_ME:

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201804111024.w3BAOl1j004671>