Author: melifaro
Date: Tue Mar 19 13:29:01 2013
New Revision: 248505
URL: http://svnweb.freebsd.org/changeset/base/248505

Log:
  Merge r247666, r247712, r247811.
  
  Fix ipfw table argument parsing/printing.
  Fix style.
  
  PR:           kern/175909
  Submitted by: Daniel Hagerty <h...@linnaean.org>
  
  Implement buffer size checking in ipfw(8) add cmd.
  
  PR:           bin/65961
  Submitted by: Eugene Grosbein <eu...@grosbein.pp.ru>
  
  Do not suddenly fail on some rulesets if -n (syntax check only) is specified
  and ipfw(4) module is not loaded.

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

Modified: stable/9/sbin/ipfw/ipfw2.c
==============================================================================
--- stable/9/sbin/ipfw/ipfw2.c  Tue Mar 19 13:21:39 2013        (r248504)
+++ stable/9/sbin/ipfw/ipfw2.c  Tue Mar 19 13:29:01 2013        (r248505)
@@ -64,6 +64,22 @@ int ipfw_socket = -1;
 #define s6_addr32 __u6_addr.__u6_addr32
 #endif
 
+#define        CHECK_LENGTH(v, len) do {                               \
+       if ((v) < (len))                                        \
+               errx(EX_DATAERR, "Rule too long");              \
+       } while (0)
+/*
+ * Check if we have enough space in cmd buffer. Note that since
+ * first 8? u32 words are reserved by reserved header, full cmd
+ * buffer can't be used, so we need to protect from buffer overrun
+ * only. At the beginnig, cblen is less than actual buffer size by
+ * size of ipfw_insn_u32 instruction + 1 u32 work. This eliminates need
+ * for checking small instructions fitting in given range.
+ * We also (ab)use the fact that ipfw_insn is always the first field
+ * for any custom instruction.
+ */
+#define        CHECK_CMDLEN    CHECK_LENGTH(cblen, F_LEN((ipfw_insn *)cmd))
+
 #define GET_UINT_ARG(arg, min, max, tok, s_x) do {                     \
        if (!av[0])                                                     \
                errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
