diff options
author | oleg <oleg@FreeBSD.org> | 2006-05-24 13:09:55 +0000 |
---|---|---|
committer | oleg <oleg@FreeBSD.org> | 2006-05-24 13:09:55 +0000 |
commit | 499297c74cc00692bc00ddab18c1e67dcbfaf0a9 (patch) | |
tree | adaee99342bafc97133da0f20bc5246de5eb3ec8 /sys | |
parent | 8ba778a258996098a6ddb344d5b39d907258b460 (diff) | |
download | FreeBSD-src-499297c74cc00692bc00ddab18c1e67dcbfaf0a9.zip FreeBSD-src-499297c74cc00692bc00ddab18c1e67dcbfaf0a9.tar.gz |
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
Diffstat (limited to 'sys')
-rw-r--r-- | sys/netinet/ip_fw.h | 6 | ||||
-rw-r--r-- | sys/netinet/ip_fw2.c | 58 |
2 files changed, 63 insertions, 1 deletions
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 0893e46..14ca1d5 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 559d5cd..bb4933b 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; |