aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sbin/ipfw/ipfw.862
-rw-r--r--sbin/ipfw/ipfw2.c80
-rw-r--r--sys/netinet/ip_fw.h6
-rw-r--r--sys/netinet/ip_fw2.c58
4 files changed, 196 insertions, 10 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 6dd3b6d55029..2011dd2b0830 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 12, 2006
+.Dd May 24, 2006
.Dt IPFW 8
.Os
.Sh NAME
@@ -420,6 +420,10 @@ rules is the following:
.Ar action
.Op Cm log Op Cm logamount Ar number
.Op Cm altq Ar queue
+.Oo
+.Bro Cm tag | untag
+.Brc Ar number
+.Oc
.Ar body
.Ek
.Ed
@@ -552,6 +556,53 @@ command.
Note: logging is done after all other packet matching conditions
have been successfully verified, and before performing the final
action (accept, deny, etc.) on the packet.
+.It Cm tag Ar number
+When a packet matches a rule with the
+.Cm tag
+keyword, the numeric tag for the given
+.Ar number
+in the range 0..65535 will be attached to the packet.
+The tag acts as an internal marker (it is not sent out over
+the wire) that can be used to identify these packets later on.
+This can be used, for example, to provide trust between interfaces
+and to start doing policy-based filtering.
+A packet can have mutiple tags at the same time.
+Tags are "sticky", meaning once a tag is applied to a packet by a
+matching rule it exists until explicit removal.
+Tags are kept with the packet everywhere within the kernel, but are
+lost when packet leaves the kernel, for example, on transmitting
+packet out to the network or sending packet to a
+.Xr divert 4
+socket.
+.Pp
+To check for previously applied tags, use the
+.Cm tagged
+rule option. To delete previously applied tag, use the
+.Cm untag
+keyword.
+.Pp
+Note: since tags are kept with the packet everywhere in kernelspace,
+they can be set and unset anywhere in kernel network subsystem
+(using
+.Xr mbuf_tags 9
+facility), not only by means of
+.Xr ipfw 4
+.Cm tag
+and
+.Cm untag
+keywords.
+For example, there can be a specialized
+.Xr netgraph 4
+node doing traffic analyzing and tagging for later inspecting
+in firewall.
+.It Cm untag Ar number
+When a packet matches a rule with the
+.Cm untag
+keyword, the tag with the number
+.Ar number
+is searched among the tags attached to this packet and,
+if found, removed from it.
+Other tags bound to packet, if present, are left untouched.
.It Cm altq Ar queue
When a packet matches a rule with the
.Cm altq
@@ -1362,6 +1413,15 @@ specified as an argument.
.It Cm src-port Ar ports
Matches IP packets whose source port is one of the port(s)
specified as argument.
+.It Cm tagged Ar tag-list
+Matches packets whose tags are included in
+.Ar tag-list ,
+which is either a single value or a list of values or ranges
+specified in the same way as
+.Ar ports .
+Tags can be applied to the packet using
+.Cm tag
+rule action parameter (see it's description for details on tags).
.It Cm tcpack Ar ack
TCP packets only.
Match if the TCP header acknowledgment number field is set to
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index eb5a5ab87ed1..36c3282eb963 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -82,6 +82,15 @@ 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); \
+ } \
+} while (0)
+
/*
* _s_x is a structure that stores a string <-> token pairs, used in
* various places in the parser. Entries are stored in arrays,
@@ -212,7 +221,10 @@ enum tokens {
TOK_ALTQ,
TOK_LOG,
+ TOK_TAG,
+ TOK_UNTAG,
+ TOK_TAGGED,
TOK_UID,
TOK_GID,
TOK_JAIL,
@@ -340,10 +352,13 @@ struct _s_x rule_actions[] = {
struct _s_x rule_action_params[] = {
{ "altq", TOK_ALTQ },
{ "log", TOK_LOG },
+ { "tag", TOK_TAG },
+ { "untag", TOK_UNTAG },
{ NULL, 0 } /* terminator */
};
struct _s_x rule_options[] = {
+ { "tagged", TOK_TAGGED },
{ "uid", TOK_UID },
{ "gid", TOK_GID },
{ "jail", TOK_JAIL },
@@ -572,6 +587,7 @@ struct _s_x _port_name[] = {
{"ipttl", O_IPTTL},
{"mac-type", O_MAC_TYPE},
{"tcpdatalen", O_TCPDATALEN},
+ {"tagged", O_TAGGED},
{NULL, 0}
};
@@ -1358,7 +1374,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
{
static int twidth = 0;
int l;
- ipfw_insn *cmd;
+ ipfw_insn *cmd, *tagptr = NULL;
char *comment = NULL; /* ptr to comment if we have one */
int proto = 0; /* default */
int flags = 0; /* prerequisites */
@@ -1500,6 +1516,10 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
altqptr = (ipfw_insn_altq *)cmd;
break;
+ case O_TAG:
+ tagptr = cmd;
+ break;
+
default:
printf("** unrecognized action %d len %d ",
cmd->opcode, cmd->len);
@@ -1520,6 +1540,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
else
printf(" altq %s", qname);
}
+ if (tagptr) {
+ if (tagptr->len & F_NOT)
+ printf(" untag %hu", tagptr->arg1);
+ else
+ printf(" tag %hu", tagptr->arg1);
+ }
/*
* then print the body.
@@ -1906,6 +1932,14 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
print_ext6hdr( (ipfw_insn *) cmd );
break;
+ case O_TAGGED:
+ if (F_LEN(cmd) == 1)
+ printf(" tagged %hu", cmd->arg1 );
+ else
+ print_newports((ipfw_insn_u16 *)cmd, 0,
+ O_TAGGED);
+ break;
+
default:
printf(" [opcode %d len %d]",
cmd->opcode, cmd->len);
@@ -3775,7 +3809,7 @@ add(int ac, char *av[])
* various flags used to record that we entered some fields.
*/
ipfw_insn *have_state = NULL; /* check-state or keep-state */
- ipfw_insn *have_log = NULL, *have_altq = NULL;
+ ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
size_t len;
int i;
@@ -4016,6 +4050,20 @@ chkarg:
}
break;
+ case TOK_TAG:
+ case TOK_UNTAG:
+ {
+ 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");
+ have_tag = cmd;
+ fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT,
+ strtoul(*av, NULL, 0));
+ ac--; av++;
+ }
+ break;
+
default:
abort();
}
@@ -4605,6 +4653,19 @@ read_options:
ac = 0;
break;
+ case TOK_TAGGED:
+ NEED1("missing tag number");
+ if (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));
+ }
+ ac--; av++;
+ break;
+
default:
errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
}
@@ -4641,9 +4702,8 @@ done:
fill_cmd(dst, O_PROBE_STATE, 0, 0);
dst = next_cmd(dst);
}
- /*
- * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ
- */
+
+ /* 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);
@@ -4652,6 +4712,7 @@ done:
case O_KEEP_STATE:
case O_LIMIT:
case O_ALTQ:
+ case O_TAG:
break;
default:
bcopy(src, dst, i * sizeof(uint32_t));
@@ -4672,9 +4733,7 @@ done:
*/
rule->act_ofs = dst - rule->cmd;
- /*
- * put back O_LOG, O_ALTQ if necessary
- */
+ /* put back O_LOG, O_ALTQ, O_TAG if necessary */
if (have_log) {
i = F_LEN(have_log);
bcopy(have_log, dst, i * sizeof(uint32_t));
@@ -4685,6 +4744,11 @@ done:
bcopy(have_altq, dst, i * sizeof(uint32_t));
dst += i;
}
+ if (have_tag) {
+ i = F_LEN(have_tag);
+ bcopy(have_tag, dst, i * sizeof(uint32_t));
+ dst += i;
+ }
/*
* copy all other actions
*/
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index 0893e466ab52..14ca1d5b1f68 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -157,6 +157,9 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_UNREACH6, /* arg1=icmpv6 code arg (deny) */
+ O_TAG, /* arg1=tag number */
+ O_TAGGED, /* arg1=tag number */
+
O_LAST_OPCODE /* not an opcode! */
};
@@ -215,6 +218,8 @@ typedef struct _ipfw_insn { /* template for instructions */
*/
#define F_INSN_SIZE(t) ((sizeof (t))/sizeof(u_int32_t))
+#define MTAG_IPFW 1148380143 /* IPFW-tagged cookie */
+
/*
* This is used to store an array of 16-bit entries (ports etc.)
*/
@@ -346,6 +351,7 @@ typedef struct _ipfw_insn_icmp6 {
* + if a rule has a "log" option, then the first action
* (at ACTION_PTR(r)) MUST be O_LOG
* + if a rule has an "altq" option, it comes after "log"
+ * + if a rule has an O_TAG option, it comes after "log" and "altq"
*
* NOTE: we use a simple linked list of rules because we never need
* to delete a rule without scanning the list. We do not use
diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c
index 559d5cd112c6..bb4933b3edc2 100644
--- a/sys/netinet/ip_fw2.c
+++ b/sys/netinet/ip_fw2.c
@@ -781,6 +781,9 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
if (cmd->opcode == O_PROB)
cmd += F_LEN(cmd);
+ if (cmd->opcode == O_TAG)
+ cmd += F_LEN(cmd);
+
action = action2;
switch (cmd->opcode) {
case O_DENY:
@@ -1660,6 +1663,8 @@ lookup_next_rule(struct ip_fw *me)
cmd += F_LEN(cmd);
if (cmd->opcode == O_ALTQ)
cmd += F_LEN(cmd);
+ if (cmd->opcode == O_TAG)
+ cmd += F_LEN(cmd);
if ( cmd->opcode == O_SKIPTO )
for (rule = me->next; rule ; rule = rule->next)
if (rule->rulenum >= cmd->arg1)
@@ -2886,6 +2891,55 @@ check_body:
match = is_ipv4;
break;
+ case O_TAG:
+ /* Packet is already tagged with this tag? */
+ mtag = m_tag_locate(m, MTAG_IPFW,
+ ((ipfw_insn *) cmd)->arg1, 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
+ * be inversed later).
+ * Otherwise we should allocate new mtag and
+ * push it into mbuf.
+ */
+ if (cmd->len & F_NOT) { /* `untag' action */
+ 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)
+ m_tag_prepend(m, mtag);
+ }
+ match = (cmd->len & F_NOT) ? 0: 1;
+ break;
+
+ case O_TAGGED:
+ if (cmdlen == 1) {
+ match = (m_tag_locate(m, MTAG_IPFW,
+ ((ipfw_insn *) cmd)->arg1, NULL) != NULL);
+ break;
+ }
+
+ /* we have ranges */
+ for (mtag = m_tag_first(m);
+ mtag != NULL && !match;
+ mtag = m_tag_next(m, mtag)) {
+ uint16_t *p;
+ int i;
+
+ if (mtag->m_tag_cookie != MTAG_IPFW)
+ continue;
+
+ p = ((ipfw_insn_u16 *)cmd)->ports;
+ i = cmdlen - 1;
+ for(; !match && i > 0; i--, p += 2)
+ match =
+ mtag->m_tag_id >= p[0] &&
+ mtag->m_tag_id <= p[1];
+ }
+ break;
+
/*
* The second set of opcodes represents 'actions',
* i.e. the terminal part of a rule once the packet
@@ -2905,7 +2959,7 @@ check_body:
* or to the SKIPTO target ('goto again' after
* having set f, cmd and l), respectively.
*
- * O_LOG and O_ALTQ action parameters:
+ * O_TAG, O_LOG and O_ALTQ action parameters:
* perform some action and set match = 1;
*
* O_LIMIT and O_KEEP_STATE: these opcodes are
@@ -3528,6 +3582,7 @@ check_ipfw_struct(struct ip_fw *rule, int size)
case O_IP6:
#endif
case O_IP4:
+ case O_TAG:
if (cmdlen != F_INSN_SIZE(ipfw_insn))
goto bad_size;
break;
@@ -3600,6 +3655,7 @@ check_ipfw_struct(struct ip_fw *rule, int size)
case O_IPTTL:
case O_IPLEN:
case O_TCPDATALEN:
+ case O_TAGGED:
if (cmdlen < 1 || cmdlen > 31)
goto bad_size;
break;