Files
linux-apfs/net/sched/act_skbedit.c
T

276 lines
7.2 KiB
C
Raw Normal View History

2008-09-12 16:30:20 -07:00
/*
* Copyright (c) 2008, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
2013-12-06 09:13:44 -08:00
* this program; if not, see <http://www.gnu.org/licenses/>.
2008-09-12 16:30:20 -07:00
*
* Author: Alexander Duyck <alexander.h.duyck@intel.com>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
#include <net/netlink.h>
#include <net/pkt_sched.h>
#include <linux/tc_act/tc_skbedit.h>
#include <net/tc_act/tc_skbedit.h>
#define SKBEDIT_TAB_MASK 15
static unsigned int skbedit_net_id;
2016-07-25 16:09:41 -07:00
static struct tc_action_ops act_skbedit_ops;
static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a,
2008-09-12 16:30:20 -07:00
struct tcf_result *res)
{
2016-07-25 16:09:41 -07:00
struct tcf_skbedit *d = to_skbedit(a);
2008-09-12 16:30:20 -07:00
spin_lock(&d->tcf_lock);
tcf_lastuse_update(&d->tcf_tm);
2011-01-09 08:30:54 +00:00
bstats_update(&d->tcf_bstats, skb);
2008-09-12 16:30:20 -07:00
if (d->flags & SKBEDIT_F_PRIORITY)
skb->priority = d->priority;
if (d->flags & SKBEDIT_F_QUEUE_MAPPING &&
skb->dev->real_num_tx_queues > d->queue_mapping)
skb_set_queue_mapping(skb, d->queue_mapping);
if (d->flags & SKBEDIT_F_MARK) {
skb->mark &= ~d->mask;
skb->mark |= d->mark & d->mask;
}
if (d->flags & SKBEDIT_F_PTYPE)
skb->pkt_type = d->ptype;
2008-09-12 16:30:20 -07:00
spin_unlock(&d->tcf_lock);
return d->tcf_action;
}
static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = {
[TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) },
[TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) },
[TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) },
[TCA_SKBEDIT_MARK] = { .len = sizeof(u32) },
[TCA_SKBEDIT_PTYPE] = { .len = sizeof(u16) },
[TCA_SKBEDIT_MASK] = { .len = sizeof(u32) },
2008-09-12 16:30:20 -07:00
};
2013-01-14 05:15:39 +00:00
static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
2016-07-25 16:09:41 -07:00
struct nlattr *est, struct tc_action **a,
2013-01-14 05:15:39 +00:00
int ovr, int bind)
2008-09-12 16:30:20 -07:00
{
struct tc_action_net *tn = net_generic(net, skbedit_net_id);
2008-09-12 16:30:20 -07:00
struct nlattr *tb[TCA_SKBEDIT_MAX + 1];
struct tc_skbedit *parm;
struct tcf_skbedit *d;
u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL;
u16 *queue_mapping = NULL, *ptype = NULL;
2016-06-13 13:46:28 -07:00
bool exists = false;
int ret = 0, err;
2008-09-12 16:30:20 -07:00
if (nla == NULL)
return -EINVAL;
err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy);
if (err < 0)
return err;
if (tb[TCA_SKBEDIT_PARMS] == NULL)
return -EINVAL;
if (tb[TCA_SKBEDIT_PRIORITY] != NULL) {
flags |= SKBEDIT_F_PRIORITY;
priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]);
}
if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
flags |= SKBEDIT_F_QUEUE_MAPPING;
queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]);
}
if (tb[TCA_SKBEDIT_PTYPE] != NULL) {
ptype = nla_data(tb[TCA_SKBEDIT_PTYPE]);
if (!skb_pkt_type_ok(*ptype))
return -EINVAL;
flags |= SKBEDIT_F_PTYPE;
}
if (tb[TCA_SKBEDIT_MARK] != NULL) {
flags |= SKBEDIT_F_MARK;
mark = nla_data(tb[TCA_SKBEDIT_MARK]);
}
if (tb[TCA_SKBEDIT_MASK] != NULL) {
flags |= SKBEDIT_F_MASK;
mask = nla_data(tb[TCA_SKBEDIT_MASK]);
}
2008-09-12 16:30:20 -07:00
parm = nla_data(tb[TCA_SKBEDIT_PARMS]);
2016-05-10 16:49:30 -04:00
exists = tcf_hash_check(tn, parm->index, a, bind);
if (exists && bind)
return 0;
if (!flags) {
2016-07-25 16:09:41 -07:00
tcf_hash_release(*a, bind);
2016-05-10 16:49:30 -04:00
return -EINVAL;
}
if (!exists) {
ret = tcf_hash_create(tn, parm->index, est, a,
2016-07-25 16:09:41 -07:00
&act_skbedit_ops, bind, false);
if (ret)
return ret;
2008-09-12 16:30:20 -07:00
2016-07-25 16:09:41 -07:00
d = to_skbedit(*a);
2008-09-12 16:30:20 -07:00
ret = ACT_P_CREATED;
} else {
2016-07-25 16:09:41 -07:00
d = to_skbedit(*a);
tcf_hash_release(*a, bind);
if (!ovr)
2008-09-12 16:30:20 -07:00
return -EEXIST;
}
spin_lock_bh(&d->tcf_lock);
d->flags = flags;
if (flags & SKBEDIT_F_PRIORITY)
d->priority = *priority;
if (flags & SKBEDIT_F_QUEUE_MAPPING)
d->queue_mapping = *queue_mapping;
if (flags & SKBEDIT_F_MARK)
d->mark = *mark;
if (flags & SKBEDIT_F_PTYPE)
d->ptype = *ptype;
/* default behaviour is to use all the bits */
d->mask = 0xffffffff;
if (flags & SKBEDIT_F_MASK)
d->mask = *mask;
2008-09-12 16:30:20 -07:00
d->tcf_action = parm->action;
spin_unlock_bh(&d->tcf_lock);
if (ret == ACT_P_CREATED)
2016-07-25 16:09:41 -07:00
tcf_hash_insert(tn, *a);
2008-09-12 16:30:20 -07:00
return ret;
}
2011-01-19 19:26:56 +00:00
static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a,
int bind, int ref)
2008-09-12 16:30:20 -07:00
{
unsigned char *b = skb_tail_pointer(skb);
2016-07-25 16:09:41 -07:00
struct tcf_skbedit *d = to_skbedit(a);
2010-08-16 20:04:22 +00:00
struct tc_skbedit opt = {
.index = d->tcf_index,
.refcnt = d->tcf_refcnt - ref,
.bindcnt = d->tcf_bindcnt - bind,
.action = d->tcf_action,
};
2008-09-12 16:30:20 -07:00
struct tcf_t t;
2012-03-29 05:11:39 -04:00
if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt))
goto nla_put_failure;
if ((d->flags & SKBEDIT_F_PRIORITY) &&
nla_put_u32(skb, TCA_SKBEDIT_PRIORITY, d->priority))
2012-03-29 05:11:39 -04:00
goto nla_put_failure;
if ((d->flags & SKBEDIT_F_QUEUE_MAPPING) &&
nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING, d->queue_mapping))
2012-03-29 05:11:39 -04:00
goto nla_put_failure;
if ((d->flags & SKBEDIT_F_MARK) &&
nla_put_u32(skb, TCA_SKBEDIT_MARK, d->mark))
2012-03-29 05:11:39 -04:00
goto nla_put_failure;
if ((d->flags & SKBEDIT_F_PTYPE) &&
nla_put_u16(skb, TCA_SKBEDIT_PTYPE, d->ptype))
goto nla_put_failure;
if ((d->flags & SKBEDIT_F_MASK) &&
nla_put_u32(skb, TCA_SKBEDIT_MASK, d->mask))
goto nla_put_failure;
tcf_tm_dump(&t, &d->tcf_tm);
2016-04-26 10:06:18 +02:00
if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD))
2012-03-29 05:11:39 -04:00
goto nla_put_failure;
2008-09-12 16:30:20 -07:00
return skb->len;
nla_put_failure:
nlmsg_trim(skb, b);
return -1;
}
static int tcf_skbedit_walker(struct net *net, struct sk_buff *skb,
struct netlink_callback *cb, int type,
2016-07-25 16:09:41 -07:00
const struct tc_action_ops *ops)
{
struct tc_action_net *tn = net_generic(net, skbedit_net_id);
2016-07-25 16:09:41 -07:00
return tcf_generic_walker(tn, skb, cb, type, ops);
}
2016-07-25 16:09:41 -07:00
static int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index)
{
struct tc_action_net *tn = net_generic(net, skbedit_net_id);
return tcf_hash_search(tn, a, index);
}
2008-09-12 16:30:20 -07:00
static struct tc_action_ops act_skbedit_ops = {
.kind = "skbedit",
.type = TCA_ACT_SKBEDIT,
.owner = THIS_MODULE,
.act = tcf_skbedit,
.dump = tcf_skbedit_dump,
.init = tcf_skbedit_init,
.walk = tcf_skbedit_walker,
.lookup = tcf_skbedit_search,
2016-07-25 16:09:41 -07:00
.size = sizeof(struct tcf_skbedit),
};
static __net_init int skbedit_init_net(struct net *net)
{
struct tc_action_net *tn = net_generic(net, skbedit_net_id);
return tc_action_net_init(tn, &act_skbedit_ops, SKBEDIT_TAB_MASK);
}
static void __net_exit skbedit_exit_net(struct net *net)
{
struct tc_action_net *tn = net_generic(net, skbedit_net_id);
tc_action_net_exit(tn);
}
static struct pernet_operations skbedit_net_ops = {
.init = skbedit_init_net,
.exit = skbedit_exit_net,
.id = &skbedit_net_id,
.size = sizeof(struct tc_action_net),
2008-09-12 16:30:20 -07:00
};
MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>");
MODULE_DESCRIPTION("SKB Editing");
MODULE_LICENSE("GPL");
static int __init skbedit_init_module(void)
{
return tcf_register_action(&act_skbedit_ops, &skbedit_net_ops);
2008-09-12 16:30:20 -07:00
}
static void __exit skbedit_cleanup_module(void)
{
tcf_unregister_action(&act_skbedit_ops, &skbedit_net_ops);
2008-09-12 16:30:20 -07:00
}
module_init(skbedit_init_module);
module_exit(skbedit_cleanup_module);