1ca9b0e27SAlexander Duyck /* 2ca9b0e27SAlexander Duyck * Copyright (c) 2008, Intel Corporation. 3ca9b0e27SAlexander Duyck * 4ca9b0e27SAlexander Duyck * This program is free software; you can redistribute it and/or modify it 5ca9b0e27SAlexander Duyck * under the terms and conditions of the GNU General Public License, 6ca9b0e27SAlexander Duyck * version 2, as published by the Free Software Foundation. 7ca9b0e27SAlexander Duyck * 8ca9b0e27SAlexander Duyck * This program is distributed in the hope it will be useful, but WITHOUT 9ca9b0e27SAlexander Duyck * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10ca9b0e27SAlexander Duyck * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11ca9b0e27SAlexander Duyck * more details. 12ca9b0e27SAlexander Duyck * 13ca9b0e27SAlexander Duyck * You should have received a copy of the GNU General Public License along with 14c057b190SJeff Kirsher * this program; if not, see <http://www.gnu.org/licenses/>. 15ca9b0e27SAlexander Duyck * 16ca9b0e27SAlexander Duyck * Author: Alexander Duyck <alexander.h.duyck@intel.com> 17ca9b0e27SAlexander Duyck */ 18ca9b0e27SAlexander Duyck 19ca9b0e27SAlexander Duyck #include <linux/module.h> 20ca9b0e27SAlexander Duyck #include <linux/init.h> 21ca9b0e27SAlexander Duyck #include <linux/kernel.h> 22ca9b0e27SAlexander Duyck #include <linux/skbuff.h> 23ca9b0e27SAlexander Duyck #include <linux/rtnetlink.h> 24ca9b0e27SAlexander Duyck #include <net/netlink.h> 25ca9b0e27SAlexander Duyck #include <net/pkt_sched.h> 26ca9b0e27SAlexander Duyck 27ca9b0e27SAlexander Duyck #include <linux/tc_act/tc_skbedit.h> 28ca9b0e27SAlexander Duyck #include <net/tc_act/tc_skbedit.h> 29ca9b0e27SAlexander Duyck 30ca9b0e27SAlexander Duyck #define SKBEDIT_TAB_MASK 15 31369ba567SWANG Cong static struct tcf_hashinfo skbedit_hash_info; 32ca9b0e27SAlexander Duyck 33dc7f9f6eSEric Dumazet static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a, 34ca9b0e27SAlexander Duyck struct tcf_result *res) 35ca9b0e27SAlexander Duyck { 36ca9b0e27SAlexander Duyck struct tcf_skbedit *d = a->priv; 37ca9b0e27SAlexander Duyck 38ca9b0e27SAlexander Duyck spin_lock(&d->tcf_lock); 39ca9b0e27SAlexander Duyck d->tcf_tm.lastuse = jiffies; 40bfe0d029SEric Dumazet bstats_update(&d->tcf_bstats, skb); 41ca9b0e27SAlexander Duyck 42ca9b0e27SAlexander Duyck if (d->flags & SKBEDIT_F_PRIORITY) 43ca9b0e27SAlexander Duyck skb->priority = d->priority; 44ca9b0e27SAlexander Duyck if (d->flags & SKBEDIT_F_QUEUE_MAPPING && 45ca9b0e27SAlexander Duyck skb->dev->real_num_tx_queues > d->queue_mapping) 46ca9b0e27SAlexander Duyck skb_set_queue_mapping(skb, d->queue_mapping); 471c55d62eSjamal if (d->flags & SKBEDIT_F_MARK) 481c55d62eSjamal skb->mark = d->mark; 49ca9b0e27SAlexander Duyck 50ca9b0e27SAlexander Duyck spin_unlock(&d->tcf_lock); 51ca9b0e27SAlexander Duyck return d->tcf_action; 52ca9b0e27SAlexander Duyck } 53ca9b0e27SAlexander Duyck 54ca9b0e27SAlexander Duyck static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { 55ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, 56ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, 57ca9b0e27SAlexander Duyck [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, 581c55d62eSjamal [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) }, 59ca9b0e27SAlexander Duyck }; 60ca9b0e27SAlexander Duyck 61c1b52739SBenjamin LaHaise static int tcf_skbedit_init(struct net *net, struct nlattr *nla, 62c1b52739SBenjamin LaHaise struct nlattr *est, struct tc_action *a, 63c1b52739SBenjamin LaHaise int ovr, int bind) 64ca9b0e27SAlexander Duyck { 65ca9b0e27SAlexander Duyck struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; 66ca9b0e27SAlexander Duyck struct tc_skbedit *parm; 67ca9b0e27SAlexander Duyck struct tcf_skbedit *d; 681c55d62eSjamal u32 flags = 0, *priority = NULL, *mark = NULL; 69ca9b0e27SAlexander Duyck u16 *queue_mapping = NULL; 70ca9b0e27SAlexander Duyck int ret = 0, err; 71ca9b0e27SAlexander Duyck 72ca9b0e27SAlexander Duyck if (nla == NULL) 73ca9b0e27SAlexander Duyck return -EINVAL; 74ca9b0e27SAlexander Duyck 75ca9b0e27SAlexander Duyck err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy); 76ca9b0e27SAlexander Duyck if (err < 0) 77ca9b0e27SAlexander Duyck return err; 78ca9b0e27SAlexander Duyck 79ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PARMS] == NULL) 80ca9b0e27SAlexander Duyck return -EINVAL; 81ca9b0e27SAlexander Duyck 82ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { 83ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_PRIORITY; 84ca9b0e27SAlexander Duyck priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); 85ca9b0e27SAlexander Duyck } 86ca9b0e27SAlexander Duyck 87ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { 88ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_QUEUE_MAPPING; 89ca9b0e27SAlexander Duyck queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); 90ca9b0e27SAlexander Duyck } 911c55d62eSjamal 921c55d62eSjamal if (tb[TCA_SKBEDIT_MARK] != NULL) { 931c55d62eSjamal flags |= SKBEDIT_F_MARK; 941c55d62eSjamal mark = nla_data(tb[TCA_SKBEDIT_MARK]); 951c55d62eSjamal } 961c55d62eSjamal 97ca9b0e27SAlexander Duyck if (!flags) 98ca9b0e27SAlexander Duyck return -EINVAL; 99ca9b0e27SAlexander Duyck 100ca9b0e27SAlexander Duyck parm = nla_data(tb[TCA_SKBEDIT_PARMS]); 101ca9b0e27SAlexander Duyck 102*86062033SWANG Cong if (!tcf_hash_check(parm->index, a, bind)) { 103*86062033SWANG Cong ret = tcf_hash_create(parm->index, est, a, sizeof(*d), bind); 104*86062033SWANG Cong if (ret) 105*86062033SWANG Cong return ret; 106ca9b0e27SAlexander Duyck 107*86062033SWANG Cong d = to_skbedit(a); 108ca9b0e27SAlexander Duyck ret = ACT_P_CREATED; 109ca9b0e27SAlexander Duyck } else { 110*86062033SWANG Cong d = to_skbedit(a); 1111a29321eSJamal Hadi Salim if (bind) 1121a29321eSJamal Hadi Salim return 0; 113*86062033SWANG Cong tcf_hash_release(a, bind); 1141a29321eSJamal Hadi Salim if (!ovr) 115ca9b0e27SAlexander Duyck return -EEXIST; 116ca9b0e27SAlexander Duyck } 117ca9b0e27SAlexander Duyck 118ca9b0e27SAlexander Duyck spin_lock_bh(&d->tcf_lock); 119ca9b0e27SAlexander Duyck 120ca9b0e27SAlexander Duyck d->flags = flags; 121ca9b0e27SAlexander Duyck if (flags & SKBEDIT_F_PRIORITY) 122ca9b0e27SAlexander Duyck d->priority = *priority; 123ca9b0e27SAlexander Duyck if (flags & SKBEDIT_F_QUEUE_MAPPING) 124ca9b0e27SAlexander Duyck d->queue_mapping = *queue_mapping; 1251c55d62eSjamal if (flags & SKBEDIT_F_MARK) 1261c55d62eSjamal d->mark = *mark; 1271c55d62eSjamal 128ca9b0e27SAlexander Duyck d->tcf_action = parm->action; 129ca9b0e27SAlexander Duyck 130ca9b0e27SAlexander Duyck spin_unlock_bh(&d->tcf_lock); 131ca9b0e27SAlexander Duyck 132ca9b0e27SAlexander Duyck if (ret == ACT_P_CREATED) 133*86062033SWANG Cong tcf_hash_insert(a); 134ca9b0e27SAlexander Duyck return ret; 135ca9b0e27SAlexander Duyck } 136ca9b0e27SAlexander Duyck 137cc7ec456SEric Dumazet static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, 138ca9b0e27SAlexander Duyck int bind, int ref) 139ca9b0e27SAlexander Duyck { 140ca9b0e27SAlexander Duyck unsigned char *b = skb_tail_pointer(skb); 141ca9b0e27SAlexander Duyck struct tcf_skbedit *d = a->priv; 1421c40be12SEric Dumazet struct tc_skbedit opt = { 1431c40be12SEric Dumazet .index = d->tcf_index, 1441c40be12SEric Dumazet .refcnt = d->tcf_refcnt - ref, 1451c40be12SEric Dumazet .bindcnt = d->tcf_bindcnt - bind, 1461c40be12SEric Dumazet .action = d->tcf_action, 1471c40be12SEric Dumazet }; 148ca9b0e27SAlexander Duyck struct tcf_t t; 149ca9b0e27SAlexander Duyck 1501b34ec43SDavid S. Miller if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt)) 1511b34ec43SDavid S. Miller goto nla_put_failure; 1521b34ec43SDavid S. Miller if ((d->flags & SKBEDIT_F_PRIORITY) && 1531b34ec43SDavid S. Miller nla_put(skb, TCA_SKBEDIT_PRIORITY, sizeof(d->priority), 1541b34ec43SDavid S. Miller &d->priority)) 1551b34ec43SDavid S. Miller goto nla_put_failure; 1561b34ec43SDavid S. Miller if ((d->flags & SKBEDIT_F_QUEUE_MAPPING) && 1571b34ec43SDavid S. Miller nla_put(skb, TCA_SKBEDIT_QUEUE_MAPPING, 1581b34ec43SDavid S. Miller sizeof(d->queue_mapping), &d->queue_mapping)) 1591b34ec43SDavid S. Miller goto nla_put_failure; 1601b34ec43SDavid S. Miller if ((d->flags & SKBEDIT_F_MARK) && 1611b34ec43SDavid S. Miller nla_put(skb, TCA_SKBEDIT_MARK, sizeof(d->mark), 1621b34ec43SDavid S. Miller &d->mark)) 1631b34ec43SDavid S. Miller goto nla_put_failure; 164ca9b0e27SAlexander Duyck t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install); 165ca9b0e27SAlexander Duyck t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse); 166ca9b0e27SAlexander Duyck t.expires = jiffies_to_clock_t(d->tcf_tm.expires); 1671b34ec43SDavid S. Miller if (nla_put(skb, TCA_SKBEDIT_TM, sizeof(t), &t)) 1681b34ec43SDavid S. Miller goto nla_put_failure; 169ca9b0e27SAlexander Duyck return skb->len; 170ca9b0e27SAlexander Duyck 171ca9b0e27SAlexander Duyck nla_put_failure: 172ca9b0e27SAlexander Duyck nlmsg_trim(skb, b); 173ca9b0e27SAlexander Duyck return -1; 174ca9b0e27SAlexander Duyck } 175ca9b0e27SAlexander Duyck 176ca9b0e27SAlexander Duyck static struct tc_action_ops act_skbedit_ops = { 177ca9b0e27SAlexander Duyck .kind = "skbedit", 178ca9b0e27SAlexander Duyck .hinfo = &skbedit_hash_info, 179ca9b0e27SAlexander Duyck .type = TCA_ACT_SKBEDIT, 180ca9b0e27SAlexander Duyck .owner = THIS_MODULE, 181ca9b0e27SAlexander Duyck .act = tcf_skbedit, 182ca9b0e27SAlexander Duyck .dump = tcf_skbedit_dump, 183*86062033SWANG Cong .cleanup = tcf_hash_release, 184ca9b0e27SAlexander Duyck .init = tcf_skbedit_init, 185ca9b0e27SAlexander Duyck }; 186ca9b0e27SAlexander Duyck 187ca9b0e27SAlexander Duyck MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); 188ca9b0e27SAlexander Duyck MODULE_DESCRIPTION("SKB Editing"); 189ca9b0e27SAlexander Duyck MODULE_LICENSE("GPL"); 190ca9b0e27SAlexander Duyck 191ca9b0e27SAlexander Duyck static int __init skbedit_init_module(void) 192ca9b0e27SAlexander Duyck { 193568a153aSWANG Cong int err = tcf_hashinfo_init(&skbedit_hash_info, SKBEDIT_TAB_MASK); 194369ba567SWANG Cong if (err) 195369ba567SWANG Cong return err; 196ca9b0e27SAlexander Duyck return tcf_register_action(&act_skbedit_ops); 197ca9b0e27SAlexander Duyck } 198ca9b0e27SAlexander Duyck 199ca9b0e27SAlexander Duyck static void __exit skbedit_cleanup_module(void) 200ca9b0e27SAlexander Duyck { 201369ba567SWANG Cong tcf_hashinfo_destroy(&skbedit_hash_info); 202ca9b0e27SAlexander Duyck tcf_unregister_action(&act_skbedit_ops); 203ca9b0e27SAlexander Duyck } 204ca9b0e27SAlexander Duyck 205ca9b0e27SAlexander Duyck module_init(skbedit_init_module); 206ca9b0e27SAlexander Duyck module_exit(skbedit_cleanup_module); 207