From owner-svn-src-all@freebsd.org Wed Mar 28 12:44:29 2018 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 8FA20F59084; Wed, 28 Mar 2018 12:44:29 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 407FB87763; Wed, 28 Mar 2018 12:44:29 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 1CFB22EBF; Wed, 28 Mar 2018 12:44:29 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w2SCiTqk045268; Wed, 28 Mar 2018 12:44:29 GMT (envelope-from ae@FreeBSD.org) Received: (from ae@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w2SCiTEL045266; Wed, 28 Mar 2018 12:44:29 GMT (envelope-from ae@FreeBSD.org) Message-Id: <201803281244.w2SCiTEL045266@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ae set sender to ae@FreeBSD.org using -f From: "Andrey V. Elsukov" Date: Wed, 28 Mar 2018 12:44:29 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r331668 - head/sbin/ipfw X-SVN-Group: head X-SVN-Commit-Author: ae X-SVN-Commit-Paths: head/sbin/ipfw X-SVN-Commit-Revision: 331668 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 28 Mar 2018 12:44:29 -0000 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 ***