1*ca9b0e27SAlexander Duyck /* 2*ca9b0e27SAlexander Duyck * Copyright (c) 2008, Intel Corporation. 3*ca9b0e27SAlexander Duyck * 4*ca9b0e27SAlexander Duyck * This program is free software; you can redistribute it and/or modify it 5*ca9b0e27SAlexander Duyck * under the terms and conditions of the GNU General Public License, 6*ca9b0e27SAlexander Duyck * version 2, as published by the Free Software Foundation. 7*ca9b0e27SAlexander Duyck * 8*ca9b0e27SAlexander Duyck * This program is distributed in the hope it will be useful, but WITHOUT 9*ca9b0e27SAlexander Duyck * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10*ca9b0e27SAlexander Duyck * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11*ca9b0e27SAlexander Duyck * more details. 12*ca9b0e27SAlexander Duyck * 13*ca9b0e27SAlexander Duyck * You should have received a copy of the GNU General Public License along with 14*ca9b0e27SAlexander Duyck * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 15*ca9b0e27SAlexander Duyck * Place - Suite 330, Boston, MA 02111-1307 USA. 16*ca9b0e27SAlexander Duyck * 17*ca9b0e27SAlexander Duyck * Author: Alexander Duyck <alexander.h.duyck@intel.com> 18*ca9b0e27SAlexander Duyck */ 19*ca9b0e27SAlexander Duyck 20*ca9b0e27SAlexander Duyck #include <linux/module.h> 21*ca9b0e27SAlexander Duyck #include <linux/init.h> 22*ca9b0e27SAlexander Duyck #include <linux/kernel.h> 23*ca9b0e27SAlexander Duyck #include <linux/skbuff.h> 24*ca9b0e27SAlexander Duyck #include <linux/rtnetlink.h> 25*ca9b0e27SAlexander Duyck #include <net/netlink.h> 26*ca9b0e27SAlexander Duyck #include <net/pkt_sched.h> 27*ca9b0e27SAlexander Duyck 28*ca9b0e27SAlexander Duyck #include <linux/tc_act/tc_skbedit.h> 29*ca9b0e27SAlexander Duyck #include <net/tc_act/tc_skbedit.h> 30*ca9b0e27SAlexander Duyck 31*ca9b0e27SAlexander Duyck #define SKBEDIT_TAB_MASK 15 32*ca9b0e27SAlexander Duyck static struct tcf_common *tcf_skbedit_ht[SKBEDIT_TAB_MASK + 1]; 33*ca9b0e27SAlexander Duyck static u32 skbedit_idx_gen; 34*ca9b0e27SAlexander Duyck static DEFINE_RWLOCK(skbedit_lock); 35*ca9b0e27SAlexander Duyck 36*ca9b0e27SAlexander Duyck static struct tcf_hashinfo skbedit_hash_info = { 37*ca9b0e27SAlexander Duyck .htab = tcf_skbedit_ht, 38*ca9b0e27SAlexander Duyck .hmask = SKBEDIT_TAB_MASK, 39*ca9b0e27SAlexander Duyck .lock = &skbedit_lock, 40*ca9b0e27SAlexander Duyck }; 41*ca9b0e27SAlexander Duyck 42*ca9b0e27SAlexander Duyck static int tcf_skbedit(struct sk_buff *skb, struct tc_action *a, 43*ca9b0e27SAlexander Duyck struct tcf_result *res) 44*ca9b0e27SAlexander Duyck { 45*ca9b0e27SAlexander Duyck struct tcf_skbedit *d = a->priv; 46*ca9b0e27SAlexander Duyck 47*ca9b0e27SAlexander Duyck spin_lock(&d->tcf_lock); 48*ca9b0e27SAlexander Duyck d->tcf_tm.lastuse = jiffies; 49*ca9b0e27SAlexander Duyck d->tcf_bstats.bytes += qdisc_pkt_len(skb); 50*ca9b0e27SAlexander Duyck d->tcf_bstats.packets++; 51*ca9b0e27SAlexander Duyck 52*ca9b0e27SAlexander Duyck if (d->flags & SKBEDIT_F_PRIORITY) 53*ca9b0e27SAlexander Duyck skb->priority = d->priority; 54*ca9b0e27SAlexander Duyck if (d->flags & SKBEDIT_F_QUEUE_MAPPING && 55*ca9b0e27SAlexander Duyck skb->dev->real_num_tx_queues > d->queue_mapping) 56*ca9b0e27SAlexander Duyck skb_set_queue_mapping(skb, d->queue_mapping); 57*ca9b0e27SAlexander Duyck 58*ca9b0e27SAlexander Duyck spin_unlock(&d->tcf_lock); 59*ca9b0e27SAlexander Duyck return d->tcf_action; 60*ca9b0e27SAlexander Duyck } 61*ca9b0e27SAlexander Duyck 62*ca9b0e27SAlexander Duyck static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { 63*ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, 64*ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, 65*ca9b0e27SAlexander Duyck [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, 66*ca9b0e27SAlexander Duyck }; 67*ca9b0e27SAlexander Duyck 68*ca9b0e27SAlexander Duyck static int tcf_skbedit_init(struct nlattr *nla, struct nlattr *est, 69*ca9b0e27SAlexander Duyck struct tc_action *a, int ovr, int bind) 70*ca9b0e27SAlexander Duyck { 71*ca9b0e27SAlexander Duyck struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; 72*ca9b0e27SAlexander Duyck struct tc_skbedit *parm; 73*ca9b0e27SAlexander Duyck struct tcf_skbedit *d; 74*ca9b0e27SAlexander Duyck struct tcf_common *pc; 75*ca9b0e27SAlexander Duyck u32 flags = 0, *priority = NULL; 76*ca9b0e27SAlexander Duyck u16 *queue_mapping = NULL; 77*ca9b0e27SAlexander Duyck int ret = 0, err; 78*ca9b0e27SAlexander Duyck 79*ca9b0e27SAlexander Duyck if (nla == NULL) 80*ca9b0e27SAlexander Duyck return -EINVAL; 81*ca9b0e27SAlexander Duyck 82*ca9b0e27SAlexander Duyck err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy); 83*ca9b0e27SAlexander Duyck if (err < 0) 84*ca9b0e27SAlexander Duyck return err; 85*ca9b0e27SAlexander Duyck 86*ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PARMS] == NULL) 87*ca9b0e27SAlexander Duyck return -EINVAL; 88*ca9b0e27SAlexander Duyck 89*ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { 90*ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_PRIORITY; 91*ca9b0e27SAlexander Duyck priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); 92*ca9b0e27SAlexander Duyck } 93*ca9b0e27SAlexander Duyck 94*ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { 95*ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_QUEUE_MAPPING; 96*ca9b0e27SAlexander Duyck queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); 97*ca9b0e27SAlexander Duyck } 98*ca9b0e27SAlexander Duyck if (!flags) 99*ca9b0e27SAlexander Duyck return -EINVAL; 100*ca9b0e27SAlexander Duyck 101*ca9b0e27SAlexander Duyck parm = nla_data(tb[TCA_SKBEDIT_PARMS]); 102*ca9b0e27SAlexander Duyck 103*ca9b0e27SAlexander Duyck pc = tcf_hash_check(parm->index, a, bind, &skbedit_hash_info); 104*ca9b0e27SAlexander Duyck if (!pc) { 105*ca9b0e27SAlexander Duyck pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, 106*ca9b0e27SAlexander Duyck &skbedit_idx_gen, &skbedit_hash_info); 107*ca9b0e27SAlexander Duyck if (unlikely(!pc)) 108*ca9b0e27SAlexander Duyck return -ENOMEM; 109*ca9b0e27SAlexander Duyck 110*ca9b0e27SAlexander Duyck d = to_skbedit(pc); 111*ca9b0e27SAlexander Duyck ret = ACT_P_CREATED; 112*ca9b0e27SAlexander Duyck } else { 113*ca9b0e27SAlexander Duyck d = to_skbedit(pc); 114*ca9b0e27SAlexander Duyck if (!ovr) { 115*ca9b0e27SAlexander Duyck tcf_hash_release(pc, bind, &skbedit_hash_info); 116*ca9b0e27SAlexander Duyck return -EEXIST; 117*ca9b0e27SAlexander Duyck } 118*ca9b0e27SAlexander Duyck } 119*ca9b0e27SAlexander Duyck 120*ca9b0e27SAlexander Duyck spin_lock_bh(&d->tcf_lock); 121*ca9b0e27SAlexander Duyck 122*ca9b0e27SAlexander Duyck d->flags = flags; 123*ca9b0e27SAlexander Duyck if (flags & SKBEDIT_F_PRIORITY) 124*ca9b0e27SAlexander Duyck d->priority = *priority; 125*ca9b0e27SAlexander Duyck if (flags & SKBEDIT_F_QUEUE_MAPPING) 126*ca9b0e27SAlexander Duyck d->queue_mapping = *queue_mapping; 127*ca9b0e27SAlexander Duyck d->tcf_action = parm->action; 128*ca9b0e27SAlexander Duyck 129*ca9b0e27SAlexander Duyck spin_unlock_bh(&d->tcf_lock); 130*ca9b0e27SAlexander Duyck 131*ca9b0e27SAlexander Duyck if (ret == ACT_P_CREATED) 132*ca9b0e27SAlexander Duyck tcf_hash_insert(pc, &skbedit_hash_info); 133*ca9b0e27SAlexander Duyck return ret; 134*ca9b0e27SAlexander Duyck } 135*ca9b0e27SAlexander Duyck 136*ca9b0e27SAlexander Duyck static inline int tcf_skbedit_cleanup(struct tc_action *a, int bind) 137*ca9b0e27SAlexander Duyck { 138*ca9b0e27SAlexander Duyck struct tcf_skbedit *d = a->priv; 139*ca9b0e27SAlexander Duyck 140*ca9b0e27SAlexander Duyck if (d) 141*ca9b0e27SAlexander Duyck return tcf_hash_release(&d->common, bind, &skbedit_hash_info); 142*ca9b0e27SAlexander Duyck return 0; 143*ca9b0e27SAlexander Duyck } 144*ca9b0e27SAlexander Duyck 145*ca9b0e27SAlexander Duyck static inline int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, 146*ca9b0e27SAlexander Duyck int bind, int ref) 147*ca9b0e27SAlexander Duyck { 148*ca9b0e27SAlexander Duyck unsigned char *b = skb_tail_pointer(skb); 149*ca9b0e27SAlexander Duyck struct tcf_skbedit *d = a->priv; 150*ca9b0e27SAlexander Duyck struct tc_skbedit opt; 151*ca9b0e27SAlexander Duyck struct tcf_t t; 152*ca9b0e27SAlexander Duyck 153*ca9b0e27SAlexander Duyck opt.index = d->tcf_index; 154*ca9b0e27SAlexander Duyck opt.refcnt = d->tcf_refcnt - ref; 155*ca9b0e27SAlexander Duyck opt.bindcnt = d->tcf_bindcnt - bind; 156*ca9b0e27SAlexander Duyck opt.action = d->tcf_action; 157*ca9b0e27SAlexander Duyck NLA_PUT(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt); 158*ca9b0e27SAlexander Duyck if (d->flags & SKBEDIT_F_PRIORITY) 159*ca9b0e27SAlexander Duyck NLA_PUT(skb, TCA_SKBEDIT_PRIORITY, sizeof(d->priority), 160*ca9b0e27SAlexander Duyck &d->priority); 161*ca9b0e27SAlexander Duyck if (d->flags & SKBEDIT_F_QUEUE_MAPPING) 162*ca9b0e27SAlexander Duyck NLA_PUT(skb, TCA_SKBEDIT_QUEUE_MAPPING, 163*ca9b0e27SAlexander Duyck sizeof(d->queue_mapping), &d->queue_mapping); 164*ca9b0e27SAlexander Duyck t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install); 165*ca9b0e27SAlexander Duyck t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse); 166*ca9b0e27SAlexander Duyck t.expires = jiffies_to_clock_t(d->tcf_tm.expires); 167*ca9b0e27SAlexander Duyck NLA_PUT(skb, TCA_SKBEDIT_TM, sizeof(t), &t); 168*ca9b0e27SAlexander Duyck return skb->len; 169*ca9b0e27SAlexander Duyck 170*ca9b0e27SAlexander Duyck nla_put_failure: 171*ca9b0e27SAlexander Duyck nlmsg_trim(skb, b); 172*ca9b0e27SAlexander Duyck return -1; 173*ca9b0e27SAlexander Duyck } 174*ca9b0e27SAlexander Duyck 175*ca9b0e27SAlexander Duyck static struct tc_action_ops act_skbedit_ops = { 176*ca9b0e27SAlexander Duyck .kind = "skbedit", 177*ca9b0e27SAlexander Duyck .hinfo = &skbedit_hash_info, 178*ca9b0e27SAlexander Duyck .type = TCA_ACT_SKBEDIT, 179*ca9b0e27SAlexander Duyck .capab = TCA_CAP_NONE, 180*ca9b0e27SAlexander Duyck .owner = THIS_MODULE, 181*ca9b0e27SAlexander Duyck .act = tcf_skbedit, 182*ca9b0e27SAlexander Duyck .dump = tcf_skbedit_dump, 183*ca9b0e27SAlexander Duyck .cleanup = tcf_skbedit_cleanup, 184*ca9b0e27SAlexander Duyck .init = tcf_skbedit_init, 185*ca9b0e27SAlexander Duyck .walk = tcf_generic_walker, 186*ca9b0e27SAlexander Duyck }; 187*ca9b0e27SAlexander Duyck 188*ca9b0e27SAlexander Duyck MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); 189*ca9b0e27SAlexander Duyck MODULE_DESCRIPTION("SKB Editing"); 190*ca9b0e27SAlexander Duyck MODULE_LICENSE("GPL"); 191*ca9b0e27SAlexander Duyck 192*ca9b0e27SAlexander Duyck static int __init skbedit_init_module(void) 193*ca9b0e27SAlexander Duyck { 194*ca9b0e27SAlexander Duyck return tcf_register_action(&act_skbedit_ops); 195*ca9b0e27SAlexander Duyck } 196*ca9b0e27SAlexander Duyck 197*ca9b0e27SAlexander Duyck static void __exit skbedit_cleanup_module(void) 198*ca9b0e27SAlexander Duyck { 199*ca9b0e27SAlexander Duyck tcf_unregister_action(&act_skbedit_ops); 200*ca9b0e27SAlexander Duyck } 201*ca9b0e27SAlexander Duyck 202*ca9b0e27SAlexander Duyck module_init(skbedit_init_module); 203*ca9b0e27SAlexander Duyck module_exit(skbedit_cleanup_module); 204