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 #include <net/ip.h> 27 #include <net/ipv6.h> 28 #include <net/dsfield.h> 29 30 #include <linux/tc_act/tc_skbedit.h> 31 #include <net/tc_act/tc_skbedit.h> 32 33 static unsigned int skbedit_net_id; 34 static struct tc_action_ops act_skbedit_ops; 35 36 static int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a, 37 struct tcf_result *res) 38 { 39 struct tcf_skbedit *d = to_skbedit(a); 40 struct tcf_skbedit_params *params; 41 int action; 42 43 tcf_lastuse_update(&d->tcf_tm); 44 bstats_cpu_update(this_cpu_ptr(d->common.cpu_bstats), skb); 45 46 params = rcu_dereference_bh(d->params); 47 action = READ_ONCE(d->tcf_action); 48 49 if (params->flags & SKBEDIT_F_PRIORITY) 50 skb->priority = params->priority; 51 if (params->flags & SKBEDIT_F_INHERITDSFIELD) { 52 int wlen = skb_network_offset(skb); 53 54 switch (tc_skb_protocol(skb)) { 55 case htons(ETH_P_IP): 56 wlen += sizeof(struct iphdr); 57 if (!pskb_may_pull(skb, wlen)) 58 goto err; 59 skb->priority = ipv4_get_dsfield(ip_hdr(skb)) >> 2; 60 break; 61 62 case htons(ETH_P_IPV6): 63 wlen += sizeof(struct ipv6hdr); 64 if (!pskb_may_pull(skb, wlen)) 65 goto err; 66 skb->priority = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; 67 break; 68 } 69 } 70 if (params->flags & SKBEDIT_F_QUEUE_MAPPING && 71 skb->dev->real_num_tx_queues > params->queue_mapping) 72 skb_set_queue_mapping(skb, params->queue_mapping); 73 if (params->flags & SKBEDIT_F_MARK) { 74 skb->mark &= ~params->mask; 75 skb->mark |= params->mark & params->mask; 76 } 77 if (params->flags & SKBEDIT_F_PTYPE) 78 skb->pkt_type = params->ptype; 79 return action; 80 81 err: 82 qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats)); 83 return TC_ACT_SHOT; 84 } 85 86 static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { 87 [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, 88 [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, 89 [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, 90 [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) }, 91 [TCA_SKBEDIT_PTYPE] = { .len = sizeof(u16) }, 92 [TCA_SKBEDIT_MASK] = { .len = sizeof(u32) }, 93 [TCA_SKBEDIT_FLAGS] = { .len = sizeof(u64) }, 94 }; 95 96 static int tcf_skbedit_init(struct net *net, struct nlattr *nla, 97 struct nlattr *est, struct tc_action **a, 98 int ovr, int bind, bool rtnl_held, 99 struct netlink_ext_ack *extack) 100 { 101 struct tc_action_net *tn = net_generic(net, skbedit_net_id); 102 struct tcf_skbedit_params *params_old, *params_new; 103 struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; 104 struct tc_skbedit *parm; 105 struct tcf_skbedit *d; 106 u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL; 107 u16 *queue_mapping = NULL, *ptype = NULL; 108 bool exists = false; 109 int ret = 0, err; 110 111 if (nla == NULL) 112 return -EINVAL; 113 114 err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy, NULL); 115 if (err < 0) 116 return err; 117 118 if (tb[TCA_SKBEDIT_PARMS] == NULL) 119 return -EINVAL; 120 121 if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { 122 flags |= SKBEDIT_F_PRIORITY; 123 priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); 124 } 125 126 if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { 127 flags |= SKBEDIT_F_QUEUE_MAPPING; 128 queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); 129 } 130 131 if (tb[TCA_SKBEDIT_PTYPE] != NULL) { 132 ptype = nla_data(tb[TCA_SKBEDIT_PTYPE]); 133 if (!skb_pkt_type_ok(*ptype)) 134 return -EINVAL; 135 flags |= SKBEDIT_F_PTYPE; 136 } 137 138 if (tb[TCA_SKBEDIT_MARK] != NULL) { 139 flags |= SKBEDIT_F_MARK; 140 mark = nla_data(tb[TCA_SKBEDIT_MARK]); 141 } 142 143 if (tb[TCA_SKBEDIT_MASK] != NULL) { 144 flags |= SKBEDIT_F_MASK; 145 mask = nla_data(tb[TCA_SKBEDIT_MASK]); 146 } 147 148 if (tb[TCA_SKBEDIT_FLAGS] != NULL) { 149 u64 *pure_flags = nla_data(tb[TCA_SKBEDIT_FLAGS]); 150 151 if (*pure_flags & SKBEDIT_F_INHERITDSFIELD) 152 flags |= SKBEDIT_F_INHERITDSFIELD; 153 } 154 155 parm = nla_data(tb[TCA_SKBEDIT_PARMS]); 156 157 err = tcf_idr_check_alloc(tn, &parm->index, a, bind); 158 if (err < 0) 159 return err; 160 exists = err; 161 if (exists && bind) 162 return 0; 163 164 if (!flags) { 165 if (exists) 166 tcf_idr_release(*a, bind); 167 else 168 tcf_idr_cleanup(tn, parm->index); 169 return -EINVAL; 170 } 171 172 if (!exists) { 173 ret = tcf_idr_create(tn, parm->index, est, a, 174 &act_skbedit_ops, bind, true); 175 if (ret) { 176 tcf_idr_cleanup(tn, parm->index); 177 return ret; 178 } 179 180 d = to_skbedit(*a); 181 ret = ACT_P_CREATED; 182 } else { 183 d = to_skbedit(*a); 184 if (!ovr) { 185 tcf_idr_release(*a, bind); 186 return -EEXIST; 187 } 188 } 189 190 ASSERT_RTNL(); 191 192 params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); 193 if (unlikely(!params_new)) { 194 if (ret == ACT_P_CREATED) 195 tcf_idr_release(*a, bind); 196 return -ENOMEM; 197 } 198 199 params_new->flags = flags; 200 if (flags & SKBEDIT_F_PRIORITY) 201 params_new->priority = *priority; 202 if (flags & SKBEDIT_F_QUEUE_MAPPING) 203 params_new->queue_mapping = *queue_mapping; 204 if (flags & SKBEDIT_F_MARK) 205 params_new->mark = *mark; 206 if (flags & SKBEDIT_F_PTYPE) 207 params_new->ptype = *ptype; 208 /* default behaviour is to use all the bits */ 209 params_new->mask = 0xffffffff; 210 if (flags & SKBEDIT_F_MASK) 211 params_new->mask = *mask; 212 213 d->tcf_action = parm->action; 214 params_old = rtnl_dereference(d->params); 215 rcu_assign_pointer(d->params, params_new); 216 if (params_old) 217 kfree_rcu(params_old, rcu); 218 219 if (ret == ACT_P_CREATED) 220 tcf_idr_insert(tn, *a); 221 return ret; 222 } 223 224 static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, 225 int bind, int ref) 226 { 227 unsigned char *b = skb_tail_pointer(skb); 228 struct tcf_skbedit *d = to_skbedit(a); 229 struct tcf_skbedit_params *params; 230 struct tc_skbedit opt = { 231 .index = d->tcf_index, 232 .refcnt = refcount_read(&d->tcf_refcnt) - ref, 233 .bindcnt = atomic_read(&d->tcf_bindcnt) - bind, 234 .action = d->tcf_action, 235 }; 236 u64 pure_flags = 0; 237 struct tcf_t t; 238 239 params = rtnl_dereference(d->params); 240 241 if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt)) 242 goto nla_put_failure; 243 if ((params->flags & SKBEDIT_F_PRIORITY) && 244 nla_put_u32(skb, TCA_SKBEDIT_PRIORITY, params->priority)) 245 goto nla_put_failure; 246 if ((params->flags & SKBEDIT_F_QUEUE_MAPPING) && 247 nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING, params->queue_mapping)) 248 goto nla_put_failure; 249 if ((params->flags & SKBEDIT_F_MARK) && 250 nla_put_u32(skb, TCA_SKBEDIT_MARK, params->mark)) 251 goto nla_put_failure; 252 if ((params->flags & SKBEDIT_F_PTYPE) && 253 nla_put_u16(skb, TCA_SKBEDIT_PTYPE, params->ptype)) 254 goto nla_put_failure; 255 if ((params->flags & SKBEDIT_F_MASK) && 256 nla_put_u32(skb, TCA_SKBEDIT_MASK, params->mask)) 257 goto nla_put_failure; 258 if (params->flags & SKBEDIT_F_INHERITDSFIELD) 259 pure_flags |= SKBEDIT_F_INHERITDSFIELD; 260 if (pure_flags != 0 && 261 nla_put(skb, TCA_SKBEDIT_FLAGS, sizeof(pure_flags), &pure_flags)) 262 goto nla_put_failure; 263 264 tcf_tm_dump(&t, &d->tcf_tm); 265 if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD)) 266 goto nla_put_failure; 267 return skb->len; 268 269 nla_put_failure: 270 nlmsg_trim(skb, b); 271 return -1; 272 } 273 274 static void tcf_skbedit_cleanup(struct tc_action *a) 275 { 276 struct tcf_skbedit *d = to_skbedit(a); 277 struct tcf_skbedit_params *params; 278 279 params = rcu_dereference_protected(d->params, 1); 280 if (params) 281 kfree_rcu(params, rcu); 282 } 283 284 static int tcf_skbedit_walker(struct net *net, struct sk_buff *skb, 285 struct netlink_callback *cb, int type, 286 const struct tc_action_ops *ops, 287 struct netlink_ext_ack *extack) 288 { 289 struct tc_action_net *tn = net_generic(net, skbedit_net_id); 290 291 return tcf_generic_walker(tn, skb, cb, type, ops, extack); 292 } 293 294 static int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index, 295 struct netlink_ext_ack *extack) 296 { 297 struct tc_action_net *tn = net_generic(net, skbedit_net_id); 298 299 return tcf_idr_search(tn, a, index); 300 } 301 302 static int tcf_skbedit_delete(struct net *net, u32 index) 303 { 304 struct tc_action_net *tn = net_generic(net, skbedit_net_id); 305 306 return tcf_idr_delete_index(tn, index); 307 } 308 309 static struct tc_action_ops act_skbedit_ops = { 310 .kind = "skbedit", 311 .type = TCA_ACT_SKBEDIT, 312 .owner = THIS_MODULE, 313 .act = tcf_skbedit_act, 314 .dump = tcf_skbedit_dump, 315 .init = tcf_skbedit_init, 316 .cleanup = tcf_skbedit_cleanup, 317 .walk = tcf_skbedit_walker, 318 .lookup = tcf_skbedit_search, 319 .delete = tcf_skbedit_delete, 320 .size = sizeof(struct tcf_skbedit), 321 }; 322 323 static __net_init int skbedit_init_net(struct net *net) 324 { 325 struct tc_action_net *tn = net_generic(net, skbedit_net_id); 326 327 return tc_action_net_init(tn, &act_skbedit_ops); 328 } 329 330 static void __net_exit skbedit_exit_net(struct list_head *net_list) 331 { 332 tc_action_net_exit(net_list, skbedit_net_id); 333 } 334 335 static struct pernet_operations skbedit_net_ops = { 336 .init = skbedit_init_net, 337 .exit_batch = skbedit_exit_net, 338 .id = &skbedit_net_id, 339 .size = sizeof(struct tc_action_net), 340 }; 341 342 MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); 343 MODULE_DESCRIPTION("SKB Editing"); 344 MODULE_LICENSE("GPL"); 345 346 static int __init skbedit_init_module(void) 347 { 348 return tcf_register_action(&act_skbedit_ops, &skbedit_net_ops); 349 } 350 351 static void __exit skbedit_cleanup_module(void) 352 { 353 tcf_unregister_action(&act_skbedit_ops, &skbedit_net_ops); 354 } 355 356 module_init(skbedit_init_module); 357 module_exit(skbedit_cleanup_module); 358