diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 2011dd2b0830..9183b2a4569d 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,7 +1,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 24, 2006 +.Dd June 15, 2006 .Dt IPFW 8 .Os .Sh NAME @@ -1587,12 +1587,16 @@ Lookup tables currently support IPv4 addresses only. The .Cm tablearg feature provides the ability to use a value, looked up in the table, as -the argument for a rule action. +the argument for a rule action, action parameter or rule option. This can significantly reduce number of rules in some configurations. The .Cm tablearg argument can be used with the following actions: -.Cm pipe , queue, divert, tee, netgraph, ngtee . +.Cm pipe , queue, divert, tee, netgraph, ngtee, +action parameters: +.Cm tag, untag, +rule options: +.Cm limit, tagged. See the .Sx EXAMPLES Section for example usage of tables and the tablearg keyword. diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index ddaf4ac8ae5e..682a52563b2b 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -82,13 +82,42 @@ int */ #define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);} -#define NOT_NUMBER(str, msg) do { \ - char *c; \ - for (c = str; *c != '\0'; c++) { \ - if (isdigit(*c)) \ - continue; \ - errx(EX_DATAERR, msg); \ - } \ +#define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ + if (!ac) \ + errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ + if (_substrcmp(*av, "tablearg") == 0) { \ + arg = IP_FW_TABLEARG; \ + break; \ + } \ + \ + { \ + long val; \ + char *end; \ + \ + val = strtol(*av, &end, 10); \ + \ + if (!isdigit(**av) || *end != '\0' || (val == 0 && errno == EINVAL)) \ + errx(EX_DATAERR, "%s: invalid argument: %s", \ + match_value(s_x, tok), *av); \ + \ + if (errno == ERANGE || val < min || val > max) \ + errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \ + match_value(s_x, tok), min, max, *av); \ + \ + if (val == IP_FW_TABLEARG) \ + errx(EX_DATAERR, "%s: illegal argument value: %s", \ + match_value(s_x, tok), *av); \ + arg = val; \ + } \ +} while (0) + +#define PRINT_UINT_ARG(str, arg) do { \ + if (str != NULL) \ + printf("%s",str); \ + if (arg == IP_FW_TABLEARG) \ + printf("tablearg"); \ + else \ + printf("%u", (uint32_t)arg); \ } while (0) /* @@ -801,30 +830,39 @@ fill_newports(ipfw_insn_u16 *cmd, char *av, int proto) while (*s) { a = strtoport(av, &s, 0, proto); - if (s == av) /* no parameter */ - break; - if (*s == '-') { /* a range */ - av = s+1; + if (s == av) /* empty or invalid argument */ + return (0); + + switch (*s) { + case '-': /* a range */ + av = s + 1; b = strtoport(av, &s, 0, proto); - if (s == av) /* no parameter */ - break; + /* Reject expressions like '1-abc' or '1-2-3'. */ + if (s == av || (*s != ',' && *s != '\0')) + return (0); p[0] = a; p[1] = b; - } else if (*s == ',' || *s == '\0' ) + break; + case ',': /* comma separated list */ + case '\0': p[0] = p[1] = a; - else /* invalid separator */ - errx(EX_DATAERR, "invalid separator <%c> in <%s>\n", + break; + default: + warnx("port list: invalid separator <%c> in <%s>", *s, av); + return (0); + } + i++; p += 2; - av = s+1; + av = s + 1; } if (i > 0) { - if (i+1 > F_LEN_MASK) + if (i + 1 > F_LEN_MASK) errx(EX_DATAERR, "too many ports/ranges\n"); - cmd->o.len |= i+1; /* leave F_NOT and F_OR untouched */ + cmd->o.len |= i + 1; /* leave F_NOT and F_OR untouched */ } - return i; + return (i); } static struct _s_x icmpcodes[] = { @@ -1475,28 +1513,33 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) print_unreach6_code(cmd->arg1); break; -#define PRINT_WITH_ARG(o) \ - if (cmd->arg1 == IP_FW_TABLEARG) \ - printf("%s tablearg", (o)); \ - else \ - printf("%s %u", (o), cmd->arg1); \ + case O_SKIPTO: + PRINT_UINT_ARG("skipto ", cmd->arg1); break; - case O_SKIPTO: - PRINT_WITH_ARG("skipto"); case O_PIPE: - PRINT_WITH_ARG("pipe"); + PRINT_UINT_ARG("pipe ", cmd->arg1); + break; + case O_QUEUE: - PRINT_WITH_ARG("queue"); + PRINT_UINT_ARG("queue ", cmd->arg1); + break; + case O_DIVERT: - PRINT_WITH_ARG("divert"); + PRINT_UINT_ARG("divert ", cmd->arg1); + break; + case O_TEE: - PRINT_WITH_ARG("tee"); + PRINT_UINT_ARG("tee ", cmd->arg1); + break; + case O_NETGRAPH: - PRINT_WITH_ARG("netgraph"); + PRINT_UINT_ARG("netgraph ", cmd->arg1); + break; + case O_NGTEE: - PRINT_WITH_ARG("ngtee"); -#undef PRINT_WITH_ARG + PRINT_UINT_ARG("ngtee ", cmd->arg1); + break; case O_FORWARD_IP: { @@ -1542,9 +1585,9 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) } if (tagptr) { if (tagptr->len & F_NOT) - printf(" untag %hu", tagptr->arg1); + PRINT_UINT_ARG(" untag ", tagptr->arg1); else - printf(" tag %hu", tagptr->arg1); + PRINT_UINT_ARG(" tag ", tagptr->arg1); } /* @@ -1898,8 +1941,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) printf(" keep-state"); break; - case O_LIMIT: - { + case O_LIMIT: { struct _s_x *p = limit_masks; ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; uint8_t x = c->limit_mask; @@ -1912,9 +1954,9 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) printf("%s%s", comma, p->s); comma = ","; } - printf(" %d", c->conn_limit); - } + PRINT_UINT_ARG(" ", c->conn_limit); break; + } case O_IP6: printf(" ip6"); @@ -1934,10 +1976,10 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) case O_TAGGED: if (F_LEN(cmd) == 1) - printf(" tagged %hu", cmd->arg1 ); + PRINT_UINT_ARG(" tagged ", cmd->arg1); else - print_newports((ipfw_insn_u16 *)cmd, 0, - O_TAGGED); + print_newports((ipfw_insn_u16 *)cmd, 0, + O_TAGGED); break; default: @@ -4061,18 +4103,18 @@ add(int ac, char *av[]) break; case TOK_TAG: - case TOK_UNTAG: - { + case TOK_UNTAG: { + uint16_t tag; + if (have_tag) - errx(EX_USAGE, "tag and untag cannot be specified more than once"); - NEED1("missing tag number"); - NOT_NUMBER(*av, "invalid tag number"); + errx(EX_USAGE, "tag and untag cannot be " + "specified more than once"); + GET_UINT_ARG(tag, 1, 65534, i, rule_action_params); have_tag = cmd; - fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, - strtoul(*av, NULL, 0)); + fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag); ac--; av++; - } break; + } default: abort(); @@ -4517,39 +4559,38 @@ add(int ac, char *av[]) fill_cmd(cmd, O_KEEP_STATE, 0, 0); break; - case TOK_LIMIT: - if (open_par) - errx(EX_USAGE, "limit cannot be part " - "of an or block"); - if (have_state) - errx(EX_USAGE, "only one of keep-state " - "and limit is allowed"); - NEED1("limit needs mask and # of connections"); - have_state = cmd; - { + case TOK_LIMIT: { ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; + int val; + + if (open_par) + errx(EX_USAGE, + "limit cannot be part of an or block"); + if (have_state) + errx(EX_USAGE, "only one of keep-state and" + "limit is allowed"); + have_state = cmd; cmd->len = F_INSN_SIZE(ipfw_insn_limit); cmd->opcode = O_LIMIT; - c->limit_mask = 0; - c->conn_limit = 0; - for (; ac >1 ;) { - int val; + c->limit_mask = c->conn_limit = 0; - val = match_token(limit_masks, *av); - if (val <= 0) + while (ac > 0) { + if ((val = match_token(limit_masks, *av)) <= 0) break; c->limit_mask |= val; ac--; av++; } - c->conn_limit = atoi(*av); - if (c->conn_limit == 0) - errx(EX_USAGE, "limit: limit must be >0"); + if (c->limit_mask == 0) - errx(EX_USAGE, "missing limit mask"); + errx(EX_USAGE, "limit: missing limit mask"); + + GET_UINT_ARG(c->conn_limit, 1, 65534, TOK_LIMIT, + rule_options); + ac--; av++; - } break; + } case TOK_PROTO: NEED1("missing protocol"); @@ -4664,14 +4705,17 @@ add(int ac, char *av[]) break; case TOK_TAGGED: - NEED1("missing tag number"); - if (strpbrk(*av, "-,")) { + if (ac > 0 && strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_TAGGED)) - errx(EX_DATAERR, "invalid tag %s", *av); - } else { - NOT_NUMBER(*av, "invalid tag number"); - fill_cmd(cmd, O_TAGGED, 0, - strtoul(*av, NULL, 0)); + errx(EX_DATAERR, "tagged: invalid tag" + " list: %s", *av); + } + else { + uint16_t tag; + + GET_UINT_ARG(tag, 1, 65534, TOK_TAGGED, + rule_options); + fill_cmd(cmd, O_TAGGED, 0, tag); } ac--; av++; break; diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c index 29db1bbbb6b4..9ab4aba77379 100644 --- a/sys/netinet/ip_fw2.c +++ b/sys/netinet/ip_fw2.c @@ -1417,7 +1417,7 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule) */ static int install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, - struct ip_fw_args *args) + struct ip_fw_args *args, uint32_t tablearg) { static int last_log; @@ -1465,11 +1465,19 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, case O_LIMIT: { /* limit number of sessions */ struct ipfw_flow_id id; ipfw_dyn_rule *parent; + uint32_t conn_limit; uint16_t limit_mask = cmd->limit_mask; + conn_limit = (cmd->conn_limit == IP_FW_TABLEARG) ? + tablearg : cmd->conn_limit; + DEB( - printf("ipfw: %s: O_LIMIT rule, conn_limit: %u\n", - __func__, cmd->conn_limit); + if (cmd->conn_limit == IP_FW_TABLEARG) + printf("ipfw: %s: O_LIMIT rule, conn_limit: %u " + "(tablearg)\n", __func__, conn_limit); + else + printf("ipfw: %s: O_LIMIT rule, conn_limit: %u\n", + __func__, conn_limit); ) id.dst_ip = id.src_ip = id.dst_port = id.src_port = 0; @@ -1497,10 +1505,10 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, return (1); } - if (parent->count >= cmd->conn_limit) { + if (parent->count >= conn_limit) { /* See if we can remove some expired rule. */ remove_dyn_rule(rule, parent); - if (parent->count >= cmd->conn_limit) { + if (parent->count >= conn_limit) { if (fw_verbose && last_log != time_uptime) { last_log = time_uptime; log(LOG_SECURITY | LOG_DEBUG, @@ -2895,10 +2903,13 @@ do { \ match = is_ipv4; break; - case O_TAG: + case O_TAG: { + uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; + /* Packet is already tagged with this tag? */ - mtag = m_tag_locate(m, MTAG_IPFW, - ((ipfw_insn *) cmd)->arg1, NULL); + mtag = m_tag_locate(m, MTAG_IPFW, tag, NULL); + /* We have `untag' action when F_NOT flag is * present. And we must remove this mtag from * mbuf and reset `match' to zero (`match' will @@ -2910,18 +2921,21 @@ do { \ if (mtag != NULL) m_tag_delete(m, mtag); } else if (mtag == NULL) { - mtag = m_tag_alloc(MTAG_IPFW, - ((ipfw_insn *) cmd)->arg1, 0, M_NOWAIT); - if (mtag != NULL) + if ((mtag = m_tag_alloc(MTAG_IPFW, + tag, 0, M_NOWAIT)) != NULL) m_tag_prepend(m, mtag); } match = (cmd->len & F_NOT) ? 0: 1; break; + } + + case O_TAGGED: { + uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; - case O_TAGGED: if (cmdlen == 1) { - match = (m_tag_locate(m, MTAG_IPFW, - ((ipfw_insn *) cmd)->arg1, NULL) != NULL); + match = m_tag_locate(m, MTAG_IPFW, + tag, NULL) != NULL; break; } @@ -2943,6 +2957,7 @@ do { \ mtag->m_tag_id <= p[1]; } break; + } /* * The second set of opcodes represents 'actions', @@ -2988,7 +3003,7 @@ do { \ case O_LIMIT: case O_KEEP_STATE: if (install_state(f, - (ipfw_insn_limit *)cmd, args)) { + (ipfw_insn_limit *)cmd, args, tablearg)) { retval = IP_FW_DENY; goto done; /* error/limit violation */ }