Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2008, Intel Corporation.
0004  *
0005  * Author: Alexander Duyck <alexander.h.duyck@intel.com>
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/init.h>
0010 #include <linux/kernel.h>
0011 #include <linux/skbuff.h>
0012 #include <linux/rtnetlink.h>
0013 #include <net/netlink.h>
0014 #include <net/pkt_sched.h>
0015 #include <net/ip.h>
0016 #include <net/ipv6.h>
0017 #include <net/dsfield.h>
0018 #include <net/pkt_cls.h>
0019 
0020 #include <linux/tc_act/tc_skbedit.h>
0021 #include <net/tc_act/tc_skbedit.h>
0022 
0023 static unsigned int skbedit_net_id;
0024 static struct tc_action_ops act_skbedit_ops;
0025 
0026 static u16 tcf_skbedit_hash(struct tcf_skbedit_params *params,
0027                 struct sk_buff *skb)
0028 {
0029     u16 queue_mapping = params->queue_mapping;
0030 
0031     if (params->flags & SKBEDIT_F_TXQ_SKBHASH) {
0032         u32 hash = skb_get_hash(skb);
0033 
0034         queue_mapping += hash % params->mapping_mod;
0035     }
0036 
0037     return netdev_cap_txqueue(skb->dev, queue_mapping);
0038 }
0039 
0040 static int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a,
0041                struct tcf_result *res)
0042 {
0043     struct tcf_skbedit *d = to_skbedit(a);
0044     struct tcf_skbedit_params *params;
0045     int action;
0046 
0047     tcf_lastuse_update(&d->tcf_tm);
0048     bstats_update(this_cpu_ptr(d->common.cpu_bstats), skb);
0049 
0050     params = rcu_dereference_bh(d->params);
0051     action = READ_ONCE(d->tcf_action);
0052 
0053     if (params->flags & SKBEDIT_F_PRIORITY)
0054         skb->priority = params->priority;
0055     if (params->flags & SKBEDIT_F_INHERITDSFIELD) {
0056         int wlen = skb_network_offset(skb);
0057 
0058         switch (skb_protocol(skb, true)) {
0059         case htons(ETH_P_IP):
0060             wlen += sizeof(struct iphdr);
0061             if (!pskb_may_pull(skb, wlen))
0062                 goto err;
0063             skb->priority = ipv4_get_dsfield(ip_hdr(skb)) >> 2;
0064             break;
0065 
0066         case htons(ETH_P_IPV6):
0067             wlen += sizeof(struct ipv6hdr);
0068             if (!pskb_may_pull(skb, wlen))
0069                 goto err;
0070             skb->priority = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2;
0071             break;
0072         }
0073     }
0074     if (params->flags & SKBEDIT_F_QUEUE_MAPPING &&
0075         skb->dev->real_num_tx_queues > params->queue_mapping) {
0076 #ifdef CONFIG_NET_EGRESS
0077         netdev_xmit_skip_txqueue(true);
0078 #endif
0079         skb_set_queue_mapping(skb, tcf_skbedit_hash(params, skb));
0080     }
0081     if (params->flags & SKBEDIT_F_MARK) {
0082         skb->mark &= ~params->mask;
0083         skb->mark |= params->mark & params->mask;
0084     }
0085     if (params->flags & SKBEDIT_F_PTYPE)
0086         skb->pkt_type = params->ptype;
0087     return action;
0088 
0089 err:
0090     qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats));
0091     return TC_ACT_SHOT;
0092 }
0093 
0094 static void tcf_skbedit_stats_update(struct tc_action *a, u64 bytes,
0095                      u64 packets, u64 drops,
0096                      u64 lastuse, bool hw)
0097 {
0098     struct tcf_skbedit *d = to_skbedit(a);
0099     struct tcf_t *tm = &d->tcf_tm;
0100 
0101     tcf_action_update_stats(a, bytes, packets, drops, hw);
0102     tm->lastuse = max_t(u64, tm->lastuse, lastuse);
0103 }
0104 
0105 static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = {
0106     [TCA_SKBEDIT_PARMS]     = { .len = sizeof(struct tc_skbedit) },
0107     [TCA_SKBEDIT_PRIORITY]      = { .len = sizeof(u32) },
0108     [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) },
0109     [TCA_SKBEDIT_MARK]      = { .len = sizeof(u32) },
0110     [TCA_SKBEDIT_PTYPE]     = { .len = sizeof(u16) },
0111     [TCA_SKBEDIT_MASK]      = { .len = sizeof(u32) },
0112     [TCA_SKBEDIT_FLAGS]     = { .len = sizeof(u64) },
0113     [TCA_SKBEDIT_QUEUE_MAPPING_MAX] = { .len = sizeof(u16) },
0114 };
0115 
0116 static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
0117                 struct nlattr *est, struct tc_action **a,
0118                 struct tcf_proto *tp, u32 act_flags,
0119                 struct netlink_ext_ack *extack)
0120 {
0121     struct tc_action_net *tn = net_generic(net, skbedit_net_id);
0122     bool bind = act_flags & TCA_ACT_FLAGS_BIND;
0123     struct tcf_skbedit_params *params_new;
0124     struct nlattr *tb[TCA_SKBEDIT_MAX + 1];
0125     struct tcf_chain *goto_ch = NULL;
0126     struct tc_skbedit *parm;
0127     struct tcf_skbedit *d;
0128     u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL;
0129     u16 *queue_mapping = NULL, *ptype = NULL;
0130     u16 mapping_mod = 1;
0131     bool exists = false;
0132     int ret = 0, err;
0133     u32 index;
0134 
0135     if (nla == NULL)
0136         return -EINVAL;
0137 
0138     err = nla_parse_nested_deprecated(tb, TCA_SKBEDIT_MAX, nla,
0139                       skbedit_policy, NULL);
0140     if (err < 0)
0141         return err;
0142 
0143     if (tb[TCA_SKBEDIT_PARMS] == NULL)
0144         return -EINVAL;
0145 
0146     if (tb[TCA_SKBEDIT_PRIORITY] != NULL) {
0147         flags |= SKBEDIT_F_PRIORITY;
0148         priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]);
0149     }
0150 
0151     if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
0152         flags |= SKBEDIT_F_QUEUE_MAPPING;
0153         queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]);
0154     }
0155 
0156     if (tb[TCA_SKBEDIT_PTYPE] != NULL) {
0157         ptype = nla_data(tb[TCA_SKBEDIT_PTYPE]);
0158         if (!skb_pkt_type_ok(*ptype))
0159             return -EINVAL;
0160         flags |= SKBEDIT_F_PTYPE;
0161     }
0162 
0163     if (tb[TCA_SKBEDIT_MARK] != NULL) {
0164         flags |= SKBEDIT_F_MARK;
0165         mark = nla_data(tb[TCA_SKBEDIT_MARK]);
0166     }
0167 
0168     if (tb[TCA_SKBEDIT_MASK] != NULL) {
0169         flags |= SKBEDIT_F_MASK;
0170         mask = nla_data(tb[TCA_SKBEDIT_MASK]);
0171     }
0172 
0173     if (tb[TCA_SKBEDIT_FLAGS] != NULL) {
0174         u64 *pure_flags = nla_data(tb[TCA_SKBEDIT_FLAGS]);
0175 
0176         if (*pure_flags & SKBEDIT_F_TXQ_SKBHASH) {
0177             u16 *queue_mapping_max;
0178 
0179             if (!tb[TCA_SKBEDIT_QUEUE_MAPPING] ||
0180                 !tb[TCA_SKBEDIT_QUEUE_MAPPING_MAX]) {
0181                 NL_SET_ERR_MSG_MOD(extack, "Missing required range of queue_mapping.");
0182                 return -EINVAL;
0183             }
0184 
0185             queue_mapping_max =
0186                 nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING_MAX]);
0187             if (*queue_mapping_max < *queue_mapping) {
0188                 NL_SET_ERR_MSG_MOD(extack, "The range of queue_mapping is invalid, max < min.");
0189                 return -EINVAL;
0190             }
0191 
0192             mapping_mod = *queue_mapping_max - *queue_mapping + 1;
0193             flags |= SKBEDIT_F_TXQ_SKBHASH;
0194         }
0195         if (*pure_flags & SKBEDIT_F_INHERITDSFIELD)
0196             flags |= SKBEDIT_F_INHERITDSFIELD;
0197     }
0198 
0199     parm = nla_data(tb[TCA_SKBEDIT_PARMS]);
0200     index = parm->index;
0201     err = tcf_idr_check_alloc(tn, &index, a, bind);
0202     if (err < 0)
0203         return err;
0204     exists = err;
0205     if (exists && bind)
0206         return 0;
0207 
0208     if (!flags) {
0209         if (exists)
0210             tcf_idr_release(*a, bind);
0211         else
0212             tcf_idr_cleanup(tn, index);
0213         return -EINVAL;
0214     }
0215 
0216     if (!exists) {
0217         ret = tcf_idr_create(tn, index, est, a,
0218                      &act_skbedit_ops, bind, true, act_flags);
0219         if (ret) {
0220             tcf_idr_cleanup(tn, index);
0221             return ret;
0222         }
0223 
0224         d = to_skbedit(*a);
0225         ret = ACT_P_CREATED;
0226     } else {
0227         d = to_skbedit(*a);
0228         if (!(act_flags & TCA_ACT_FLAGS_REPLACE)) {
0229             tcf_idr_release(*a, bind);
0230             return -EEXIST;
0231         }
0232     }
0233     err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
0234     if (err < 0)
0235         goto release_idr;
0236 
0237     params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
0238     if (unlikely(!params_new)) {
0239         err = -ENOMEM;
0240         goto put_chain;
0241     }
0242 
0243     params_new->flags = flags;
0244     if (flags & SKBEDIT_F_PRIORITY)
0245         params_new->priority = *priority;
0246     if (flags & SKBEDIT_F_QUEUE_MAPPING) {
0247         params_new->queue_mapping = *queue_mapping;
0248         params_new->mapping_mod = mapping_mod;
0249     }
0250     if (flags & SKBEDIT_F_MARK)
0251         params_new->mark = *mark;
0252     if (flags & SKBEDIT_F_PTYPE)
0253         params_new->ptype = *ptype;
0254     /* default behaviour is to use all the bits */
0255     params_new->mask = 0xffffffff;
0256     if (flags & SKBEDIT_F_MASK)
0257         params_new->mask = *mask;
0258 
0259     spin_lock_bh(&d->tcf_lock);
0260     goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
0261     params_new = rcu_replace_pointer(d->params, params_new,
0262                      lockdep_is_held(&d->tcf_lock));
0263     spin_unlock_bh(&d->tcf_lock);
0264     if (params_new)
0265         kfree_rcu(params_new, rcu);
0266     if (goto_ch)
0267         tcf_chain_put_by_act(goto_ch);
0268 
0269     return ret;
0270 put_chain:
0271     if (goto_ch)
0272         tcf_chain_put_by_act(goto_ch);
0273 release_idr:
0274     tcf_idr_release(*a, bind);
0275     return err;
0276 }
0277 
0278 static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a,
0279                 int bind, int ref)
0280 {
0281     unsigned char *b = skb_tail_pointer(skb);
0282     struct tcf_skbedit *d = to_skbedit(a);
0283     struct tcf_skbedit_params *params;
0284     struct tc_skbedit opt = {
0285         .index   = d->tcf_index,
0286         .refcnt  = refcount_read(&d->tcf_refcnt) - ref,
0287         .bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
0288     };
0289     u64 pure_flags = 0;
0290     struct tcf_t t;
0291 
0292     spin_lock_bh(&d->tcf_lock);
0293     params = rcu_dereference_protected(d->params,
0294                        lockdep_is_held(&d->tcf_lock));
0295     opt.action = d->tcf_action;
0296 
0297     if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt))
0298         goto nla_put_failure;
0299     if ((params->flags & SKBEDIT_F_PRIORITY) &&
0300         nla_put_u32(skb, TCA_SKBEDIT_PRIORITY, params->priority))
0301         goto nla_put_failure;
0302     if ((params->flags & SKBEDIT_F_QUEUE_MAPPING) &&
0303         nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING, params->queue_mapping))
0304         goto nla_put_failure;
0305     if ((params->flags & SKBEDIT_F_MARK) &&
0306         nla_put_u32(skb, TCA_SKBEDIT_MARK, params->mark))
0307         goto nla_put_failure;
0308     if ((params->flags & SKBEDIT_F_PTYPE) &&
0309         nla_put_u16(skb, TCA_SKBEDIT_PTYPE, params->ptype))
0310         goto nla_put_failure;
0311     if ((params->flags & SKBEDIT_F_MASK) &&
0312         nla_put_u32(skb, TCA_SKBEDIT_MASK, params->mask))
0313         goto nla_put_failure;
0314     if (params->flags & SKBEDIT_F_INHERITDSFIELD)
0315         pure_flags |= SKBEDIT_F_INHERITDSFIELD;
0316     if (params->flags & SKBEDIT_F_TXQ_SKBHASH) {
0317         if (nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING_MAX,
0318                 params->queue_mapping + params->mapping_mod - 1))
0319             goto nla_put_failure;
0320 
0321         pure_flags |= SKBEDIT_F_TXQ_SKBHASH;
0322     }
0323     if (pure_flags != 0 &&
0324         nla_put(skb, TCA_SKBEDIT_FLAGS, sizeof(pure_flags), &pure_flags))
0325         goto nla_put_failure;
0326 
0327     tcf_tm_dump(&t, &d->tcf_tm);
0328     if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD))
0329         goto nla_put_failure;
0330     spin_unlock_bh(&d->tcf_lock);
0331 
0332     return skb->len;
0333 
0334 nla_put_failure:
0335     spin_unlock_bh(&d->tcf_lock);
0336     nlmsg_trim(skb, b);
0337     return -1;
0338 }
0339 
0340 static void tcf_skbedit_cleanup(struct tc_action *a)
0341 {
0342     struct tcf_skbedit *d = to_skbedit(a);
0343     struct tcf_skbedit_params *params;
0344 
0345     params = rcu_dereference_protected(d->params, 1);
0346     if (params)
0347         kfree_rcu(params, rcu);
0348 }
0349 
0350 static int tcf_skbedit_walker(struct net *net, struct sk_buff *skb,
0351                   struct netlink_callback *cb, int type,
0352                   const struct tc_action_ops *ops,
0353                   struct netlink_ext_ack *extack)
0354 {
0355     struct tc_action_net *tn = net_generic(net, skbedit_net_id);
0356 
0357     return tcf_generic_walker(tn, skb, cb, type, ops, extack);
0358 }
0359 
0360 static int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index)
0361 {
0362     struct tc_action_net *tn = net_generic(net, skbedit_net_id);
0363 
0364     return tcf_idr_search(tn, a, index);
0365 }
0366 
0367 static size_t tcf_skbedit_get_fill_size(const struct tc_action *act)
0368 {
0369     return nla_total_size(sizeof(struct tc_skbedit))
0370         + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_PRIORITY */
0371         + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_QUEUE_MAPPING */
0372         + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_QUEUE_MAPPING_MAX */
0373         + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MARK */
0374         + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_PTYPE */
0375         + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MASK */
0376         + nla_total_size_64bit(sizeof(u64)); /* TCA_SKBEDIT_FLAGS */
0377 }
0378 
0379 static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data,
0380                      u32 *index_inc, bool bind,
0381                      struct netlink_ext_ack *extack)
0382 {
0383     if (bind) {
0384         struct flow_action_entry *entry = entry_data;
0385 
0386         if (is_tcf_skbedit_mark(act)) {
0387             entry->id = FLOW_ACTION_MARK;
0388             entry->mark = tcf_skbedit_mark(act);
0389         } else if (is_tcf_skbedit_ptype(act)) {
0390             entry->id = FLOW_ACTION_PTYPE;
0391             entry->ptype = tcf_skbedit_ptype(act);
0392         } else if (is_tcf_skbedit_priority(act)) {
0393             entry->id = FLOW_ACTION_PRIORITY;
0394             entry->priority = tcf_skbedit_priority(act);
0395         } else if (is_tcf_skbedit_queue_mapping(act)) {
0396             NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"queue_mapping\" option is used");
0397             return -EOPNOTSUPP;
0398         } else if (is_tcf_skbedit_inheritdsfield(act)) {
0399             NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"inheritdsfield\" option is used");
0400             return -EOPNOTSUPP;
0401         } else {
0402             NL_SET_ERR_MSG_MOD(extack, "Unsupported skbedit option offload");
0403             return -EOPNOTSUPP;
0404         }
0405         *index_inc = 1;
0406     } else {
0407         struct flow_offload_action *fl_action = entry_data;
0408 
0409         if (is_tcf_skbedit_mark(act))
0410             fl_action->id = FLOW_ACTION_MARK;
0411         else if (is_tcf_skbedit_ptype(act))
0412             fl_action->id = FLOW_ACTION_PTYPE;
0413         else if (is_tcf_skbedit_priority(act))
0414             fl_action->id = FLOW_ACTION_PRIORITY;
0415         else
0416             return -EOPNOTSUPP;
0417     }
0418 
0419     return 0;
0420 }
0421 
0422 static struct tc_action_ops act_skbedit_ops = {
0423     .kind       =   "skbedit",
0424     .id     =   TCA_ID_SKBEDIT,
0425     .owner      =   THIS_MODULE,
0426     .act        =   tcf_skbedit_act,
0427     .stats_update   =   tcf_skbedit_stats_update,
0428     .dump       =   tcf_skbedit_dump,
0429     .init       =   tcf_skbedit_init,
0430     .cleanup    =   tcf_skbedit_cleanup,
0431     .walk       =   tcf_skbedit_walker,
0432     .get_fill_size  =   tcf_skbedit_get_fill_size,
0433     .lookup     =   tcf_skbedit_search,
0434     .offload_act_setup =    tcf_skbedit_offload_act_setup,
0435     .size       =   sizeof(struct tcf_skbedit),
0436 };
0437 
0438 static __net_init int skbedit_init_net(struct net *net)
0439 {
0440     struct tc_action_net *tn = net_generic(net, skbedit_net_id);
0441 
0442     return tc_action_net_init(net, tn, &act_skbedit_ops);
0443 }
0444 
0445 static void __net_exit skbedit_exit_net(struct list_head *net_list)
0446 {
0447     tc_action_net_exit(net_list, skbedit_net_id);
0448 }
0449 
0450 static struct pernet_operations skbedit_net_ops = {
0451     .init = skbedit_init_net,
0452     .exit_batch = skbedit_exit_net,
0453     .id   = &skbedit_net_id,
0454     .size = sizeof(struct tc_action_net),
0455 };
0456 
0457 MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>");
0458 MODULE_DESCRIPTION("SKB Editing");
0459 MODULE_LICENSE("GPL");
0460 
0461 static int __init skbedit_init_module(void)
0462 {
0463     return tcf_register_action(&act_skbedit_ops, &skbedit_net_ops);
0464 }
0465 
0466 static void __exit skbedit_cleanup_module(void)
0467 {
0468     tcf_unregister_action(&act_skbedit_ops, &skbedit_net_ops);
0469 }
0470 
0471 module_init(skbedit_init_module);
0472 module_exit(skbedit_cleanup_module);