summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoroleg <oleg@FreeBSD.org>2006-06-15 09:39:22 +0000
committeroleg <oleg@FreeBSD.org>2006-06-15 09:39:22 +0000
commit7a65db868d2dd50e0b00551e66f65f991130d187 (patch)
treee0ceace24ee6812b8ba9aacd9913b87c088d5493
parent45f57ec2f17004811352063e238f34b1cecf3c09 (diff)
downloadFreeBSD-src-7a65db868d2dd50e0b00551e66f65f991130d187.zip
FreeBSD-src-7a65db868d2dd50e0b00551e66f65f991130d187.tar.gz
Add support of 'tablearg' feature for:
- 'tag' & 'untag' action parameters. - 'tagged' & 'limit' rule options. Rule examples: pipe 1 tag tablearg ip from table(1) to any allow ip from any to table(2) tagged tablearg allow tcp from table(3) to any 25 setup limit src-addr tablearg sbin/ipfw/ipfw2.c: 1) new macros GET_UINT_ARG - support of 'tablearg' keyword, argument range checking. PRINT_UINT_ARG - support of 'tablearg' keyword. 2) strtoport(): do not silently truncate/accept invalid port list expressions like: '1,2-abc' or '1,2-3-4' or '1,2-3x4'. style(9) cleanup. Approved by: glebius (mentor) MFC after: 1 month
-rw-r--r--sbin/ipfw/ipfw.810
-rw-r--r--sbin/ipfw/ipfw2.c198
-rw-r--r--sys/netinet/ip_fw2.c45
3 files changed, 158 insertions, 95 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 2011dd2..9183b2a 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 ddaf4ac..682a525 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 @@ chkarg:
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 @@ read_options:
fill_cmd(cmd, O_KEEP_STATE, 0, 0);
break;
- case TOK_LIMIT:
+ 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");
+ 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");
+ errx(EX_USAGE, "only one of keep-state and"
+ "limit is allowed");
have_state = cmd;
- {
- ipfw_insn_limit *c = (ipfw_insn_limit *)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 @@ read_options:
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 29db1bb..9ab4aba 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 @@ check_body:
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 @@ check_body:
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 @@ check_body:
mtag->m_tag_id <= p[1];
}
break;
+ }
/*
* The second set of opcodes represents 'actions',
@@ -2988,7 +3003,7 @@ check_body:
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 */
}
OpenPOWER on IntegriCloud