Implement internal (i.e. inside kernel) packet tagging using mbuf_tags(9).
Since tags are kept while packet resides in kernelspace, it's possible to use other kernel facilities (like netgraph nodes) for altering those tags. Submitted by: Andrey Elsukov <bu7cher at yandex dot ru> Submitted by: Vadim Goncharov <vadimnuclight at tpu dot ru> Approved by: glebius (mentor) Idea from: OpenBSD PF MFC after: 1 month
This commit is contained in:
parent
16a67f532d
commit
6a7d5cb645
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=158879
@ -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
|
||||
|
@ -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 @@ add(int ac, char *av[])
|
||||
}
|
||||
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 @@ add(int ac, char *av[])
|
||||
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 @@ add(int ac, char *av[])
|
||||
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 @@ add(int ac, char *av[])
|
||||
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 @@ add(int ac, char *av[])
|
||||
*/
|
||||
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 @@ add(int ac, char *av[])
|
||||
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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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 @@ do { \
|
||||
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 @@ do { \
|
||||
* 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;
|
||||
|
Loading…
Reference in New Issue
Block a user