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
This commit is contained in:
oleg 2006-06-15 09:39:22 +00:00
parent 45f57ec2f1
commit 7a65db868d
3 changed files with 160 additions and 97 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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 */
}