19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2ca9b0e27SAlexander Duyck /* 3ca9b0e27SAlexander Duyck * Copyright (c) 2008, Intel Corporation. 4ca9b0e27SAlexander Duyck * 5ca9b0e27SAlexander Duyck * Author: Alexander Duyck <alexander.h.duyck@intel.com> 6ca9b0e27SAlexander Duyck */ 7ca9b0e27SAlexander Duyck 8ca9b0e27SAlexander Duyck #include <linux/module.h> 9ca9b0e27SAlexander Duyck #include <linux/init.h> 10ca9b0e27SAlexander Duyck #include <linux/kernel.h> 11ca9b0e27SAlexander Duyck #include <linux/skbuff.h> 12ca9b0e27SAlexander Duyck #include <linux/rtnetlink.h> 13ca9b0e27SAlexander Duyck #include <net/netlink.h> 14ca9b0e27SAlexander Duyck #include <net/pkt_sched.h> 15e7e3728bSQiaobin Fu #include <net/ip.h> 16e7e3728bSQiaobin Fu #include <net/ipv6.h> 17e7e3728bSQiaobin Fu #include <net/dsfield.h> 18ec7727bbSDavide Caratti #include <net/pkt_cls.h> 19ca9b0e27SAlexander Duyck 20ca9b0e27SAlexander Duyck #include <linux/tc_act/tc_skbedit.h> 21ca9b0e27SAlexander Duyck #include <net/tc_act/tc_skbedit.h> 22ca9b0e27SAlexander Duyck 23a85a970aSWANG Cong static struct tc_action_ops act_skbedit_ops; 24ddf97ccdSWANG Cong 2538a6f086STonghao Zhang static u16 tcf_skbedit_hash(struct tcf_skbedit_params *params, 2638a6f086STonghao Zhang struct sk_buff *skb) 2738a6f086STonghao Zhang { 2838a6f086STonghao Zhang u16 queue_mapping = params->queue_mapping; 2938a6f086STonghao Zhang 3038a6f086STonghao Zhang if (params->flags & SKBEDIT_F_TXQ_SKBHASH) { 3138a6f086STonghao Zhang u32 hash = skb_get_hash(skb); 3238a6f086STonghao Zhang 3338a6f086STonghao Zhang queue_mapping += hash % params->mapping_mod; 3438a6f086STonghao Zhang } 3538a6f086STonghao Zhang 3638a6f086STonghao Zhang return netdev_cap_txqueue(skb->dev, queue_mapping); 3738a6f086STonghao Zhang } 3838a6f086STonghao Zhang 3945da1dacSJamal Hadi Salim static int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a, 40ca9b0e27SAlexander Duyck struct tcf_result *res) 41ca9b0e27SAlexander Duyck { 42a85a970aSWANG Cong struct tcf_skbedit *d = to_skbedit(a); 43c749cddaSDavide Caratti struct tcf_skbedit_params *params; 44c749cddaSDavide Caratti int action; 45ca9b0e27SAlexander Duyck 469c4a4e48SJamal Hadi Salim tcf_lastuse_update(&d->tcf_tm); 4750dc9a85SAhmed S. Darwish bstats_update(this_cpu_ptr(d->common.cpu_bstats), skb); 48ca9b0e27SAlexander Duyck 497fd4b288SPaolo Abeni params = rcu_dereference_bh(d->params); 50c749cddaSDavide Caratti action = READ_ONCE(d->tcf_action); 51c749cddaSDavide Caratti 52c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_PRIORITY) 53c749cddaSDavide Caratti skb->priority = params->priority; 54c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_INHERITDSFIELD) { 55e7e3728bSQiaobin Fu int wlen = skb_network_offset(skb); 56e7e3728bSQiaobin Fu 57d7bf2ebeSToke Høiland-Jørgensen switch (skb_protocol(skb, true)) { 58e7e3728bSQiaobin Fu case htons(ETH_P_IP): 59e7e3728bSQiaobin Fu wlen += sizeof(struct iphdr); 60e7e3728bSQiaobin Fu if (!pskb_may_pull(skb, wlen)) 61e7e3728bSQiaobin Fu goto err; 62e7e3728bSQiaobin Fu skb->priority = ipv4_get_dsfield(ip_hdr(skb)) >> 2; 63e7e3728bSQiaobin Fu break; 64e7e3728bSQiaobin Fu 65e7e3728bSQiaobin Fu case htons(ETH_P_IPV6): 66e7e3728bSQiaobin Fu wlen += sizeof(struct ipv6hdr); 67e7e3728bSQiaobin Fu if (!pskb_may_pull(skb, wlen)) 68e7e3728bSQiaobin Fu goto err; 69e7e3728bSQiaobin Fu skb->priority = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; 70e7e3728bSQiaobin Fu break; 71e7e3728bSQiaobin Fu } 72e7e3728bSQiaobin Fu } 73c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_QUEUE_MAPPING && 742f1e85b1STonghao Zhang skb->dev->real_num_tx_queues > params->queue_mapping) { 752f1e85b1STonghao Zhang #ifdef CONFIG_NET_EGRESS 762f1e85b1STonghao Zhang netdev_xmit_skip_txqueue(true); 772f1e85b1STonghao Zhang #endif 7838a6f086STonghao Zhang skb_set_queue_mapping(skb, tcf_skbedit_hash(params, skb)); 792f1e85b1STonghao Zhang } 80c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_MARK) { 81c749cddaSDavide Caratti skb->mark &= ~params->mask; 82c749cddaSDavide Caratti skb->mark |= params->mark & params->mask; 834fe77d82SAntonio Quartulli } 84c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_PTYPE) 85c749cddaSDavide Caratti skb->pkt_type = params->ptype; 86c749cddaSDavide Caratti return action; 877fd4b288SPaolo Abeni 88e7e3728bSQiaobin Fu err: 896f3dfb0dSDavide Caratti qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats)); 907fd4b288SPaolo Abeni return TC_ACT_SHOT; 91ca9b0e27SAlexander Duyck } 92ca9b0e27SAlexander Duyck 93837cb17dSPetr Machata static void tcf_skbedit_stats_update(struct tc_action *a, u64 bytes, 944b61d3e8SPo Liu u64 packets, u64 drops, 954b61d3e8SPo Liu u64 lastuse, bool hw) 96837cb17dSPetr Machata { 97837cb17dSPetr Machata struct tcf_skbedit *d = to_skbedit(a); 98837cb17dSPetr Machata struct tcf_t *tm = &d->tcf_tm; 99837cb17dSPetr Machata 1004b61d3e8SPo Liu tcf_action_update_stats(a, bytes, packets, drops, hw); 101837cb17dSPetr Machata tm->lastuse = max_t(u64, tm->lastuse, lastuse); 102837cb17dSPetr Machata } 103837cb17dSPetr Machata 104ca9b0e27SAlexander Duyck static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { 105ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, 106ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, 107ca9b0e27SAlexander Duyck [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, 1081c55d62eSjamal [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) }, 109ff202ee1SJamal Hadi Salim [TCA_SKBEDIT_PTYPE] = { .len = sizeof(u16) }, 1104fe77d82SAntonio Quartulli [TCA_SKBEDIT_MASK] = { .len = sizeof(u32) }, 111e7e3728bSQiaobin Fu [TCA_SKBEDIT_FLAGS] = { .len = sizeof(u64) }, 11238a6f086STonghao Zhang [TCA_SKBEDIT_QUEUE_MAPPING_MAX] = { .len = sizeof(u16) }, 113ca9b0e27SAlexander Duyck }; 114ca9b0e27SAlexander Duyck 115c1b52739SBenjamin LaHaise static int tcf_skbedit_init(struct net *net, struct nlattr *nla, 116a85a970aSWANG Cong struct nlattr *est, struct tc_action **a, 117abbb0d33SVlad Buslov struct tcf_proto *tp, u32 act_flags, 118789871bbSVlad Buslov struct netlink_ext_ack *extack) 119ca9b0e27SAlexander Duyck { 120acd0a7abSZhengchao Shao struct tc_action_net *tn = net_generic(net, act_skbedit_ops.net_id); 121695176bfSCong Wang bool bind = act_flags & TCA_ACT_FLAGS_BIND; 1226d7a8df6SVlad Buslov struct tcf_skbedit_params *params_new; 123ca9b0e27SAlexander Duyck struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; 124ec7727bbSDavide Caratti struct tcf_chain *goto_ch = NULL; 125ca9b0e27SAlexander Duyck struct tc_skbedit *parm; 126ca9b0e27SAlexander Duyck struct tcf_skbedit *d; 1274fe77d82SAntonio Quartulli u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL; 128ff202ee1SJamal Hadi Salim u16 *queue_mapping = NULL, *ptype = NULL; 12938a6f086STonghao Zhang u16 mapping_mod = 1; 130b2313077SWANG Cong bool exists = false; 131b2313077SWANG Cong int ret = 0, err; 1327be8ef2cSDmytro Linkin u32 index; 133ca9b0e27SAlexander Duyck 134ca9b0e27SAlexander Duyck if (nla == NULL) 135ca9b0e27SAlexander Duyck return -EINVAL; 136ca9b0e27SAlexander Duyck 1378cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_SKBEDIT_MAX, nla, 1388cb08174SJohannes Berg skbedit_policy, NULL); 139ca9b0e27SAlexander Duyck if (err < 0) 140ca9b0e27SAlexander Duyck return err; 141ca9b0e27SAlexander Duyck 142ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PARMS] == NULL) 143ca9b0e27SAlexander Duyck return -EINVAL; 144ca9b0e27SAlexander Duyck 145ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { 146ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_PRIORITY; 147ca9b0e27SAlexander Duyck priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); 148ca9b0e27SAlexander Duyck } 149ca9b0e27SAlexander Duyck 150ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { 151*4a6a676fSAmritha Nambiar if (is_tcf_skbedit_ingress(act_flags) && 152*4a6a676fSAmritha Nambiar !(act_flags & TCA_ACT_FLAGS_SKIP_SW)) { 153*4a6a676fSAmritha Nambiar NL_SET_ERR_MSG_MOD(extack, "\"queue_mapping\" option on receive side is hardware only, use skip_sw"); 154*4a6a676fSAmritha Nambiar return -EOPNOTSUPP; 155*4a6a676fSAmritha Nambiar } 156ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_QUEUE_MAPPING; 157ca9b0e27SAlexander Duyck queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); 158ca9b0e27SAlexander Duyck } 1591c55d62eSjamal 160ff202ee1SJamal Hadi Salim if (tb[TCA_SKBEDIT_PTYPE] != NULL) { 161ff202ee1SJamal Hadi Salim ptype = nla_data(tb[TCA_SKBEDIT_PTYPE]); 162ff202ee1SJamal Hadi Salim if (!skb_pkt_type_ok(*ptype)) 163ff202ee1SJamal Hadi Salim return -EINVAL; 164ff202ee1SJamal Hadi Salim flags |= SKBEDIT_F_PTYPE; 165ff202ee1SJamal Hadi Salim } 166ff202ee1SJamal Hadi Salim 1671c55d62eSjamal if (tb[TCA_SKBEDIT_MARK] != NULL) { 1681c55d62eSjamal flags |= SKBEDIT_F_MARK; 1691c55d62eSjamal mark = nla_data(tb[TCA_SKBEDIT_MARK]); 1701c55d62eSjamal } 1711c55d62eSjamal 1724fe77d82SAntonio Quartulli if (tb[TCA_SKBEDIT_MASK] != NULL) { 1734fe77d82SAntonio Quartulli flags |= SKBEDIT_F_MASK; 1744fe77d82SAntonio Quartulli mask = nla_data(tb[TCA_SKBEDIT_MASK]); 1754fe77d82SAntonio Quartulli } 1764fe77d82SAntonio Quartulli 177e7e3728bSQiaobin Fu if (tb[TCA_SKBEDIT_FLAGS] != NULL) { 178e7e3728bSQiaobin Fu u64 *pure_flags = nla_data(tb[TCA_SKBEDIT_FLAGS]); 179e7e3728bSQiaobin Fu 18038a6f086STonghao Zhang if (*pure_flags & SKBEDIT_F_TXQ_SKBHASH) { 18138a6f086STonghao Zhang u16 *queue_mapping_max; 18238a6f086STonghao Zhang 18338a6f086STonghao Zhang if (!tb[TCA_SKBEDIT_QUEUE_MAPPING] || 18438a6f086STonghao Zhang !tb[TCA_SKBEDIT_QUEUE_MAPPING_MAX]) { 18538a6f086STonghao Zhang NL_SET_ERR_MSG_MOD(extack, "Missing required range of queue_mapping."); 18638a6f086STonghao Zhang return -EINVAL; 18738a6f086STonghao Zhang } 18838a6f086STonghao Zhang 18938a6f086STonghao Zhang queue_mapping_max = 19038a6f086STonghao Zhang nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING_MAX]); 19138a6f086STonghao Zhang if (*queue_mapping_max < *queue_mapping) { 19238a6f086STonghao Zhang NL_SET_ERR_MSG_MOD(extack, "The range of queue_mapping is invalid, max < min."); 19338a6f086STonghao Zhang return -EINVAL; 19438a6f086STonghao Zhang } 19538a6f086STonghao Zhang 19638a6f086STonghao Zhang mapping_mod = *queue_mapping_max - *queue_mapping + 1; 19738a6f086STonghao Zhang flags |= SKBEDIT_F_TXQ_SKBHASH; 19838a6f086STonghao Zhang } 199e7e3728bSQiaobin Fu if (*pure_flags & SKBEDIT_F_INHERITDSFIELD) 200e7e3728bSQiaobin Fu flags |= SKBEDIT_F_INHERITDSFIELD; 201e7e3728bSQiaobin Fu } 202e7e3728bSQiaobin Fu 203ca9b0e27SAlexander Duyck parm = nla_data(tb[TCA_SKBEDIT_PARMS]); 2047be8ef2cSDmytro Linkin index = parm->index; 2057be8ef2cSDmytro Linkin err = tcf_idr_check_alloc(tn, &index, a, bind); 2060190c1d4SVlad Buslov if (err < 0) 2070190c1d4SVlad Buslov return err; 2080190c1d4SVlad Buslov exists = err; 2095e1567aeSJamal Hadi Salim if (exists && bind) 2105e1567aeSJamal Hadi Salim return 0; 2115e1567aeSJamal Hadi Salim 2125e1567aeSJamal Hadi Salim if (!flags) { 213af5d0184SRoman Mashak if (exists) 21465a206c0SChris Mi tcf_idr_release(*a, bind); 2150190c1d4SVlad Buslov else 2167be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index); 2175e1567aeSJamal Hadi Salim return -EINVAL; 2185e1567aeSJamal Hadi Salim } 2195e1567aeSJamal Hadi Salim 2205e1567aeSJamal Hadi Salim if (!exists) { 2217be8ef2cSDmytro Linkin ret = tcf_idr_create(tn, index, est, a, 22240bd094dSBaowen Zheng &act_skbedit_ops, bind, true, act_flags); 2230190c1d4SVlad Buslov if (ret) { 2247be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index); 22586062033SWANG Cong return ret; 2260190c1d4SVlad Buslov } 227ca9b0e27SAlexander Duyck 228a85a970aSWANG Cong d = to_skbedit(*a); 229ca9b0e27SAlexander Duyck ret = ACT_P_CREATED; 230ca9b0e27SAlexander Duyck } else { 231a85a970aSWANG Cong d = to_skbedit(*a); 232695176bfSCong Wang if (!(act_flags & TCA_ACT_FLAGS_REPLACE)) { 23365a206c0SChris Mi tcf_idr_release(*a, bind); 234ca9b0e27SAlexander Duyck return -EEXIST; 235ca9b0e27SAlexander Duyck } 2364e8ddd7fSVlad Buslov } 237ec7727bbSDavide Caratti err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 238ec7727bbSDavide Caratti if (err < 0) 239ec7727bbSDavide Caratti goto release_idr; 240ca9b0e27SAlexander Duyck 241c749cddaSDavide Caratti params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); 242c749cddaSDavide Caratti if (unlikely(!params_new)) { 243ec7727bbSDavide Caratti err = -ENOMEM; 244ec7727bbSDavide Caratti goto put_chain; 245c749cddaSDavide Caratti } 246c749cddaSDavide Caratti 247c749cddaSDavide Caratti params_new->flags = flags; 248ca9b0e27SAlexander Duyck if (flags & SKBEDIT_F_PRIORITY) 249c749cddaSDavide Caratti params_new->priority = *priority; 25038a6f086STonghao Zhang if (flags & SKBEDIT_F_QUEUE_MAPPING) { 251c749cddaSDavide Caratti params_new->queue_mapping = *queue_mapping; 25238a6f086STonghao Zhang params_new->mapping_mod = mapping_mod; 25338a6f086STonghao Zhang } 2541c55d62eSjamal if (flags & SKBEDIT_F_MARK) 255c749cddaSDavide Caratti params_new->mark = *mark; 256ff202ee1SJamal Hadi Salim if (flags & SKBEDIT_F_PTYPE) 257c749cddaSDavide Caratti params_new->ptype = *ptype; 2584fe77d82SAntonio Quartulli /* default behaviour is to use all the bits */ 259c749cddaSDavide Caratti params_new->mask = 0xffffffff; 2604fe77d82SAntonio Quartulli if (flags & SKBEDIT_F_MASK) 261c749cddaSDavide Caratti params_new->mask = *mask; 2621c55d62eSjamal 2636d7a8df6SVlad Buslov spin_lock_bh(&d->tcf_lock); 264ec7727bbSDavide Caratti goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); 265445d3749SPaul E. McKenney params_new = rcu_replace_pointer(d->params, params_new, 2666d7a8df6SVlad Buslov lockdep_is_held(&d->tcf_lock)); 2676d7a8df6SVlad Buslov spin_unlock_bh(&d->tcf_lock); 2686d7a8df6SVlad Buslov if (params_new) 2696d7a8df6SVlad Buslov kfree_rcu(params_new, rcu); 270ec7727bbSDavide Caratti if (goto_ch) 271ec7727bbSDavide Caratti tcf_chain_put_by_act(goto_ch); 272ca9b0e27SAlexander Duyck 273ca9b0e27SAlexander Duyck return ret; 274ec7727bbSDavide Caratti put_chain: 275ec7727bbSDavide Caratti if (goto_ch) 276ec7727bbSDavide Caratti tcf_chain_put_by_act(goto_ch); 277ec7727bbSDavide Caratti release_idr: 278ec7727bbSDavide Caratti tcf_idr_release(*a, bind); 279ec7727bbSDavide Caratti return err; 280ca9b0e27SAlexander Duyck } 281ca9b0e27SAlexander Duyck 282cc7ec456SEric Dumazet static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, 283ca9b0e27SAlexander Duyck int bind, int ref) 284ca9b0e27SAlexander Duyck { 285ca9b0e27SAlexander Duyck unsigned char *b = skb_tail_pointer(skb); 286a85a970aSWANG Cong struct tcf_skbedit *d = to_skbedit(a); 287c749cddaSDavide Caratti struct tcf_skbedit_params *params; 2881c40be12SEric Dumazet struct tc_skbedit opt = { 2891c40be12SEric Dumazet .index = d->tcf_index, 290036bb443SVlad Buslov .refcnt = refcount_read(&d->tcf_refcnt) - ref, 291036bb443SVlad Buslov .bindcnt = atomic_read(&d->tcf_bindcnt) - bind, 2921c40be12SEric Dumazet }; 293e7e3728bSQiaobin Fu u64 pure_flags = 0; 294c749cddaSDavide Caratti struct tcf_t t; 295c749cddaSDavide Caratti 2966d7a8df6SVlad Buslov spin_lock_bh(&d->tcf_lock); 2976d7a8df6SVlad Buslov params = rcu_dereference_protected(d->params, 2986d7a8df6SVlad Buslov lockdep_is_held(&d->tcf_lock)); 2996d7a8df6SVlad Buslov opt.action = d->tcf_action; 300ca9b0e27SAlexander Duyck 3011b34ec43SDavid S. Miller if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt)) 3021b34ec43SDavid S. Miller goto nla_put_failure; 303c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_PRIORITY) && 304c749cddaSDavide Caratti nla_put_u32(skb, TCA_SKBEDIT_PRIORITY, params->priority)) 3051b34ec43SDavid S. Miller goto nla_put_failure; 306c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_QUEUE_MAPPING) && 307c749cddaSDavide Caratti nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING, params->queue_mapping)) 3081b34ec43SDavid S. Miller goto nla_put_failure; 309c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_MARK) && 310c749cddaSDavide Caratti nla_put_u32(skb, TCA_SKBEDIT_MARK, params->mark)) 3111b34ec43SDavid S. Miller goto nla_put_failure; 312c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_PTYPE) && 313c749cddaSDavide Caratti nla_put_u16(skb, TCA_SKBEDIT_PTYPE, params->ptype)) 314ff202ee1SJamal Hadi Salim goto nla_put_failure; 315c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_MASK) && 316c749cddaSDavide Caratti nla_put_u32(skb, TCA_SKBEDIT_MASK, params->mask)) 3174fe77d82SAntonio Quartulli goto nla_put_failure; 318c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_INHERITDSFIELD) 319e7e3728bSQiaobin Fu pure_flags |= SKBEDIT_F_INHERITDSFIELD; 32038a6f086STonghao Zhang if (params->flags & SKBEDIT_F_TXQ_SKBHASH) { 32138a6f086STonghao Zhang if (nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING_MAX, 32238a6f086STonghao Zhang params->queue_mapping + params->mapping_mod - 1)) 32338a6f086STonghao Zhang goto nla_put_failure; 32438a6f086STonghao Zhang 32538a6f086STonghao Zhang pure_flags |= SKBEDIT_F_TXQ_SKBHASH; 32638a6f086STonghao Zhang } 327e7e3728bSQiaobin Fu if (pure_flags != 0 && 328e7e3728bSQiaobin Fu nla_put(skb, TCA_SKBEDIT_FLAGS, sizeof(pure_flags), &pure_flags)) 329e7e3728bSQiaobin Fu goto nla_put_failure; 33048d8ee16SJamal Hadi Salim 33148d8ee16SJamal Hadi Salim tcf_tm_dump(&t, &d->tcf_tm); 3329854518eSNicolas Dichtel if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD)) 3331b34ec43SDavid S. Miller goto nla_put_failure; 3346d7a8df6SVlad Buslov spin_unlock_bh(&d->tcf_lock); 3356d7a8df6SVlad Buslov 336ca9b0e27SAlexander Duyck return skb->len; 337ca9b0e27SAlexander Duyck 338ca9b0e27SAlexander Duyck nla_put_failure: 3396d7a8df6SVlad Buslov spin_unlock_bh(&d->tcf_lock); 340ca9b0e27SAlexander Duyck nlmsg_trim(skb, b); 341ca9b0e27SAlexander Duyck return -1; 342ca9b0e27SAlexander Duyck } 343ca9b0e27SAlexander Duyck 344c749cddaSDavide Caratti static void tcf_skbedit_cleanup(struct tc_action *a) 345c749cddaSDavide Caratti { 346c749cddaSDavide Caratti struct tcf_skbedit *d = to_skbedit(a); 347c749cddaSDavide Caratti struct tcf_skbedit_params *params; 348c749cddaSDavide Caratti 349c749cddaSDavide Caratti params = rcu_dereference_protected(d->params, 1); 350c749cddaSDavide Caratti if (params) 351c749cddaSDavide Caratti kfree_rcu(params, rcu); 352c749cddaSDavide Caratti } 353c749cddaSDavide Caratti 354e1fea322SRoman Mashak static size_t tcf_skbedit_get_fill_size(const struct tc_action *act) 355e1fea322SRoman Mashak { 356e1fea322SRoman Mashak return nla_total_size(sizeof(struct tc_skbedit)) 357e1fea322SRoman Mashak + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_PRIORITY */ 358e1fea322SRoman Mashak + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_QUEUE_MAPPING */ 35938a6f086STonghao Zhang + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_QUEUE_MAPPING_MAX */ 360e1fea322SRoman Mashak + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MARK */ 361e1fea322SRoman Mashak + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_PTYPE */ 362e1fea322SRoman Mashak + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MASK */ 363e1fea322SRoman Mashak + nla_total_size_64bit(sizeof(u64)); /* TCA_SKBEDIT_FLAGS */ 364e1fea322SRoman Mashak } 365e1fea322SRoman Mashak 366c54e1d92SBaowen Zheng static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data, 367c2ccf84eSIdo Schimmel u32 *index_inc, bool bind, 368c2ccf84eSIdo Schimmel struct netlink_ext_ack *extack) 369c54e1d92SBaowen Zheng { 370c54e1d92SBaowen Zheng if (bind) { 371c54e1d92SBaowen Zheng struct flow_action_entry *entry = entry_data; 372c54e1d92SBaowen Zheng 373c54e1d92SBaowen Zheng if (is_tcf_skbedit_mark(act)) { 374c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_MARK; 375c54e1d92SBaowen Zheng entry->mark = tcf_skbedit_mark(act); 376c54e1d92SBaowen Zheng } else if (is_tcf_skbedit_ptype(act)) { 377c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_PTYPE; 378c54e1d92SBaowen Zheng entry->ptype = tcf_skbedit_ptype(act); 379c54e1d92SBaowen Zheng } else if (is_tcf_skbedit_priority(act)) { 380c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_PRIORITY; 381c54e1d92SBaowen Zheng entry->priority = tcf_skbedit_priority(act); 382*4a6a676fSAmritha Nambiar } else if (is_tcf_skbedit_tx_queue_mapping(act)) { 383*4a6a676fSAmritha Nambiar NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"queue_mapping\" option is used on transmit side"); 384a9c64939SIdo Schimmel return -EOPNOTSUPP; 385*4a6a676fSAmritha Nambiar } else if (is_tcf_skbedit_rx_queue_mapping(act)) { 386*4a6a676fSAmritha Nambiar entry->id = FLOW_ACTION_RX_QUEUE_MAPPING; 387*4a6a676fSAmritha Nambiar entry->rx_queue = tcf_skbedit_rx_queue_mapping(act); 388a9c64939SIdo Schimmel } else if (is_tcf_skbedit_inheritdsfield(act)) { 389a9c64939SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"inheritdsfield\" option is used"); 390a9c64939SIdo Schimmel return -EOPNOTSUPP; 391c54e1d92SBaowen Zheng } else { 392a9c64939SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Unsupported skbedit option offload"); 393c54e1d92SBaowen Zheng return -EOPNOTSUPP; 394c54e1d92SBaowen Zheng } 395c54e1d92SBaowen Zheng *index_inc = 1; 396c54e1d92SBaowen Zheng } else { 3978cbfe939SBaowen Zheng struct flow_offload_action *fl_action = entry_data; 3988cbfe939SBaowen Zheng 3998cbfe939SBaowen Zheng if (is_tcf_skbedit_mark(act)) 4008cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_MARK; 4018cbfe939SBaowen Zheng else if (is_tcf_skbedit_ptype(act)) 4028cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_PTYPE; 4038cbfe939SBaowen Zheng else if (is_tcf_skbedit_priority(act)) 4048cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_PRIORITY; 405*4a6a676fSAmritha Nambiar else if (is_tcf_skbedit_rx_queue_mapping(act)) 406*4a6a676fSAmritha Nambiar fl_action->id = FLOW_ACTION_RX_QUEUE_MAPPING; 4078cbfe939SBaowen Zheng else 408c54e1d92SBaowen Zheng return -EOPNOTSUPP; 409c54e1d92SBaowen Zheng } 410c54e1d92SBaowen Zheng 411c54e1d92SBaowen Zheng return 0; 412c54e1d92SBaowen Zheng } 413c54e1d92SBaowen Zheng 414ca9b0e27SAlexander Duyck static struct tc_action_ops act_skbedit_ops = { 415ca9b0e27SAlexander Duyck .kind = "skbedit", 416eddd2cf1SEli Cohen .id = TCA_ID_SKBEDIT, 417ca9b0e27SAlexander Duyck .owner = THIS_MODULE, 41845da1dacSJamal Hadi Salim .act = tcf_skbedit_act, 419837cb17dSPetr Machata .stats_update = tcf_skbedit_stats_update, 420ca9b0e27SAlexander Duyck .dump = tcf_skbedit_dump, 421ca9b0e27SAlexander Duyck .init = tcf_skbedit_init, 422c749cddaSDavide Caratti .cleanup = tcf_skbedit_cleanup, 423e1fea322SRoman Mashak .get_fill_size = tcf_skbedit_get_fill_size, 424c54e1d92SBaowen Zheng .offload_act_setup = tcf_skbedit_offload_act_setup, 425a85a970aSWANG Cong .size = sizeof(struct tcf_skbedit), 426ddf97ccdSWANG Cong }; 427ddf97ccdSWANG Cong 428ddf97ccdSWANG Cong static __net_init int skbedit_init_net(struct net *net) 429ddf97ccdSWANG Cong { 430acd0a7abSZhengchao Shao struct tc_action_net *tn = net_generic(net, act_skbedit_ops.net_id); 431ddf97ccdSWANG Cong 432981471bdSCong Wang return tc_action_net_init(net, tn, &act_skbedit_ops); 433ddf97ccdSWANG Cong } 434ddf97ccdSWANG Cong 435039af9c6SCong Wang static void __net_exit skbedit_exit_net(struct list_head *net_list) 436ddf97ccdSWANG Cong { 437acd0a7abSZhengchao Shao tc_action_net_exit(net_list, act_skbedit_ops.net_id); 438ddf97ccdSWANG Cong } 439ddf97ccdSWANG Cong 440ddf97ccdSWANG Cong static struct pernet_operations skbedit_net_ops = { 441ddf97ccdSWANG Cong .init = skbedit_init_net, 442039af9c6SCong Wang .exit_batch = skbedit_exit_net, 443acd0a7abSZhengchao Shao .id = &act_skbedit_ops.net_id, 444ddf97ccdSWANG Cong .size = sizeof(struct tc_action_net), 445ca9b0e27SAlexander Duyck }; 446ca9b0e27SAlexander Duyck 447ca9b0e27SAlexander Duyck MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); 448ca9b0e27SAlexander Duyck MODULE_DESCRIPTION("SKB Editing"); 449ca9b0e27SAlexander Duyck MODULE_LICENSE("GPL"); 450ca9b0e27SAlexander Duyck 451ca9b0e27SAlexander Duyck static int __init skbedit_init_module(void) 452ca9b0e27SAlexander Duyck { 453ddf97ccdSWANG Cong return tcf_register_action(&act_skbedit_ops, &skbedit_net_ops); 454ca9b0e27SAlexander Duyck } 455ca9b0e27SAlexander Duyck 456ca9b0e27SAlexander Duyck static void __exit skbedit_cleanup_module(void) 457ca9b0e27SAlexander Duyck { 458ddf97ccdSWANG Cong tcf_unregister_action(&act_skbedit_ops, &skbedit_net_ops); 459ca9b0e27SAlexander Duyck } 460ca9b0e27SAlexander Duyck 461ca9b0e27SAlexander Duyck module_init(skbedit_init_module); 462ca9b0e27SAlexander Duyck module_exit(skbedit_cleanup_module); 463