@@ -653,7 +669,7 @@ strtoport(char *s, char **end, int base,
  * Fill the body of the command with the list of port ranges.
  */
 static int
-fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
+fill_newports(ipfw_insn_u16 *cmd, char *av, int proto, int cblen)
 {
        uint16_t a, b, *p = cmd->ports;
        int i = 0;
@@ -664,6 +680,8 @@ fill_newports(ipfw_insn_u16 *cmd, char *
                if (s == av)                    /* empty or invalid argument */
                        return (0);
 
+               CHECK_LENGTH(cblen, i + 2);
+
                switch (*s) {
                case '-':                       /* a range */
                        av = s + 1;
@@ -2067,7 +2085,7 @@ lookup_host (char *host, struct in_addr 
  * We can have multiple comma-separated address/mask entries.
  */
 static void
-fill_ip(ipfw_insn_ip *cmd, char *av)
+fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
 {
        int len = 0;
        uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
@@ -2107,6 +2125,8 @@ fill_ip(ipfw_insn_ip *cmd, char *av)
        int masklen;
        char md, nd = '\0';
 
+       CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn) + 2 + len);
+
        if (p) {
                md = *p;
                *p++ = '\0';
@@ -2365,11 +2385,13 @@ ipfw_delete(char *av[])
  * patterns which match interfaces.
  */
 static void
-fill_iface(ipfw_insn_if *cmd, char *arg)
+fill_iface(ipfw_insn_if *cmd, char *arg, int cblen)
 {
        cmd->name[0] = '\0';
        cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
 
+       CHECK_CMDLEN;
+
        /* Parse the interface or address */
        if (strcmp(arg, "any") == 0)
                cmd->o.len = 0;         /* effectively ignore this command */
@@ -2440,8 +2462,10 @@ get_mac_addr_mask(const char *p, uint8_t
  * the new command in case it has been clobbered before.
  */
 static ipfw_insn *
-next_cmd(ipfw_insn *cmd)
+next_cmd(ipfw_insn *cmd, int *len)
 {
+       *len -= F_LEN(cmd);
+       CHECK_LENGTH(*len, 0);
        cmd += F_LEN(cmd);
        bzero(cmd, sizeof(*cmd));
        return cmd;
@@ -2451,7 +2475,7 @@ next_cmd(ipfw_insn *cmd)
  * Takes arguments and copies them into a comment
  */
 static void
-fill_comment(ipfw_insn *cmd, char **av)
+fill_comment(ipfw_insn *cmd, char **av, int cblen)
 {
        int i, l;
        char *p = (char *)(cmd + 1);
@@ -2469,6 +2493,8 @@ fill_comment(ipfw_insn *cmd, char **av)
                    "comment too long (max 80 chars)");
        l = 1 + (l+3)/4;
        cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
+       CHECK_CMDLEN;
+
        for (i = 0; av[i] != NULL; i++) {
                strcpy(p, av[i]);
                p += strlen(av[i]);
@@ -2494,7 +2520,7 @@ fill_cmd(ipfw_insn *cmd, enum ipfw_opcod
  * two microinstructions, and returns the pointer to the last one.
  */
 static ipfw_insn *
-add_mac(ipfw_insn *cmd, char *av[])
+add_mac(ipfw_insn *cmd, char *av[], int cblen)
 {
        ipfw_insn_mac *mac;
 
@@ -2503,6 +2529,7 @@ add_mac(ipfw_insn *cmd, char *av[])
 
        cmd->opcode = O_MACADDR2;
        cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
+       CHECK_CMDLEN;
 
        mac = (ipfw_insn_mac *)cmd;
        get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
@@ -2512,12 +2539,13 @@ add_mac(ipfw_insn *cmd, char *av[])
 }
 
 static ipfw_insn *
-add_mactype(ipfw_insn *cmd, char *av)
+add_mactype(ipfw_insn *cmd, char *av, int cblen)
 {
        if (!av)
                errx(EX_DATAERR, "missing MAC type");
        if (strcmp(av, "any") != 0) { /* we have a non-null type */
-               fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
+               fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE,
+                   cblen);
                cmd->opcode = O_MAC_TYPE;
                return cmd;
        } else
@@ -2586,9 +2614,9 @@ add_proto_compat(ipfw_insn *cmd, char *a
 }
 
 static ipfw_insn *
-add_srcip(ipfw_insn *cmd, char *av)
+add_srcip(ipfw_insn *cmd, char *av, int cblen)
 {
-       fill_ip((ipfw_insn_ip *)cmd, av);
+       fill_ip((ipfw_insn_ip *)cmd, av, cblen);
        if (cmd->opcode == O_IP_DST_SET)                        /* set */
                cmd->opcode = O_IP_SRC_SET;
        else if (cmd->opcode == O_IP_DST_LOOKUP)                /* table */
@@ -2603,9 +2631,9 @@ add_srcip(ipfw_insn *cmd, char *av)
 }
 
 static ipfw_insn *
-add_dstip(ipfw_insn *cmd, char *av)
+add_dstip(ipfw_insn *cmd, char *av, int cblen)
 {
-       fill_ip((ipfw_insn_ip *)cmd, av);
+       fill_ip((ipfw_insn_ip *)cmd, av, cblen);
        if (cmd->opcode == O_IP_DST_SET)                        /* set */
                ;
        else if (cmd->opcode == O_IP_DST_LOOKUP)                /* table */
@@ -2620,12 +2648,12 @@ add_dstip(ipfw_insn *cmd, char *av)
 }
 
 static ipfw_insn *
-add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
+add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen)
 {
        /* XXX "any" is trapped before. Perhaps "to" */
        if (_substrcmp(av, "any") == 0) {
                return NULL;
-       } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
+       } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) {
                /* XXX todo: check that we have a protocol with ports */
                cmd->opcode = opcode;
                return cmd;
@@ -2634,7 +2662,7 @@ add_ports(ipfw_insn *cmd, char *av, u_ch
 }
 
 static ipfw_insn *
-add_src(ipfw_insn *cmd, char *av, u_char proto)
+add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen)
 {
        struct in6_addr a;
        char *host, *ch;
@@ -2647,11 +2675,11 @@ add_src(ipfw_insn *cmd, char *av, u_char
 
        if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
            inet_pton(AF_INET6, host, &a) == 1)
-               ret = add_srcip6(cmd, av);
+               ret = add_srcip6(cmd, av, cblen);
        /* XXX: should check for IPv4, not !IPv6 */
        if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
            inet_pton(AF_INET6, host, &a) != 1))
-               ret = add_srcip(cmd, av);
+               ret = add_srcip(cmd, av, cblen);
        if (ret == NULL && strcmp(av, "any") != 0)
                ret = cmd;
 
@@ -2660,7 +2688,7 @@ add_src(ipfw_insn *cmd, char *av, u_char
 }
 
 static ipfw_insn *
-add_dst(ipfw_insn *cmd, char *av, u_char proto)
+add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen)
 {
        struct in6_addr a;
        char *host, *ch;
@@ -2673,11 +2701,11 @@ add_dst(ipfw_insn *cmd, char *av, u_char
 
        if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
            inet_pton(AF_INET6, host, &a) == 1)
-               ret = add_dstip6(cmd, av);
+               ret = add_dstip6(cmd, av, cblen);
        /* XXX: should check for IPv4, not !IPv6 */
        if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
            inet_pton(AF_INET6, host, &a) != 1))
-               ret = add_dstip(cmd, av);
+               ret = add_dstip(cmd, av, cblen);
        if (ret == NULL && strcmp(av, "any") != 0)
                ret = cmd;
 
@@ -2707,6 +2735,7 @@ ipfw_add(char *av[])
         * go into actbuf[].
         */
        static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
+       int rblen, ablen, cblen;
 
        ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
        ipfw_insn *first_cmd;   /* first match pattern */
@@ -2737,6 +2766,15 @@ ipfw_add(char *av[])
        cmd = (ipfw_insn *)cmdbuf;
        action = (ipfw_insn *)actbuf;
 
+       rblen = sizeof(rulebuf) / sizeof(rulebuf[0]);
+       rblen -= offsetof(struct ip_fw, cmd) / sizeof(rulebuf[0]);
+       ablen = sizeof(actbuf) / sizeof(actbuf[0]);
+       cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]);
+       cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1;
+
+#define        CHECK_RBUFLEN(len)      { CHECK_LENGTH(rblen, len); rblen -= 
len; }
+#define        CHECK_ACTLEN            CHECK_LENGTH(ablen, action->len)
+
        av++;
 
        /* [rule N]     -- Rule number optional */
@@ -2768,6 +2806,7 @@ ipfw_add(char *av[])
        i = match_token(rule_actions, *av);
        av++;
        action->len = 1;        /* default */
+       CHECK_ACTLEN;
        switch(i) {
        case TOK_CHECKSTATE:
                have_state = action;
@@ -2819,6 +2858,7 @@ ipfw_add(char *av[])
        case TOK_NAT:
                action->opcode = O_NAT;
                action->len = F_INSN_SIZE(ipfw_insn_nat);
+               CHECK_ACTLEN;
                if (_substrcmp(*av, "global") == 0) {
                        action->arg1 = 0;
                        av++;
@@ -2935,6 +2975,7 @@ chkarg:
 
                        action->opcode = O_FORWARD_IP;
                        action->len = F_INSN_SIZE(ipfw_insn_sa);
+                       CHECK_ACTLEN;
 
                        /*
                         * In the kernel we assume AF_INET and use only
@@ -2951,6 +2992,7 @@ chkarg:
 
                        action->opcode = O_FORWARD_IP6;
                        action->len = F_INSN_SIZE(ipfw_insn_sa6);
+                       CHECK_ACTLEN;
 
                        p->sa.sin6_len = sizeof(struct sockaddr_in6);
                        p->sa.sin6_family = AF_INET6;
@@ -3004,7 +3046,7 @@ chkarg:
        default:
                errx(EX_DATAERR, "invalid action %s\n", av[-1]);
        }
-       action = next_cmd(action);
+       action = next_cmd(action, &ablen);
 
        /*
         * [altq queuename] -- altq tag, optional
@@ -3026,6 +3068,7 @@ chkarg:
                                    "log cannot be specified more than once");
                        have_log = (ipfw_insn *)c;
                        cmd->len = F_INSN_SIZE(ipfw_insn_log);
+                       CHECK_CMDLEN;
                        cmd->opcode = O_LOG;
                        if (av[0] && _substrcmp(*av, "logamount") == 0) {
                                av++;
@@ -3039,9 +3082,14 @@ chkarg:
                        } else {
                                len = sizeof(c->max_log);
                                if (sysctlbyname("net.inet.ip.fw.verbose_limit",
-                                   &c->max_log, &len, NULL, 0) == -1)
+                                   &c->max_log, &len, NULL, 0) == -1) {
+                                       if (co.test_only) {
+                                               c->max_log = 0;
+                                               break;
+                                       }
                                        errx(1, "sysctlbyname(\"%s\")",
                                            "net.inet.ip.fw.verbose_limit");
+                               }
                        }
                    }
                        break;
@@ -3057,6 +3105,7 @@ chkarg:
                                    "altq cannot be specified more than once");
                        have_altq = (ipfw_insn *)a;
                        cmd->len = F_INSN_SIZE(ipfw_insn_altq);
+                       CHECK_CMDLEN;
                        cmd->opcode = O_ALTQ;
                        a->qid = altq_name_to_qid(*av);
                        av++;
@@ -3082,7 +3131,7 @@ chkarg:
                default:
                        abort();
                }
-               cmd = next_cmd(cmd);
+               cmd = next_cmd(cmd, &cblen);
        }
 
        if (have_state) /* must be a check-state, we are done */
@@ -3167,7 +3216,7 @@ chkarg:
                av++;
                if (F_LEN(cmd) != 0) {
                        prev = cmd;
-                       cmd = next_cmd(cmd);
+                       cmd = next_cmd(cmd, &cblen);
                }
        } else if (first_cmd != cmd) {
                errx(EX_DATAERR, "invalid protocol ``%s''", *av);
@@ -3188,11 +3237,11 @@ chkarg:
     OR_START(source_ip);
        NOT_BLOCK;      /* optional "not" */
        NEED1("missing source address");
-       if (add_src(cmd, *av, proto)) {
+       if (add_src(cmd, *av, proto, cblen)) {
                av++;
                if (F_LEN(cmd) != 0) {  /* ! any */
                        prev = cmd;
-                       cmd = next_cmd(cmd);
+                       cmd = next_cmd(cmd, &cblen);
                }
        } else
                errx(EX_USAGE, "bad source address %s", *av);
@@ -3204,10 +3253,10 @@ chkarg:
        NOT_BLOCK;      /* optional "not" */
        if ( av[0] != NULL ) {
                if (_substrcmp(*av, "any") == 0 ||
-                   add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
+                   add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) {
                        av++;
                        if (F_LEN(cmd) != 0)
-                               cmd = next_cmd(cmd);
+                               cmd = next_cmd(cmd, &cblen);
                }
        }
 
@@ -3224,11 +3273,11 @@ chkarg:
     OR_START(dest_ip);
        NOT_BLOCK;      /* optional "not" */
        NEED1("missing dst address");
-       if (add_dst(cmd, *av, proto)) {
+       if (add_dst(cmd, *av, proto, cblen)) {
                av++;
                if (F_LEN(cmd) != 0) {  /* ! any */
                        prev = cmd;
-                       cmd = next_cmd(cmd);
+                       cmd = next_cmd(cmd, &cblen);
                }
        } else
                errx( EX_USAGE, "bad destination address %s", *av);
@@ -3240,10 +3289,10 @@ chkarg:
        NOT_BLOCK;      /* optional "not" */
        if (av[0]) {
                if (_substrcmp(*av, "any") == 0 ||
-                   add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
+                   add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) {
                        av++;
                        if (F_LEN(cmd) != 0)
-                               cmd = next_cmd(cmd);
+                               cmd = next_cmd(cmd, &cblen);
                }
        }
 
@@ -3331,7 +3380,7 @@ read_options:
                case TOK_VIA:
                        NEED1("recv, xmit, via require interface name"
                                " or address");
-                       fill_iface((ipfw_insn_if *)cmd, av[0]);
+                       fill_iface((ipfw_insn_if *)cmd, av[0], cblen);
                        av++;
                        if (F_LEN(cmd) == 0)    /* not a valid address */
                                break;
@@ -3351,14 +3400,14 @@ read_options:
 
                case TOK_ICMP6TYPES:
                        NEED1("icmptypes requires list of types");
-                       fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
+                       fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av, cblen);
                        av++;
                        break;
 
                case TOK_IPTTL:
                        NEED1("ipttl requires TTL");
                        if (strpbrk(*av, "-,")) {
-                           if (!add_ports(cmd, *av, 0, O_IPTTL))
+                           if (!add_ports(cmd, *av, 0, O_IPTTL, cblen))
                                errx(EX_DATAERR, "invalid ipttl %s", *av);
                        } else
                            fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
@@ -3368,7 +3417,7 @@ read_options:
                case TOK_IPID:
                        NEED1("ipid requires id");
                        if (strpbrk(*av, "-,")) {
-                           if (!add_ports(cmd, *av, 0, O_IPID))
+                           if (!add_ports(cmd, *av, 0, O_IPID, cblen))
                                errx(EX_DATAERR, "invalid ipid %s", *av);
                        } else
                            fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
@@ -3378,7 +3427,7 @@ read_options:
                case TOK_IPLEN:
                        NEED1("iplen requires length");
                        if (strpbrk(*av, "-,")) {
-                           if (!add_ports(cmd, *av, 0, O_IPLEN))
+                           if (!add_ports(cmd, *av, 0, O_IPLEN, cblen))
                                errx(EX_DATAERR, "invalid ip len %s", *av);
                        } else
                            fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
@@ -3474,7 +3523,7 @@ read_options:
                case TOK_TCPDATALEN:
                        NEED1("tcpdatalen requires length");
                        if (strpbrk(*av, "-,")) {
-                           if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
+                           if (!add_ports(cmd, *av, 0, O_TCPDATALEN, cblen))
                                errx(EX_DATAERR, "invalid tcpdata len %s", *av);
                        } else
                            fill_cmd(cmd, O_TCPDATALEN, 0,
@@ -3500,7 +3549,7 @@ read_options:
                case TOK_TCPWIN:
                        NEED1("tcpwin requires length");
                        if (strpbrk(*av, "-,")) {
-                           if (!add_ports(cmd, *av, 0, O_TCPWIN))
+                           if (!add_ports(cmd, *av, 0, O_TCPWIN, cblen))
                                errx(EX_DATAERR, "invalid tcpwin len %s", *av);
                        } else
                            fill_cmd(cmd, O_TCPWIN, 0,
@@ -3539,6 +3588,7 @@ read_options:
                        have_state = cmd;
 
                        cmd->len = F_INSN_SIZE(ipfw_insn_limit);
+                       CHECK_CMDLEN;
                        cmd->opcode = O_LIMIT;
                        c->limit_mask = c->conn_limit = 0;
 
@@ -3570,28 +3620,28 @@ read_options:
 
                case TOK_SRCIP:
                        NEED1("missing source IP");
-                       if (add_srcip(cmd, *av)) {
+                       if (add_srcip(cmd, *av, cblen)) {
                                av++;
                        }
                        break;
 
                case TOK_DSTIP:
                        NEED1("missing destination IP");
-                       if (add_dstip(cmd, *av)) {
+                       if (add_dstip(cmd, *av, cblen)) {
                                av++;
                        }
                        break;
 
                case TOK_SRCIP6:
                        NEED1("missing source IP6");
-                       if (add_srcip6(cmd, *av)) {
+                       if (add_srcip6(cmd, *av, cblen)) {
                                av++;
                        }
                        break;
 
                case TOK_DSTIP6:
                        NEED1("missing destination IP6");
-                       if (add_dstip6(cmd, *av)) {
+                       if (add_dstip6(cmd, *av, cblen)) {
                                av++;
                        }
                        break;
@@ -3599,7 +3649,7 @@ read_options:
                case TOK_SRCPORT:
                        NEED1("missing source port");
                        if (_substrcmp(*av, "any") == 0 ||
-                           add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
+                           add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) {
                                av++;
                        } else
                                errx(EX_DATAERR, "invalid source port %s", *av);
@@ -3608,7 +3658,7 @@ read_options:
                case TOK_DSTPORT:
                        NEED1("missing destination port");
                        if (_substrcmp(*av, "any") == 0 ||
-                           add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
+                           add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) {
                                av++;
                        } else
                                errx(EX_DATAERR, "invalid destination port %s",
@@ -3616,13 +3666,13 @@ read_options:
                        break;
 
                case TOK_MAC:
-                       if (add_mac(cmd, av))
+                       if (add_mac(cmd, av, cblen))
                                av += 2;
                        break;
 
                case TOK_MACTYPE:
                        NEED1("missing mac type");
-                       if (!add_mactype(cmd, *av))
+                       if (!add_mactype(cmd, *av, cblen))
                                errx(EX_DATAERR, "invalid mac type %s", *av);
                        av++;
                        break;
@@ -3660,18 +3710,18 @@ read_options:
                        if (proto != IPPROTO_IPV6 )
                                errx( EX_USAGE, "flow-id filter is active "
                                    "only for ipv6 protocol\n");
-                       fill_flow6( (ipfw_insn_u32 *) cmd, *av );
+                       fill_flow6( (ipfw_insn_u32 *) cmd, *av, cblen);
                        av++;
                        break;
 
                case TOK_COMMENT:
-                       fill_comment(cmd, av);
+                       fill_comment(cmd, av, cblen);
                        av[0]=NULL;
                        break;
 
                case TOK_TAGGED:
                        if (av[0] && strpbrk(*av, "-,")) {
-                               if (!add_ports(cmd, *av, 0, O_TAGGED))
+                               if (!add_ports(cmd, *av, 0, O_TAGGED, cblen))
                                        errx(EX_DATAERR, "tagged: invalid tag"
                                            " list: %s", *av);
                        }
@@ -3724,7 +3774,7 @@ read_options:
                }
                if (F_LEN(cmd) > 0) {   /* prepare to advance */
                        prev = cmd;
-                       cmd = next_cmd(cmd);
+                       cmd = next_cmd(cmd, &cblen);
                }
        }
 
@@ -3753,12 +3803,13 @@ done:
         */
        if (have_state && have_state->opcode != O_CHECK_STATE) {
                fill_cmd(dst, O_PROBE_STATE, 0, 0);
-               dst = next_cmd(dst);
+               dst = next_cmd(dst, &rblen);
        }
 
        /* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
        for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
                i = F_LEN(src);
+               CHECK_RBUFLEN(i);
 
                switch (src->opcode) {
                case O_LOG:
@@ -3778,6 +3829,7 @@ done:
         */
        if (have_state && have_state->opcode != O_CHECK_STATE) {
                i = F_LEN(have_state);
+               CHECK_RBUFLEN(i);
                bcopy(have_state, dst, i * sizeof(uint32_t));
                dst += i;
        }
@@ -3789,24 +3841,29 @@ done:
        /* put back O_LOG, O_ALTQ, O_TAG if necessary */
        if (have_log) {
                i = F_LEN(have_log);
+               CHECK_RBUFLEN(i);
                bcopy(have_log, dst, i * sizeof(uint32_t));
                dst += i;
        }
        if (have_altq) {
                i = F_LEN(have_altq);
+               CHECK_RBUFLEN(i);
                bcopy(have_altq, dst, i * sizeof(uint32_t));
                dst += i;
        }
        if (have_tag) {
                i = F_LEN(have_tag);
+               CHECK_RBUFLEN(i);
                bcopy(have_tag, dst, i * sizeof(uint32_t));
                dst += i;
        }
+
        /*
         * copy all other actions
         */
        for (src = (ipfw_insn *)actbuf; src != action; src += i) {
                i = F_LEN(src);
+               CHECK_RBUFLEN(i);
                bcopy(src, dst, i * sizeof(uint32_t));
                dst += i;
        }
@@ -3911,6 +3968,7 @@ ipfw_flush(int force)
 
 
 static void table_list(uint16_t num, int need_header);
+static void table_fill_xentry(char *arg, ipfw_table_xentry *xent);
 
 /*
  * This one handles all table-related commands
@@ -3926,15 +3984,18 @@ ipfw_table_handler(int ac, char *av[])
        int do_add;
        int is_all;
        size_t len;
-       char *p;
-       uint32_t a, type, mask, addrlen;
+       uint32_t a;
        uint32_t tables_max;
 
        len = sizeof(tables_max);
        if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len,
-               NULL, 0) == -1)
-               errx(1, "Can't determine maximum number of ipfw tables. "
-                   "Perhaps you forgot to load ipfw module?");
+           NULL, 0) == -1) {
+               if (co.test_only)
+                       tables_max = 128; /* Old conservative default */
+               else
+                       errx(1, "Can't determine maximum number of ipfw tables."
+                           " Perhaps you forgot to load ipfw module?");
+       }
 
        memset(&xent, 0, sizeof(xent));
 
@@ -3963,57 +4024,8 @@ ipfw_table_handler(int ac, char *av[])
                ac--; av++;
                if (!ac)
                        errx(EX_USAGE, "address required");
-               /* 
-                * Let's try to guess type by agrument.
-                * Possible types: 
-                * 1) IPv4[/mask]
-                * 2) IPv6[/mask]
-                * 3) interface name
-                * 4) port ?
-                */
-               type = 0;
-               if (ishexnumber(*av[0])) {
-                       /* Remove / if exists */
-                       if ((p = strchr(*av, '/')) != NULL) {
-                               *p = '\0';
-                               mask = atoi(p + 1);
-                       }
-
-                       if (inet_pton(AF_INET, *av, &xent.k.addr6) == 1) {
-                               type = IPFW_TABLE_CIDR;
-                               if ((p != NULL) && (mask > 32))
-                                       errx(EX_DATAERR, "bad IPv4 mask width: 
%s", p + 1);
-                               xent.masklen = p ? mask : 32;
-                               addrlen = sizeof(struct in_addr);
-                       } else if (inet_pton(AF_INET6, *av, &xent.k.addr6) == 
1) {
-                               type = IPFW_TABLE_CIDR;
-                               if ((p != NULL) && (mask > 128))
-                                       errx(EX_DATAERR, "bad IPv6 mask width: 
%s", p + 1);
-                               xent.masklen = p ? mask : 128;
-                               addrlen = sizeof(struct in6_addr);
-                       }
-               }
 
-               if ((type == 0) && (strchr(*av, '.') == NULL)) {
-                       /* Assume interface name. Copy significant data only */
-                       mask = MIN(strlen(*av), IF_NAMESIZE - 1);
-                       memcpy(xent.k.iface, *av, mask);
-                       /* Set mask to exact match */
-                       xent.masklen = 8 * IF_NAMESIZE;
-                       type = IPFW_TABLE_INTERFACE;
-                       addrlen = IF_NAMESIZE;
-               }
-
-               if (type == 0) {
-                       if (lookup_host(*av, (struct in_addr *)&xent.k.addr6) 
!= 0)
-                               errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
-                       xent.masklen = 32;
-                       type = IPFW_TABLE_CIDR;
-                       addrlen = sizeof(struct in_addr);
-               }
-
-               xent.type = type;
-               xent.len = offsetof(ipfw_table_xentry, k) + addrlen;
+               table_fill_xentry(*av, &xent);
 
                ac--; av++;
                if (do_add && ac) {
@@ -4063,6 +4075,93 @@ ipfw_table_handler(int ac, char *av[])
 }
 
 static void
+table_fill_xentry(char *arg, ipfw_table_xentry *xent)
+{
+       int addrlen, mask, masklen, type;
+       struct in6_addr *paddr;
+       uint32_t *pkey;
+       char *p;
+       uint32_t key;
+
+       mask = 0;
+       type = 0;
+       addrlen = 0;
+       masklen = 0;
+
+       /* 
+        * Let's try to guess type by agrument.
+        * Possible types: 
+        * 1) IPv4[/mask]
+        * 2) IPv6[/mask]
+        * 3) interface name
+        * 4) port, uid/gid or other u32 key (base 10 format)
+        * 5) hostname
+        */
+       paddr = &xent->k.addr6;
+       if (ishexnumber(*arg) != 0 || *arg == ':') {
+               /* Remove / if exists */
+               if ((p = strchr(arg, '/')) != NULL) {
+                       *p = '\0';
+                       mask = atoi(p + 1);
+               }
+
+               if (inet_pton(AF_INET, arg, paddr) == 1) {
+                       if (p != NULL && mask > 32)
+                               errx(EX_DATAERR, "bad IPv4 mask width: %s",
+                                   p + 1);
+
+                       type = IPFW_TABLE_CIDR;
+                       masklen = p ? mask : 32;
+                       addrlen = sizeof(struct in_addr);
+               } else if (inet_pton(AF_INET6, arg, paddr) == 1) {
+                       if (IN6_IS_ADDR_V4COMPAT(paddr))
+                               errx(EX_DATAERR,
+                                   "Use IPv4 instead of v4-compatible");
+                       if (p != NULL && mask > 128)
+                               errx(EX_DATAERR, "bad IPv6 mask width: %s",
+                                   p + 1);
+
+                       type = IPFW_TABLE_CIDR;
+                       masklen = p ? mask : 128;
+                       addrlen = sizeof(struct in6_addr);
+               } else {
+                       /* Port or any other key */
+                       key = strtol(arg, &p, 10);
+                       /* Skip non-base 10 entries like 'fa1' */
+                       if (p != arg) {
+                               pkey = (uint32_t *)paddr;
+                               *pkey = htonl(key);
+                               type = IPFW_TABLE_CIDR;
+                               addrlen = sizeof(uint32_t);
+                       }
+               }
+       }
+
+       if (type == 0 && strchr(arg, '.') == NULL) {
+               /* Assume interface name. Copy significant data only */
+               mask = MIN(strlen(arg), IF_NAMESIZE - 1);
+               memcpy(xent->k.iface, arg, mask);
+               /* Set mask to exact match */
+               masklen = 8 * IF_NAMESIZE;
+               type = IPFW_TABLE_INTERFACE;
+               addrlen = IF_NAMESIZE;
+       }
+
+       if (type == 0) {
+               if (lookup_host(arg, (struct in_addr *)paddr) != 0)
+                       errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
+
+               masklen = 32;
+               type = IPFW_TABLE_CIDR;
+               addrlen = sizeof(struct in_addr);
+       }
+
+       xent->type = type;
+       xent->masklen = masklen;
+       xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
+}
+
+static void
 table_list(uint16_t num, int need_header)
 {
        ipfw_xtable *tbl;
@@ -4105,8 +4204,8 @@ table_list(uint16_t num, int need_header
                        tval = xent->value;
                        addr6 = &xent->k.addr6;
 
-                       if ((addr6->s6_addr32[0] == 0) && (addr6->s6_addr32[1] 
== 0) && 
-                           (addr6->s6_addr32[2] == 0)) {
+
+                       if (IN6_IS_ADDR_V4COMPAT(addr6)) {
                                /* IPv4 address */
                                inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, 
sizeof(tbuf));
                        } else {

Modified: stable/9/sbin/ipfw/ipfw2.h
==============================================================================
--- stable/9/sbin/ipfw/ipfw2.h  Tue Mar 19 13:21:39 2013        (r248504)
+++ stable/9/sbin/ipfw/ipfw2.h  Tue Mar 19 13:29:01 2013        (r248505)
@@ -283,10 +283,10 @@ void print_flow6id(struct _ipfw_insn_u32
 void print_icmp6types(struct _ipfw_insn_u32 *cmd);
 void print_ext6hdr(struct _ipfw_insn *cmd );
 
-struct _ipfw_insn *add_srcip6(struct _ipfw_insn *cmd, char *av);
-struct _ipfw_insn *add_dstip6(struct _ipfw_insn *cmd, char *av);
+struct _ipfw_insn *add_srcip6(struct _ipfw_insn *cmd, char *av, int cblen);
+struct _ipfw_insn *add_dstip6(struct _ipfw_insn *cmd, char *av, int cblen);
 
-void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av );
+void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av, int cblen);
 void fill_unreach6_code(u_short *codep, char *str);
-void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av);
+void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av, int cblen);
 int fill_ext6hdr(struct _ipfw_insn *cmd, char *av);

Modified: stable/9/sbin/ipfw/ipv6.c
==============================================================================
--- stable/9/sbin/ipfw/ipv6.c   Tue Mar 19 13:21:39 2013        (r248504)
+++ stable/9/sbin/ipfw/ipv6.c   Tue Mar 19 13:29:01 2013        (r248505)
@@ -42,6 +42,11 @@
 #include <netinet/ip_fw.h>
 #include <arpa/inet.h>
 
+#define        CHECK_LENGTH(v, len) do {                       \
+       if ((v) < (len))                                \
+               errx(EX_DATAERR, "Rule too long");      \
+       } while (0)
+
 static struct _s_x icmp6codes[] = {
       { "no-route",            ICMP6_DST_UNREACH_NOROUTE },
       { "admin-prohib",                ICMP6_DST_UNREACH_ADMIN },
@@ -131,10 +136,12 @@ print_ip6(ipfw_insn_ip6 *cmd, char const
 }
 
 void
-fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
+fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av, int cblen)
 {
        uint8_t type;
 
+       CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_icmp6));
+
        bzero(cmd, sizeof(*cmd));
        while (*av) {
           if (*av == ',')
@@ -327,7 +334,7 @@ lookup_host6 (char *host, struct in6_add
  * Return 1 on success, 0 on failure.
  */
 static int
-fill_ip6(ipfw_insn_ip6 *cmd, char *av)
+fill_ip6(ipfw_insn_ip6 *cmd, char *av, int cblen)
 {
        int len = 0;
        struct in6_addr *d = &(cmd->addr6);
@@ -379,6 +386,8 @@ fill_ip6(ipfw_insn_ip6 *cmd, char *av)
                int masklen;
                char md = '\0';
 
+               CHECK_LENGTH(cblen, 1 + len + 2 * F_INSN_SIZE(struct in6_addr));
+
                if ((p = strpbrk(av, "/,")) ) {
                        md = *p;        /* save the separator */
                        *p = '\0';      /* terminate address string */
@@ -453,7 +462,7 @@ fill_ip6(ipfw_insn_ip6 *cmd, char *av)
  * additional flow-id we want to filter, the basic is 1
  */
 void
-fill_flow6( ipfw_insn_u32 *cmd, char *av )
+fill_flow6( ipfw_insn_u32 *cmd, char *av, int cblen)
 {
        u_int32_t type;  /* Current flow number */
        u_int16_t nflow = 0;    /* Current flow index */
@@ -461,6 +470,8 @@ fill_flow6( ipfw_insn_u32 *cmd, char *av
        cmd->d[0] = 0;    /* Initializing the base number*/
 
        while (s) {
+               CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_u32) + nflow + 1);
+
                av = strsep( &s, ",") ;
                type = strtoul(av, &av, 0);
                if (*av != ',' && *av != '\0')
@@ -481,10 +492,10 @@ fill_flow6( ipfw_insn_u32 *cmd, char *av
 }
 
 ipfw_insn *
-add_srcip6(ipfw_insn *cmd, char *av)
+add_srcip6(ipfw_insn *cmd, char *av, int cblen)
 {
 
-       fill_ip6((ipfw_insn_ip6 *)cmd, av);
+       fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen);
        if (cmd->opcode == O_IP_DST_SET)                        /* set */
                cmd->opcode = O_IP_SRC_SET;
        else if (cmd->opcode == O_IP_DST_LOOKUP)                /* table */
@@ -503,10 +514,10 @@ add_srcip6(ipfw_insn *cmd, char *av)
 }
 
 ipfw_insn *
-add_dstip6(ipfw_insn *cmd, char *av)
+add_dstip6(ipfw_insn *cmd, char *av, int cblen)
 {
 
-       fill_ip6((ipfw_insn_ip6 *)cmd, av);
+       fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen);
        if (cmd->opcode == O_IP_DST_SET)                        /* set */
                ;
        else if (cmd->opcode == O_IP_DST_LOOKUP)                /* table */
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to