Author: ae
Date: Wed Mar 28 12:44:28 2018
New Revision: 331668
URL: https://svnweb.freebsd.org/changeset/base/331668

Log:
  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
  Discussed with:       melifaro
  MFC after:    2 weeks
  Sponsored by: Yandex LLC

Modified:
  head/sbin/ipfw/ipfw2.c
  head/sbin/ipfw/ipfw2.h
  head/sbin/ipfw/main.c

Modified: head/sbin/ipfw/ipfw2.c
==============================================================================
--- head/sbin/ipfw/ipfw2.c      Wed Mar 28 08:58:32 2018        (r331667)
+++ head/sbin/ipfw/ipfw2.c      Wed Mar 28 12:44:28 2018        (r331668)
@@ -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 ***
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to