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