2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* net/sched/gact.c Generic actions
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* copyright Jamal Hadi Salim (2002-4)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
2007-03-26 10:06:12 +04:00
|
|
|
#include <net/netlink.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <net/pkt_sched.h>
|
|
|
|
#include <linux/tc_act/tc_gact.h>
|
|
|
|
#include <net/tc_act/tc_gact.h>
|
|
|
|
|
2006-08-22 10:54:55 +04:00
|
|
|
#define GACT_TAB_MASK 15
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#ifdef CONFIG_GACT_PROB
|
2006-08-22 10:54:55 +04:00
|
|
|
static int gact_net_rand(struct tcf_gact *gact)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2014-01-11 16:15:59 +04:00
|
|
|
if (!gact->tcfg_pval || prandom_u32() % gact->tcfg_pval)
|
2006-08-22 10:54:55 +04:00
|
|
|
return gact->tcf_action;
|
|
|
|
return gact->tcfg_paction;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-08-22 10:54:55 +04:00
|
|
|
static int gact_determ(struct tcf_gact *gact)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-12-02 07:21:44 +03:00
|
|
|
if (!gact->tcfg_pval || gact->tcf_bstats.packets % gact->tcfg_pval)
|
2006-08-22 10:54:55 +04:00
|
|
|
return gact->tcf_action;
|
|
|
|
return gact->tcfg_paction;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-08-22 10:54:55 +04:00
|
|
|
typedef int (*g_rand)(struct tcf_gact *gact);
|
2011-01-19 22:26:56 +03:00
|
|
|
static g_rand gact_rand[MAX_RAND] = { NULL, gact_net_rand, gact_determ };
|
2006-08-22 10:54:55 +04:00
|
|
|
#endif /* CONFIG_GACT_PROB */
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-01-24 07:36:30 +03:00
|
|
|
static const struct nla_policy gact_policy[TCA_GACT_MAX + 1] = {
|
|
|
|
[TCA_GACT_PARMS] = { .len = sizeof(struct tc_gact) },
|
|
|
|
[TCA_GACT_PROB] = { .len = sizeof(struct tc_gact_p) },
|
|
|
|
};
|
|
|
|
|
2013-01-14 09:15:39 +04:00
|
|
|
static int tcf_gact_init(struct net *net, struct nlattr *nla,
|
|
|
|
struct nlattr *est, struct tc_action *a,
|
|
|
|
int ovr, int bind)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-01-23 09:11:50 +03:00
|
|
|
struct nlattr *tb[TCA_GACT_MAX + 1];
|
2005-04-17 02:20:36 +04:00
|
|
|
struct tc_gact *parm;
|
2006-08-22 10:54:55 +04:00
|
|
|
struct tcf_gact *gact;
|
2005-04-17 02:20:36 +04:00
|
|
|
int ret = 0;
|
2008-01-24 07:33:32 +03:00
|
|
|
int err;
|
2012-08-03 14:57:52 +04:00
|
|
|
#ifdef CONFIG_GACT_PROB
|
|
|
|
struct tc_gact_p *p_parm = NULL;
|
|
|
|
#endif
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-01-24 07:33:32 +03:00
|
|
|
if (nla == NULL)
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EINVAL;
|
|
|
|
|
2008-01-24 07:36:30 +03:00
|
|
|
err = nla_parse_nested(tb, TCA_GACT_MAX, nla, gact_policy);
|
2008-01-24 07:33:32 +03:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2008-01-24 07:36:30 +03:00
|
|
|
if (tb[TCA_GACT_PARMS] == NULL)
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EINVAL;
|
2008-01-23 09:11:50 +03:00
|
|
|
parm = nla_data(tb[TCA_GACT_PARMS]);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-01-24 07:36:30 +03:00
|
|
|
#ifndef CONFIG_GACT_PROB
|
2008-01-23 09:11:50 +03:00
|
|
|
if (tb[TCA_GACT_PROB] != NULL)
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EOPNOTSUPP;
|
2012-08-03 14:57:52 +04:00
|
|
|
#else
|
|
|
|
if (tb[TCA_GACT_PROB]) {
|
|
|
|
p_parm = nla_data(tb[TCA_GACT_PROB]);
|
|
|
|
if (p_parm->ptype >= MAX_RAND)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
#endif
|
|
|
|
|
2014-02-12 05:07:31 +04:00
|
|
|
if (!tcf_hash_check(parm->index, a, bind)) {
|
|
|
|
ret = tcf_hash_create(parm->index, est, a, sizeof(*gact), bind);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2005-04-17 02:20:36 +04:00
|
|
|
ret = ACT_P_CREATED;
|
|
|
|
} else {
|
2013-12-23 17:02:11 +04:00
|
|
|
if (bind)/* dont override defaults */
|
|
|
|
return 0;
|
2014-02-12 05:07:31 +04:00
|
|
|
tcf_hash_release(a, bind);
|
2013-12-23 17:02:11 +04:00
|
|
|
if (!ovr)
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
2014-02-12 05:07:31 +04:00
|
|
|
gact = to_gact(a);
|
2006-08-22 10:54:55 +04:00
|
|
|
|
|
|
|
spin_lock_bh(&gact->tcf_lock);
|
|
|
|
gact->tcf_action = parm->action;
|
2005-04-17 02:20:36 +04:00
|
|
|
#ifdef CONFIG_GACT_PROB
|
2012-08-03 14:57:52 +04:00
|
|
|
if (p_parm) {
|
2006-08-22 10:54:55 +04:00
|
|
|
gact->tcfg_paction = p_parm->paction;
|
|
|
|
gact->tcfg_pval = p_parm->pval;
|
|
|
|
gact->tcfg_ptype = p_parm->ptype;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
#endif
|
2006-08-22 10:54:55 +04:00
|
|
|
spin_unlock_bh(&gact->tcf_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (ret == ACT_P_CREATED)
|
2014-02-12 05:07:31 +04:00
|
|
|
tcf_hash_insert(a);
|
2005-04-17 02:20:36 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-07-06 03:25:42 +04:00
|
|
|
static int tcf_gact(struct sk_buff *skb, const struct tc_action *a,
|
|
|
|
struct tcf_result *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-08-22 10:54:55 +04:00
|
|
|
struct tcf_gact *gact = a->priv;
|
2005-04-17 02:20:36 +04:00
|
|
|
int action = TC_ACT_SHOT;
|
|
|
|
|
2006-08-22 10:54:55 +04:00
|
|
|
spin_lock(&gact->tcf_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
#ifdef CONFIG_GACT_PROB
|
2012-08-03 14:57:52 +04:00
|
|
|
if (gact->tcfg_ptype)
|
2006-08-22 10:54:55 +04:00
|
|
|
action = gact_rand[gact->tcfg_ptype](gact);
|
2005-04-17 02:20:36 +04:00
|
|
|
else
|
2006-08-22 10:54:55 +04:00
|
|
|
action = gact->tcf_action;
|
2005-04-17 02:20:36 +04:00
|
|
|
#else
|
2006-08-22 10:54:55 +04:00
|
|
|
action = gact->tcf_action;
|
2005-04-17 02:20:36 +04:00
|
|
|
#endif
|
2008-07-20 11:08:27 +04:00
|
|
|
gact->tcf_bstats.bytes += qdisc_pkt_len(skb);
|
2006-08-22 10:54:55 +04:00
|
|
|
gact->tcf_bstats.packets++;
|
2005-04-17 02:20:36 +04:00
|
|
|
if (action == TC_ACT_SHOT)
|
2006-08-22 10:54:55 +04:00
|
|
|
gact->tcf_qstats.drops++;
|
|
|
|
gact->tcf_tm.lastuse = jiffies;
|
|
|
|
spin_unlock(&gact->tcf_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
2006-08-22 10:54:55 +04:00
|
|
|
static int tcf_gact_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-04-20 07:29:13 +04:00
|
|
|
unsigned char *b = skb_tail_pointer(skb);
|
2006-08-22 10:54:55 +04:00
|
|
|
struct tcf_gact *gact = a->priv;
|
2010-08-17 00:04:22 +04:00
|
|
|
struct tc_gact opt = {
|
|
|
|
.index = gact->tcf_index,
|
|
|
|
.refcnt = gact->tcf_refcnt - ref,
|
|
|
|
.bindcnt = gact->tcf_bindcnt - bind,
|
|
|
|
.action = gact->tcf_action,
|
|
|
|
};
|
2005-04-17 02:20:36 +04:00
|
|
|
struct tcf_t t;
|
|
|
|
|
2012-03-29 13:11:39 +04:00
|
|
|
if (nla_put(skb, TCA_GACT_PARMS, sizeof(opt), &opt))
|
|
|
|
goto nla_put_failure;
|
2005-04-17 02:20:36 +04:00
|
|
|
#ifdef CONFIG_GACT_PROB
|
2006-08-22 10:54:55 +04:00
|
|
|
if (gact->tcfg_ptype) {
|
2010-08-17 00:04:22 +04:00
|
|
|
struct tc_gact_p p_opt = {
|
|
|
|
.paction = gact->tcfg_paction,
|
|
|
|
.pval = gact->tcfg_pval,
|
|
|
|
.ptype = gact->tcfg_ptype,
|
|
|
|
};
|
|
|
|
|
2012-03-29 13:11:39 +04:00
|
|
|
if (nla_put(skb, TCA_GACT_PROB, sizeof(p_opt), &p_opt))
|
|
|
|
goto nla_put_failure;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
#endif
|
2006-08-22 10:54:55 +04:00
|
|
|
t.install = jiffies_to_clock_t(jiffies - gact->tcf_tm.install);
|
|
|
|
t.lastuse = jiffies_to_clock_t(jiffies - gact->tcf_tm.lastuse);
|
|
|
|
t.expires = jiffies_to_clock_t(gact->tcf_tm.expires);
|
2012-03-29 13:11:39 +04:00
|
|
|
if (nla_put(skb, TCA_GACT_TM, sizeof(t), &t))
|
|
|
|
goto nla_put_failure;
|
2005-04-17 02:20:36 +04:00
|
|
|
return skb->len;
|
|
|
|
|
2008-01-23 09:11:50 +03:00
|
|
|
nla_put_failure:
|
2007-03-26 10:06:12 +04:00
|
|
|
nlmsg_trim(skb, b);
|
2005-04-17 02:20:36 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct tc_action_ops act_gact_ops = {
|
|
|
|
.kind = "gact",
|
|
|
|
.type = TCA_ACT_GACT,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.act = tcf_gact,
|
|
|
|
.dump = tcf_gact_dump,
|
|
|
|
.init = tcf_gact_init,
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
|
|
|
|
MODULE_DESCRIPTION("Generic Classifier actions");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
2006-08-22 10:54:55 +04:00
|
|
|
static int __init gact_init_module(void)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_GACT_PROB
|
2011-01-19 22:26:56 +03:00
|
|
|
pr_info("GACT probability on\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
#else
|
2011-01-19 22:26:56 +03:00
|
|
|
pr_info("GACT probability NOT on\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
#endif
|
2014-02-12 05:07:33 +04:00
|
|
|
return tcf_register_action(&act_gact_ops, GACT_TAB_MASK);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-08-22 10:54:55 +04:00
|
|
|
static void __exit gact_cleanup_module(void)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
tcf_unregister_action(&act_gact_ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(gact_init_module);
|
|
|
|
module_exit(gact_cleanup_module);
|