#include <asm/uaccess.h>
#include <asm/system.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kmod.h>
#include <net/sock.h>
#include <net/pkt_sched.h>
+#include <net/pkt_cls.h>
#if 0 /* control */
#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
static struct tcf_proto_ops *tcf_proto_base;
/* Protects list of registered TC modules. It is pure SMP lock. */
-static rwlock_t cls_mod_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(cls_mod_lock);
/* Find classifier type by string name */
-struct tcf_proto_ops * tcf_proto_lookup_ops(struct rtattr *kind)
+static struct tcf_proto_ops * tcf_proto_lookup_ops(struct rtattr *kind)
{
struct tcf_proto_ops *t = NULL;
if (kind) {
read_lock(&cls_mod_lock);
for (t = tcf_proto_base; t; t = t->next) {
- if (rtattr_strcmp(kind, t->kind) == 0)
+ if (rtattr_strcmp(kind, t->kind) == 0) {
+ if (!try_module_get(t->owner))
+ t = NULL;
break;
+ }
}
read_unlock(&cls_mod_lock);
}
static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
{
- struct rtattr **tca = arg;
- struct tcmsg *t = NLMSG_DATA(n);
- u32 protocol = TC_H_MIN(t->tcm_info);
- u32 prio = TC_H_MAJ(t->tcm_info);
- u32 nprio = prio;
- u32 parent = t->tcm_parent;
+ struct rtattr **tca;
+ struct tcmsg *t;
+ u32 protocol;
+ u32 prio;
+ u32 nprio;
+ u32 parent;
struct net_device *dev;
struct Qdisc *q;
struct tcf_proto **back, **chain;
- struct tcf_proto *tp = NULL;
+ struct tcf_proto *tp;
struct tcf_proto_ops *tp_ops;
struct Qdisc_class_ops *cops;
- unsigned long cl = 0;
+ unsigned long cl;
unsigned long fh;
int err;
+replay:
+ tca = arg;
+ t = NLMSG_DATA(n);
+ protocol = TC_H_MIN(t->tcm_info);
+ prio = TC_H_MAJ(t->tcm_info);
+ nprio = prio;
+ parent = t->tcm_parent;
+ cl = 0;
+
if (prio == 0) {
/* If no priority is given, user wants we allocated it. */
if (n->nlmsg_type != RTM_NEWTFILTER || !(n->nlmsg_flags&NLM_F_CREATE))
err = -ENOBUFS;
if ((tp = kmalloc(sizeof(*tp), GFP_KERNEL)) == NULL)
goto errout;
+ err = -EINVAL;
tp_ops = tcf_proto_lookup_ops(tca[TCA_KIND-1]);
+ if (tp_ops == NULL) {
#ifdef CONFIG_KMOD
- if (tp_ops==NULL && tca[TCA_KIND-1] != NULL) {
struct rtattr *kind = tca[TCA_KIND-1];
+ char name[IFNAMSIZ];
- if (RTA_PAYLOAD(kind) <= IFNAMSIZ) {
- request_module("cls_%s", (char*)RTA_DATA(kind));
+ if (kind != NULL &&
+ rtattr_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) {
+ rtnl_unlock();
+ request_module("cls_%s", name);
+ rtnl_lock();
tp_ops = tcf_proto_lookup_ops(kind);
+ /* We dropped the RTNL semaphore in order to
+ * perform the module load. So, even if we
+ * succeeded in loading the module we have to
+ * replay the request. We indicate this using
+ * -EAGAIN.
+ */
+ if (tp_ops != NULL) {
+ module_put(tp_ops->owner);
+ err = -EAGAIN;
+ }
}
- }
#endif
- if (tp_ops == NULL) {
- err = -EINVAL;
kfree(tp);
goto errout;
}
tp->q = q;
tp->classify = tp_ops->classify;
tp->classid = parent;
- err = -EBUSY;
- if (!try_module_get(tp_ops->owner)) {
- kfree(tp);
- goto errout;
- }
if ((err = tp_ops->init(tp)) != 0) {
module_put(tp_ops->owner);
kfree(tp);
errout:
if (cl)
cops->put(q, cl);
+ if (err == -EAGAIN)
+ /* Replay the request. */
+ goto replay;
return err;
}
-unsigned long tcf_set_class(struct tcf_proto *tp, unsigned long *clp,
- unsigned long cl)
-{
- unsigned long old_cl;
-
- tcf_tree_lock(tp);
- old_cl = __cls_set_class(clp, cl);
- tcf_tree_unlock(tp);
-
- return old_cl;
-}
-
-
static int
tcf_fill_node(struct sk_buff *skb, struct tcf_proto *tp, unsigned long fh,
u32 pid, u32 seq, unsigned flags, int event)
return skb->len;
}
+void
+tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ if (exts->action) {
+ tcf_action_destroy(exts->action, TCA_ACT_UNBIND);
+ exts->action = NULL;
+ }
+#elif defined CONFIG_NET_CLS_POLICE
+ if (exts->police) {
+ tcf_police_release(exts->police, TCA_ACT_UNBIND);
+ exts->police = NULL;
+ }
+#endif
+}
+
+
+int
+tcf_exts_validate(struct tcf_proto *tp, struct rtattr **tb,
+ struct rtattr *rate_tlv, struct tcf_exts *exts,
+ struct tcf_ext_map *map)
+{
+ memset(exts, 0, sizeof(*exts));
+
+#ifdef CONFIG_NET_CLS_ACT
+ {
+ int err;
+ struct tc_action *act;
+
+ if (map->police && tb[map->police-1]) {
+ act = tcf_action_init_1(tb[map->police-1], rate_tlv, "police",
+ TCA_ACT_NOREPLACE, TCA_ACT_BIND, &err);
+ if (act == NULL)
+ return err;
+
+ act->type = TCA_OLD_COMPAT;
+ exts->action = act;
+ } else if (map->action && tb[map->action-1]) {
+ act = tcf_action_init(tb[map->action-1], rate_tlv, NULL,
+ TCA_ACT_NOREPLACE, TCA_ACT_BIND, &err);
+ if (act == NULL)
+ return err;
+
+ exts->action = act;
+ }
+ }
+#elif defined CONFIG_NET_CLS_POLICE
+ if (map->police && tb[map->police-1]) {
+ struct tcf_police *p;
+
+ p = tcf_police_locate(tb[map->police-1], rate_tlv);
+ if (p == NULL)
+ return -EINVAL;
+
+ exts->police = p;
+ } else if (map->action && tb[map->action-1])
+ return -EOPNOTSUPP;
+#else
+ if ((map->action && tb[map->action-1]) ||
+ (map->police && tb[map->police-1]))
+ return -EOPNOTSUPP;
+#endif
+
+ return 0;
+}
+
+void
+tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
+ struct tcf_exts *src)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ if (src->action) {
+ struct tc_action *act;
+ tcf_tree_lock(tp);
+ act = xchg(&dst->action, src->action);
+ tcf_tree_unlock(tp);
+ if (act)
+ tcf_action_destroy(act, TCA_ACT_UNBIND);
+ }
+#elif defined CONFIG_NET_CLS_POLICE
+ if (src->police) {
+ struct tcf_police *p;
+ tcf_tree_lock(tp);
+ p = xchg(&dst->police, src->police);
+ tcf_tree_unlock(tp);
+ if (p)
+ tcf_police_release(p, TCA_ACT_UNBIND);
+ }
+#endif
+}
+
+int
+tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts,
+ struct tcf_ext_map *map)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ if (map->action && exts->action) {
+ /*
+ * again for backward compatible mode - we want
+ * to work with both old and new modes of entering
+ * tc data even if iproute2 was newer - jhs
+ */
+ struct rtattr * p_rta = (struct rtattr*) skb->tail;
+
+ if (exts->action->type != TCA_OLD_COMPAT) {
+ RTA_PUT(skb, map->action, 0, NULL);
+ if (tcf_action_dump(skb, exts->action, 0, 0) < 0)
+ goto rtattr_failure;
+ p_rta->rta_len = skb->tail - (u8*)p_rta;
+ } else if (map->police) {
+ RTA_PUT(skb, map->police, 0, NULL);
+ if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0)
+ goto rtattr_failure;
+ p_rta->rta_len = skb->tail - (u8*)p_rta;
+ }
+ }
+#elif defined CONFIG_NET_CLS_POLICE
+ if (map->police && exts->police) {
+ struct rtattr * p_rta = (struct rtattr*) skb->tail;
+
+ RTA_PUT(skb, map->police, 0, NULL);
+
+ if (tcf_police_dump(skb, exts->police) < 0)
+ goto rtattr_failure;
+
+ p_rta->rta_len = skb->tail - (u8*)p_rta;
+ }
+#endif
+ return 0;
+rtattr_failure: __attribute__ ((unused))
+ return -1;
+}
+
+int
+tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts,
+ struct tcf_ext_map *map)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ if (exts->action)
+ if (tcf_action_copy_stats(skb, exts->action) < 0)
+ goto rtattr_failure;
+#elif defined CONFIG_NET_CLS_POLICE
+ if (exts->police)
+ if (tcf_police_dump_stats(skb, exts->police) < 0)
+ goto rtattr_failure;
+#endif
+ return 0;
+rtattr_failure: __attribute__ ((unused))
+ return -1;
+}
static int __init tc_filter_init(void)
{
EXPORT_SYMBOL(register_tcf_proto_ops);
EXPORT_SYMBOL(unregister_tcf_proto_ops);
-EXPORT_SYMBOL(tcf_set_class);
+EXPORT_SYMBOL(tcf_exts_validate);
+EXPORT_SYMBOL(tcf_exts_destroy);
+EXPORT_SYMBOL(tcf_exts_change);
+EXPORT_SYMBOL(tcf_exts_dump);
+EXPORT_SYMBOL(tcf_exts_dump_stats);