diff -Nru linux-2.6.10-bk4.orig/include/linux/pkt_cls.h linux-2.6.10-bk4/include/linux/pkt_cls.h --- linux-2.6.10-bk4.orig/include/linux/pkt_cls.h 2005-01-02 21:39:16.000000000 +0100 +++ linux-2.6.10-bk4/include/linux/pkt_cls.h 2005-01-02 21:31:48.000000000 +0100 @@ -319,4 +319,47 @@ #define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) +struct tcf_ematch_tree_hdr +{ + __u16 nmatches; + __u16 progid; +}; + +enum +{ + TCA_EMATCH_TREE_UNSPEC, + TCA_EMATCH_TREE_HDR, + TCA_EMATCH_TREE_LIST, + __TCA_EMATCH_TREE_MAX +}; +#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1) + +struct tcf_ematch_hdr +{ + __u16 handle; + __u16 matchID; + __u16 kind; + __u8 flags; + __u8 pad; /* currently unused */ +}; + +enum +{ + TCF_EM_CONTAINER, + __TCF_EM_MAX +}; + +#define TCF_EM_REL_MASK 3 +#define TCF_EM_REL_VALID(v) \ + (!(((v) & TCF_EM_REL_MASK) == TCF_EM_REL_MASK)) +#define TCF_EM_LAST_KEY(v) (!((v) & TCF_EM_REL_MASK)) + +#define TCF_EM_REL_OBVIOUS(v, r) (TCF_EM_LAST_KEY(v) || \ + (!(r) && ((v) & TCF_EM_REL_AND)) || ((r) && ((v) & TCF_EM_REL_OR))) + +#define TCF_EM_REL_END (1<<0) +#define TCF_EM_REL_AND (1<<1) +#define TCF_EM_REL_OR (1<<2) +#define TCF_EM_INVERT (1<<3) + #endif diff -Nru linux-2.6.10-bk4.orig/include/linux/rtnetlink.h linux-2.6.10-bk4/include/linux/rtnetlink.h --- linux-2.6.10-bk4.orig/include/linux/rtnetlink.h 2005-01-02 21:39:16.000000000 +0100 +++ linux-2.6.10-bk4/include/linux/rtnetlink.h 2005-01-02 21:32:46.000000000 +0100 @@ -779,6 +779,11 @@ goto rtattr_failure; \ __rta_fill(skb, attrtype, attrlen, data); }) +#define RTA_PUT_NOHDR(skb, attrlen, data) \ +({ if (unlikely(skb_tailroom(skb) < (int)(attrlen))) \ + goto rtattr_failure; \ + memcpy(skb_put(skb, RTA_ALIGN(attrlen)), data, attrlen); }) + static inline struct rtattr * __rta_reserve(struct sk_buff *skb, int attrtype, int attrlen) { diff -Nru linux-2.6.10-bk4.orig/include/net/pkt_cls.h linux-2.6.10-bk4/include/net/pkt_cls.h --- linux-2.6.10-bk4.orig/include/net/pkt_cls.h 2005-01-02 21:39:16.000000000 +0100 +++ linux-2.6.10-bk4/include/net/pkt_cls.h 2005-01-02 21:33:21.000000000 +0100 @@ -149,6 +149,75 @@ extern int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts, struct tcf_ext_map *map); +#ifdef CONFIG_NET_EMATCH + +struct tcf_ematch_ops; + +struct tcf_ematch +{ + struct tcf_ematch_hdr hdr; + struct tcf_ematch_ops * ops; + unsigned long data; +}; + +struct tcf_ematch_tree +{ + struct tcf_ematch_tree_hdr hdr; + struct tcf_ematch * matches; + +}; + +#define em_lookup_match(t, i) (&(t)->matches[(i)]) + +struct tcf_ematch_ops +{ + int kind; + + int (*change)(struct tcf_proto *tp, void *data, + int len, struct tcf_ematch *m); + + int (*match)(struct sk_buff *skb, struct tcf_ematch *m); + int (*destroy)(struct tcf_proto *tp, struct tcf_ematch *m); + int (*dump)(struct sk_buff *skb, struct tcf_ematch *m); + + struct module * owner; + struct list_head link; +}; + +extern int tcf_em_register(struct tcf_ematch_ops *); +extern int tcf_em_unregister(struct tcf_ematch_ops *); +extern int tcf_em_tree_validate(struct tcf_proto *, struct rtattr *, + struct tcf_ematch_tree *); +extern void tcf_em_tree_destroy(struct tcf_proto *, struct tcf_ematch_tree *); +extern int tcf_em_tree_dump(struct sk_buff *, struct tcf_ematch_tree *, int); +extern int __tcf_em_tree_match(struct sk_buff *, struct tcf_ematch_tree *); + +static inline void +tcf_em_tree_change(struct tcf_proto *tp, struct tcf_ematch_tree *dst, + struct tcf_ematch_tree *src) +{ + tcf_tree_lock(tp); + memcpy(dst, src, sizeof(*dst)); + tcf_tree_unlock(tp); +} + +#define tcf_em_tree_match(skb, t) \ + ((t)->hdr.nmatches ? __tcf_em_tree_match(skb, t) : 1) + +#else /* CONFIG_NET_EMATCH */ + +struct tcf_ematch_tree +{ +}; + +#define tcf_em_tree_validate(tp, tb, t) do { } while(0) +#define tcf_em_tree_destroy(tp, t) do { } while(0) +#define tcf_em_tree_dump(skb, t, tlv) do { } while(0) +#define tcf_em_tree_change(tp, dst, src) do { } while(0) +#define tcf_em_tree_match(skb, t) do { } while(0) + +#endif /* CONFIG_NET_EMATCH */ + #ifdef CONFIG_NET_CLS_IND static inline int tcf_change_indev(struct tcf_proto *tp, char *indev, struct rtattr *indev_tlv) diff -Nru linux-2.6.10-bk4.orig/net/sched/Kconfig linux-2.6.10-bk4/net/sched/Kconfig --- linux-2.6.10-bk4.orig/net/sched/Kconfig 2005-01-02 21:39:16.000000000 +0100 +++ linux-2.6.10-bk4/net/sched/Kconfig 2005-01-02 21:31:44.000000000 +0100 @@ -375,6 +375,12 @@ To compile this code as a module, choose M here: the module will be called cls_rsvp6. +config NET_EMATCH + bool "Extended Matches" + depends on NET_CLS + ---help--- + TODO + config NET_CLS_ACT bool "Packet ACTION" depends on EXPERIMENTAL && NET_CLS && NET_QOS diff -Nru linux-2.6.10-bk4.orig/net/sched/Makefile linux-2.6.10-bk4/net/sched/Makefile --- linux-2.6.10-bk4.orig/net/sched/Makefile 2005-01-02 21:39:16.000000000 +0100 +++ linux-2.6.10-bk4/net/sched/Makefile 2005-01-02 21:31:48.000000000 +0100 @@ -33,3 +33,4 @@ obj-$(CONFIG_NET_CLS_RSVP) += cls_rsvp.o obj-$(CONFIG_NET_CLS_TCINDEX) += cls_tcindex.o obj-$(CONFIG_NET_CLS_RSVP6) += cls_rsvp6.o +obj-$(CONFIG_NET_EMATCH) += ematch.o diff -Nru linux-2.6.10-bk4.orig/net/sched/ematch.c linux-2.6.10-bk4/net/sched/ematch.c --- linux-2.6.10-bk4.orig/net/sched/ematch.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.10-bk4/net/sched/ematch.c 2005-01-01 23:32:13.000000000 +0100 @@ -0,0 +1,300 @@ +/* + * net/sched/ematch.c Extended Match API + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Thomas Graf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EMATCH_STACK_SIZE 32 + +static LIST_HEAD(ematch_ops); +static rwlock_t ematch_mod_lock = RW_LOCK_UNLOCKED; + +static inline struct tcf_ematch_ops * +tcf_em_lookup(u16 kind) +{ + struct tcf_ematch_ops *e = NULL; + + read_lock(&ematch_mod_lock); + list_for_each_entry(e, &ematch_ops, link) { + if (kind == e->kind) { + if (!try_module_get(e->owner)) + e = NULL; + break; + } + } + read_unlock(&ematch_mod_lock); + + return e; +} + +int tcf_em_register(struct tcf_ematch_ops *ops) +{ + int err = -EEXIST; + struct tcf_ematch_ops *e; + + write_lock(&ematch_mod_lock); + list_for_each_entry(e, &ematch_ops, link) + if (ops->kind == e->kind) + goto errout; + + list_add_tail(&ops->link, &ematch_ops); + err = 0; +errout: + write_unlock(&ematch_mod_lock); + return err; +} + +int tcf_em_unregister(struct tcf_ematch_ops *ops) +{ + int err = 0; + struct tcf_ematch_ops *e; + + write_lock(&ematch_mod_lock); + list_for_each_entry(e, &ematch_ops, link) { + if (e == ops) { + list_del(&e->link); + goto out; + } + } + + err = -ENOENT; +out: + write_unlock(&ematch_mod_lock); + return err; +} + +static int tcf_em_validate(struct tcf_proto *tp, struct tcf_ematch_tree_hdr *th, + struct tcf_ematch *m, struct rtattr *rta) +{ + int err = -EINVAL; + struct tcf_ematch_hdr *mh = RTA_DATA(rta); + int datalen = RTA_PAYLOAD(rta) - sizeof(*mh); + void *data = (void *) data + sizeof(*mh); + + if (!TCF_EM_REL_VALID(mh->flags)) + goto errout; + + memcpy(&m->hdr, mh, sizeof(*mh)); + + if (mh->kind == TCF_EM_CONTAINER) { + u32 ref; + + if (datalen < sizeof(ref)) + goto errout; + ref = *(u32 *) data; + if (ref >= th->nmatches) + goto errout; + m->data = ref; + } else { + struct tcf_ematch_ops *ops = tcf_em_lookup(mh->kind); + + if (ops == NULL) { + err = -ENOENT; + goto errout; + } + + if (ops->change) { + err = ops->change(tp, data, datalen, m); + if (err < 0) + goto errout; + } + } + + err = 0; +errout: + return err; +} + +int tcf_em_tree_validate(struct tcf_proto *tp, struct rtattr *rta, + struct tcf_ematch_tree *tree) +{ + int i, len, mlen, err = -EINVAL; + struct rtattr *m, *tb[TCA_EMATCH_TREE_MAX]; + struct tcf_ematch_tree_hdr *th; + + if (!rta || rtattr_parse_nested(tb, TCA_EMATCH_TREE_MAX, rta) < 0) + goto errout; + + if (RTA_PAYLOAD(tb[TCA_EMATCH_TREE_HDR-1]) < sizeof(*th) || + RTA_PAYLOAD(tb[TCA_EMATCH_TREE_LIST-1]) < sizeof(*m)) + goto errout; + + th = RTA_DATA(tb[TCA_EMATCH_TREE_HDR-1]); + m = RTA_DATA(tb[TCA_EMATCH_TREE_LIST-1]); + len = RTA_PAYLOAD(tb[TCA_EMATCH_TREE_LIST-1]); + mlen = th->nmatches * sizeof(struct tcf_ematch); + + memcpy(&tree->hdr, th, sizeof(*th)); + + tree->matches = kmalloc(mlen, GFP_KERNEL); + if (tree->matches == NULL) + goto errout; + memset(tree->matches, 0, mlen); + + for (i = 0; RTA_OK(m, len); i++) { + if (rta->rta_type != (i+1) || i >= th->nmatches || + RTA_PAYLOAD(rta) < sizeof(struct tcf_ematch_hdr)) { + err = -EINVAL; + goto errout_abort; + } + + err = tcf_em_validate(tp, th, em_lookup_match(tree, i), rta); + if (err < 0) + goto errout_abort; + + m = RTA_NEXT(m, len); + } + + if (i != th->nmatches) { + err = -EINVAL; + goto errout_abort; + } + + + err = 0; +errout: + return err; + +errout_abort: + tcf_em_tree_destroy(tp, tree); + return err; +} + + +void tcf_em_tree_destroy(struct tcf_proto *tp, struct tcf_ematch_tree *t) +{ + int i; + + if (t->matches == NULL) + return; + + for (i = 0; i < t->hdr.nmatches; i++) { + struct tcf_ematch *m = em_lookup_match(t, i); + if (m->ops) { + if (m->ops->destroy) + m->ops->destroy(tp, m); + module_put(m->ops->owner); + } + } + + kfree(t->matches); +} + +int tcf_em_tree_dump(struct sk_buff *skb, struct tcf_ematch_tree *t, int tlv) +{ + int i; + struct rtattr * p_rta = (struct rtattr*) skb->tail; + struct rtattr * pm_rta; + + RTA_PUT(skb, tlv, 0, NULL); + RTA_PUT(skb, TCA_EMATCH_TREE_HDR, sizeof(t->hdr), &t->hdr); + + pm_rta = (struct rtattr *) skb->tail; + RTA_PUT(skb, TCA_EMATCH_TREE_LIST, 0, NULL); + + for (i = 0; i < t->hdr.nmatches; i++) { + struct rtattr * pd_rta = (struct rtattr*) skb->tail; + struct tcf_ematch *m = em_lookup_match(t, i); + + RTA_PUT(skb, i+1, sizeof(m->hdr), &m->hdr); + if (m->ops->dump && m->ops->dump(skb, m) < 0) + goto rtattr_failure; + pd_rta->rta_len = skb->tail - (u8*) pd_rta; + } + + pm_rta->rta_len = skb->tail - (u8*)pm_rta; + p_rta->rta_len = skb->tail - (u8*)p_rta; + return 0; +rtattr_failure: + return -1; +} + +static inline int tcf_em_match(struct sk_buff *skb, struct tcf_ematch *m) +{ + int r = 0; + + if (m->ops->match) + r = m->ops->match(skb, m); + + return m->hdr.flags & TCF_EM_INVERT ? !r : r; +} + +/* Do not use this function directly, use tcf_em_tree_match */ +int __tcf_em_tree_match(struct sk_buff *skb, struct tcf_ematch_tree *t) +{ + int i = 0, n = 0, r = 0; + struct tcf_ematch *m; + int stack[EMATCH_STACK_SIZE]; + + memset(stack, 0, sizeof(stack)); + +proceed: + while (n < t->hdr.nmatches) { + m = em_lookup_match(t, n); + + if (m->hdr.kind == TCF_EM_CONTAINER) { + if (unlikely(i >= EMATCH_STACK_SIZE)) { + if (net_ratelimit()) + printk("Local stack overflow, " \ + "increase EMATCH_STACK_SIZE\n"); + return -1; + } + + if (unlikely(m->data <= n)) { + if (net_ratelimit()) + printk("Detected backward precedence " \ + "jump, fix your filter.\n"); + return -1; + } + + stack[i++] = n; + n = m->data; + continue; + } + + r = tcf_em_match(skb, m); + if (TCF_EM_REL_OBVIOUS(m->hdr.flags, r)) + break; + n++; + } + +pop_stack: + if (i > 0) { + n = stack[--i]; + m = em_lookup_match(t, n); + + if (TCF_EM_REL_OBVIOUS(m->hdr.flags, r)) + goto pop_stack; + else { + n++; + goto proceed; + } + } + + return r; +} + +EXPORT_SYMBOL(tcf_em_register); +EXPORT_SYMBOL(tcf_em_unregister); +EXPORT_SYMBOL(tcf_em_tree_validate); +EXPORT_SYMBOL(tcf_em_tree_destroy); +EXPORT_SYMBOL(tcf_em_tree_dump); +EXPORT_SYMBOL(__tcf_em_tree_match); +