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 23c7d03a00SAlexey Dobriyan static unsigned int skbedit_net_id; 24a85a970aSWANG Cong static struct tc_action_ops act_skbedit_ops; 25ddf97ccdSWANG Cong 2645da1dacSJamal Hadi Salim static int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a, 27ca9b0e27SAlexander Duyck struct tcf_result *res) 28ca9b0e27SAlexander Duyck { 29a85a970aSWANG Cong struct tcf_skbedit *d = to_skbedit(a); 30c749cddaSDavide Caratti struct tcf_skbedit_params *params; 31c749cddaSDavide Caratti int action; 32ca9b0e27SAlexander Duyck 339c4a4e48SJamal Hadi Salim tcf_lastuse_update(&d->tcf_tm); 3450dc9a85SAhmed S. Darwish bstats_update(this_cpu_ptr(d->common.cpu_bstats), skb); 35ca9b0e27SAlexander Duyck 367fd4b288SPaolo Abeni params = rcu_dereference_bh(d->params); 37c749cddaSDavide Caratti action = READ_ONCE(d->tcf_action); 38c749cddaSDavide Caratti 39c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_PRIORITY) 40c749cddaSDavide Caratti skb->priority = params->priority; 41c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_INHERITDSFIELD) { 42e7e3728bSQiaobin Fu int wlen = skb_network_offset(skb); 43e7e3728bSQiaobin Fu 44d7bf2ebeSToke Høiland-Jørgensen switch (skb_protocol(skb, true)) { 45e7e3728bSQiaobin Fu case htons(ETH_P_IP): 46e7e3728bSQiaobin Fu wlen += sizeof(struct iphdr); 47e7e3728bSQiaobin Fu if (!pskb_may_pull(skb, wlen)) 48e7e3728bSQiaobin Fu goto err; 49e7e3728bSQiaobin Fu skb->priority = ipv4_get_dsfield(ip_hdr(skb)) >> 2; 50e7e3728bSQiaobin Fu break; 51e7e3728bSQiaobin Fu 52e7e3728bSQiaobin Fu case htons(ETH_P_IPV6): 53e7e3728bSQiaobin Fu wlen += sizeof(struct ipv6hdr); 54e7e3728bSQiaobin Fu if (!pskb_may_pull(skb, wlen)) 55e7e3728bSQiaobin Fu goto err; 56e7e3728bSQiaobin Fu skb->priority = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; 57e7e3728bSQiaobin Fu break; 58e7e3728bSQiaobin Fu } 59e7e3728bSQiaobin Fu } 60c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_QUEUE_MAPPING && 61*2f1e85b1STonghao Zhang skb->dev->real_num_tx_queues > params->queue_mapping) { 62*2f1e85b1STonghao Zhang #ifdef CONFIG_NET_EGRESS 63*2f1e85b1STonghao Zhang netdev_xmit_skip_txqueue(true); 64*2f1e85b1STonghao Zhang #endif 65c749cddaSDavide Caratti skb_set_queue_mapping(skb, params->queue_mapping); 66*2f1e85b1STonghao Zhang } 67c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_MARK) { 68c749cddaSDavide Caratti skb->mark &= ~params->mask; 69c749cddaSDavide Caratti skb->mark |= params->mark & params->mask; 704fe77d82SAntonio Quartulli } 71c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_PTYPE) 72c749cddaSDavide Caratti skb->pkt_type = params->ptype; 73c749cddaSDavide Caratti return action; 747fd4b288SPaolo Abeni 75e7e3728bSQiaobin Fu err: 766f3dfb0dSDavide Caratti qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats)); 777fd4b288SPaolo Abeni return TC_ACT_SHOT; 78ca9b0e27SAlexander Duyck } 79ca9b0e27SAlexander Duyck 80837cb17dSPetr Machata static void tcf_skbedit_stats_update(struct tc_action *a, u64 bytes, 814b61d3e8SPo Liu u64 packets, u64 drops, 824b61d3e8SPo Liu u64 lastuse, bool hw) 83837cb17dSPetr Machata { 84837cb17dSPetr Machata struct tcf_skbedit *d = to_skbedit(a); 85837cb17dSPetr Machata struct tcf_t *tm = &d->tcf_tm; 86837cb17dSPetr Machata 874b61d3e8SPo Liu tcf_action_update_stats(a, bytes, packets, drops, hw); 88837cb17dSPetr Machata tm->lastuse = max_t(u64, tm->lastuse, lastuse); 89837cb17dSPetr Machata } 90837cb17dSPetr Machata 91ca9b0e27SAlexander Duyck static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { 92ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, 93ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, 94ca9b0e27SAlexander Duyck [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, 951c55d62eSjamal [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) }, 96ff202ee1SJamal Hadi Salim [TCA_SKBEDIT_PTYPE] = { .len = sizeof(u16) }, 974fe77d82SAntonio Quartulli [TCA_SKBEDIT_MASK] = { .len = sizeof(u32) }, 98e7e3728bSQiaobin Fu [TCA_SKBEDIT_FLAGS] = { .len = sizeof(u64) }, 99ca9b0e27SAlexander Duyck }; 100ca9b0e27SAlexander Duyck 101c1b52739SBenjamin LaHaise static int tcf_skbedit_init(struct net *net, struct nlattr *nla, 102a85a970aSWANG Cong struct nlattr *est, struct tc_action **a, 103abbb0d33SVlad Buslov struct tcf_proto *tp, u32 act_flags, 104789871bbSVlad Buslov struct netlink_ext_ack *extack) 105ca9b0e27SAlexander Duyck { 106ddf97ccdSWANG Cong struct tc_action_net *tn = net_generic(net, skbedit_net_id); 107695176bfSCong Wang bool bind = act_flags & TCA_ACT_FLAGS_BIND; 1086d7a8df6SVlad Buslov struct tcf_skbedit_params *params_new; 109ca9b0e27SAlexander Duyck struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; 110ec7727bbSDavide Caratti struct tcf_chain *goto_ch = NULL; 111ca9b0e27SAlexander Duyck struct tc_skbedit *parm; 112ca9b0e27SAlexander Duyck struct tcf_skbedit *d; 1134fe77d82SAntonio Quartulli u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL; 114ff202ee1SJamal Hadi Salim u16 *queue_mapping = NULL, *ptype = NULL; 115b2313077SWANG Cong bool exists = false; 116b2313077SWANG Cong int ret = 0, err; 1177be8ef2cSDmytro Linkin u32 index; 118ca9b0e27SAlexander Duyck 119ca9b0e27SAlexander Duyck if (nla == NULL) 120ca9b0e27SAlexander Duyck return -EINVAL; 121ca9b0e27SAlexander Duyck 1228cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_SKBEDIT_MAX, nla, 1238cb08174SJohannes Berg skbedit_policy, NULL); 124ca9b0e27SAlexander Duyck if (err < 0) 125ca9b0e27SAlexander Duyck return err; 126ca9b0e27SAlexander Duyck 127ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PARMS] == NULL) 128ca9b0e27SAlexander Duyck return -EINVAL; 129ca9b0e27SAlexander Duyck 130ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { 131ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_PRIORITY; 132ca9b0e27SAlexander Duyck priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); 133ca9b0e27SAlexander Duyck } 134ca9b0e27SAlexander Duyck 135ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { 136ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_QUEUE_MAPPING; 137ca9b0e27SAlexander Duyck queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); 138ca9b0e27SAlexander Duyck } 1391c55d62eSjamal 140ff202ee1SJamal Hadi Salim if (tb[TCA_SKBEDIT_PTYPE] != NULL) { 141ff202ee1SJamal Hadi Salim ptype = nla_data(tb[TCA_SKBEDIT_PTYPE]); 142ff202ee1SJamal Hadi Salim if (!skb_pkt_type_ok(*ptype)) 143ff202ee1SJamal Hadi Salim return -EINVAL; 144ff202ee1SJamal Hadi Salim flags |= SKBEDIT_F_PTYPE; 145ff202ee1SJamal Hadi Salim } 146ff202ee1SJamal Hadi Salim 1471c55d62eSjamal if (tb[TCA_SKBEDIT_MARK] != NULL) { 1481c55d62eSjamal flags |= SKBEDIT_F_MARK; 1491c55d62eSjamal mark = nla_data(tb[TCA_SKBEDIT_MARK]); 1501c55d62eSjamal } 1511c55d62eSjamal 1524fe77d82SAntonio Quartulli if (tb[TCA_SKBEDIT_MASK] != NULL) { 1534fe77d82SAntonio Quartulli flags |= SKBEDIT_F_MASK; 1544fe77d82SAntonio Quartulli mask = nla_data(tb[TCA_SKBEDIT_MASK]); 1554fe77d82SAntonio Quartulli } 1564fe77d82SAntonio Quartulli 157e7e3728bSQiaobin Fu if (tb[TCA_SKBEDIT_FLAGS] != NULL) { 158e7e3728bSQiaobin Fu u64 *pure_flags = nla_data(tb[TCA_SKBEDIT_FLAGS]); 159e7e3728bSQiaobin Fu 160e7e3728bSQiaobin Fu if (*pure_flags & SKBEDIT_F_INHERITDSFIELD) 161e7e3728bSQiaobin Fu flags |= SKBEDIT_F_INHERITDSFIELD; 162e7e3728bSQiaobin Fu } 163e7e3728bSQiaobin Fu 164ca9b0e27SAlexander Duyck parm = nla_data(tb[TCA_SKBEDIT_PARMS]); 1657be8ef2cSDmytro Linkin index = parm->index; 1667be8ef2cSDmytro Linkin err = tcf_idr_check_alloc(tn, &index, a, bind); 1670190c1d4SVlad Buslov if (err < 0) 1680190c1d4SVlad Buslov return err; 1690190c1d4SVlad Buslov exists = err; 1705e1567aeSJamal Hadi Salim if (exists && bind) 1715e1567aeSJamal Hadi Salim return 0; 1725e1567aeSJamal Hadi Salim 1735e1567aeSJamal Hadi Salim if (!flags) { 174af5d0184SRoman Mashak if (exists) 17565a206c0SChris Mi tcf_idr_release(*a, bind); 1760190c1d4SVlad Buslov else 1777be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index); 1785e1567aeSJamal Hadi Salim return -EINVAL; 1795e1567aeSJamal Hadi Salim } 1805e1567aeSJamal Hadi Salim 1815e1567aeSJamal Hadi Salim if (!exists) { 1827be8ef2cSDmytro Linkin ret = tcf_idr_create(tn, index, est, a, 18340bd094dSBaowen Zheng &act_skbedit_ops, bind, true, act_flags); 1840190c1d4SVlad Buslov if (ret) { 1857be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index); 18686062033SWANG Cong return ret; 1870190c1d4SVlad Buslov } 188ca9b0e27SAlexander Duyck 189a85a970aSWANG Cong d = to_skbedit(*a); 190ca9b0e27SAlexander Duyck ret = ACT_P_CREATED; 191ca9b0e27SAlexander Duyck } else { 192a85a970aSWANG Cong d = to_skbedit(*a); 193695176bfSCong Wang if (!(act_flags & TCA_ACT_FLAGS_REPLACE)) { 19465a206c0SChris Mi tcf_idr_release(*a, bind); 195ca9b0e27SAlexander Duyck return -EEXIST; 196ca9b0e27SAlexander Duyck } 1974e8ddd7fSVlad Buslov } 198ec7727bbSDavide Caratti err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 199ec7727bbSDavide Caratti if (err < 0) 200ec7727bbSDavide Caratti goto release_idr; 201ca9b0e27SAlexander Duyck 202c749cddaSDavide Caratti params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); 203c749cddaSDavide Caratti if (unlikely(!params_new)) { 204ec7727bbSDavide Caratti err = -ENOMEM; 205ec7727bbSDavide Caratti goto put_chain; 206c749cddaSDavide Caratti } 207c749cddaSDavide Caratti 208c749cddaSDavide Caratti params_new->flags = flags; 209ca9b0e27SAlexander Duyck if (flags & SKBEDIT_F_PRIORITY) 210c749cddaSDavide Caratti params_new->priority = *priority; 211ca9b0e27SAlexander Duyck if (flags & SKBEDIT_F_QUEUE_MAPPING) 212c749cddaSDavide Caratti params_new->queue_mapping = *queue_mapping; 2131c55d62eSjamal if (flags & SKBEDIT_F_MARK) 214c749cddaSDavide Caratti params_new->mark = *mark; 215ff202ee1SJamal Hadi Salim if (flags & SKBEDIT_F_PTYPE) 216c749cddaSDavide Caratti params_new->ptype = *ptype; 2174fe77d82SAntonio Quartulli /* default behaviour is to use all the bits */ 218c749cddaSDavide Caratti params_new->mask = 0xffffffff; 2194fe77d82SAntonio Quartulli if (flags & SKBEDIT_F_MASK) 220c749cddaSDavide Caratti params_new->mask = *mask; 2211c55d62eSjamal 2226d7a8df6SVlad Buslov spin_lock_bh(&d->tcf_lock); 223ec7727bbSDavide Caratti goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); 224445d3749SPaul E. McKenney params_new = rcu_replace_pointer(d->params, params_new, 2256d7a8df6SVlad Buslov lockdep_is_held(&d->tcf_lock)); 2266d7a8df6SVlad Buslov spin_unlock_bh(&d->tcf_lock); 2276d7a8df6SVlad Buslov if (params_new) 2286d7a8df6SVlad Buslov kfree_rcu(params_new, rcu); 229ec7727bbSDavide Caratti if (goto_ch) 230ec7727bbSDavide Caratti tcf_chain_put_by_act(goto_ch); 231ca9b0e27SAlexander Duyck 232ca9b0e27SAlexander Duyck return ret; 233ec7727bbSDavide Caratti put_chain: 234ec7727bbSDavide Caratti if (goto_ch) 235ec7727bbSDavide Caratti tcf_chain_put_by_act(goto_ch); 236ec7727bbSDavide Caratti release_idr: 237ec7727bbSDavide Caratti tcf_idr_release(*a, bind); 238ec7727bbSDavide Caratti return err; 239ca9b0e27SAlexander Duyck } 240ca9b0e27SAlexander Duyck 241cc7ec456SEric Dumazet static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, 242ca9b0e27SAlexander Duyck int bind, int ref) 243ca9b0e27SAlexander Duyck { 244ca9b0e27SAlexander Duyck unsigned char *b = skb_tail_pointer(skb); 245a85a970aSWANG Cong struct tcf_skbedit *d = to_skbedit(a); 246c749cddaSDavide Caratti struct tcf_skbedit_params *params; 2471c40be12SEric Dumazet struct tc_skbedit opt = { 2481c40be12SEric Dumazet .index = d->tcf_index, 249036bb443SVlad Buslov .refcnt = refcount_read(&d->tcf_refcnt) - ref, 250036bb443SVlad Buslov .bindcnt = atomic_read(&d->tcf_bindcnt) - bind, 2511c40be12SEric Dumazet }; 252e7e3728bSQiaobin Fu u64 pure_flags = 0; 253c749cddaSDavide Caratti struct tcf_t t; 254c749cddaSDavide Caratti 2556d7a8df6SVlad Buslov spin_lock_bh(&d->tcf_lock); 2566d7a8df6SVlad Buslov params = rcu_dereference_protected(d->params, 2576d7a8df6SVlad Buslov lockdep_is_held(&d->tcf_lock)); 2586d7a8df6SVlad Buslov opt.action = d->tcf_action; 259ca9b0e27SAlexander Duyck 2601b34ec43SDavid S. Miller if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt)) 2611b34ec43SDavid S. Miller goto nla_put_failure; 262c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_PRIORITY) && 263c749cddaSDavide Caratti nla_put_u32(skb, TCA_SKBEDIT_PRIORITY, params->priority)) 2641b34ec43SDavid S. Miller goto nla_put_failure; 265c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_QUEUE_MAPPING) && 266c749cddaSDavide Caratti nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING, params->queue_mapping)) 2671b34ec43SDavid S. Miller goto nla_put_failure; 268c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_MARK) && 269c749cddaSDavide Caratti nla_put_u32(skb, TCA_SKBEDIT_MARK, params->mark)) 2701b34ec43SDavid S. Miller goto nla_put_failure; 271c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_PTYPE) && 272c749cddaSDavide Caratti nla_put_u16(skb, TCA_SKBEDIT_PTYPE, params->ptype)) 273ff202ee1SJamal Hadi Salim goto nla_put_failure; 274c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_MASK) && 275c749cddaSDavide Caratti nla_put_u32(skb, TCA_SKBEDIT_MASK, params->mask)) 2764fe77d82SAntonio Quartulli goto nla_put_failure; 277c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_INHERITDSFIELD) 278e7e3728bSQiaobin Fu pure_flags |= SKBEDIT_F_INHERITDSFIELD; 279e7e3728bSQiaobin Fu if (pure_flags != 0 && 280e7e3728bSQiaobin Fu nla_put(skb, TCA_SKBEDIT_FLAGS, sizeof(pure_flags), &pure_flags)) 281e7e3728bSQiaobin Fu goto nla_put_failure; 28248d8ee16SJamal Hadi Salim 28348d8ee16SJamal Hadi Salim tcf_tm_dump(&t, &d->tcf_tm); 2849854518eSNicolas Dichtel if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD)) 2851b34ec43SDavid S. Miller goto nla_put_failure; 2866d7a8df6SVlad Buslov spin_unlock_bh(&d->tcf_lock); 2876d7a8df6SVlad Buslov 288ca9b0e27SAlexander Duyck return skb->len; 289ca9b0e27SAlexander Duyck 290ca9b0e27SAlexander Duyck nla_put_failure: 2916d7a8df6SVlad Buslov spin_unlock_bh(&d->tcf_lock); 292ca9b0e27SAlexander Duyck nlmsg_trim(skb, b); 293ca9b0e27SAlexander Duyck return -1; 294ca9b0e27SAlexander Duyck } 295ca9b0e27SAlexander Duyck 296c749cddaSDavide Caratti static void tcf_skbedit_cleanup(struct tc_action *a) 297c749cddaSDavide Caratti { 298c749cddaSDavide Caratti struct tcf_skbedit *d = to_skbedit(a); 299c749cddaSDavide Caratti struct tcf_skbedit_params *params; 300c749cddaSDavide Caratti 301c749cddaSDavide Caratti params = rcu_dereference_protected(d->params, 1); 302c749cddaSDavide Caratti if (params) 303c749cddaSDavide Caratti kfree_rcu(params, rcu); 304c749cddaSDavide Caratti } 305c749cddaSDavide Caratti 306ddf97ccdSWANG Cong static int tcf_skbedit_walker(struct net *net, struct sk_buff *skb, 307ddf97ccdSWANG Cong struct netlink_callback *cb, int type, 30841780105SAlexander Aring const struct tc_action_ops *ops, 30941780105SAlexander Aring struct netlink_ext_ack *extack) 310ddf97ccdSWANG Cong { 311ddf97ccdSWANG Cong struct tc_action_net *tn = net_generic(net, skbedit_net_id); 312ddf97ccdSWANG Cong 313b3620145SAlexander Aring return tcf_generic_walker(tn, skb, cb, type, ops, extack); 314ddf97ccdSWANG Cong } 315ddf97ccdSWANG Cong 316f061b48cSCong Wang static int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index) 317ddf97ccdSWANG Cong { 318ddf97ccdSWANG Cong struct tc_action_net *tn = net_generic(net, skbedit_net_id); 319ddf97ccdSWANG Cong 32065a206c0SChris Mi return tcf_idr_search(tn, a, index); 321ddf97ccdSWANG Cong } 322ddf97ccdSWANG Cong 323e1fea322SRoman Mashak static size_t tcf_skbedit_get_fill_size(const struct tc_action *act) 324e1fea322SRoman Mashak { 325e1fea322SRoman Mashak return nla_total_size(sizeof(struct tc_skbedit)) 326e1fea322SRoman Mashak + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_PRIORITY */ 327e1fea322SRoman Mashak + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_QUEUE_MAPPING */ 328e1fea322SRoman Mashak + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MARK */ 329e1fea322SRoman Mashak + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_PTYPE */ 330e1fea322SRoman Mashak + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MASK */ 331e1fea322SRoman Mashak + nla_total_size_64bit(sizeof(u64)); /* TCA_SKBEDIT_FLAGS */ 332e1fea322SRoman Mashak } 333e1fea322SRoman Mashak 334c54e1d92SBaowen Zheng static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data, 335c2ccf84eSIdo Schimmel u32 *index_inc, bool bind, 336c2ccf84eSIdo Schimmel struct netlink_ext_ack *extack) 337c54e1d92SBaowen Zheng { 338c54e1d92SBaowen Zheng if (bind) { 339c54e1d92SBaowen Zheng struct flow_action_entry *entry = entry_data; 340c54e1d92SBaowen Zheng 341c54e1d92SBaowen Zheng if (is_tcf_skbedit_mark(act)) { 342c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_MARK; 343c54e1d92SBaowen Zheng entry->mark = tcf_skbedit_mark(act); 344c54e1d92SBaowen Zheng } else if (is_tcf_skbedit_ptype(act)) { 345c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_PTYPE; 346c54e1d92SBaowen Zheng entry->ptype = tcf_skbedit_ptype(act); 347c54e1d92SBaowen Zheng } else if (is_tcf_skbedit_priority(act)) { 348c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_PRIORITY; 349c54e1d92SBaowen Zheng entry->priority = tcf_skbedit_priority(act); 350a9c64939SIdo Schimmel } else if (is_tcf_skbedit_queue_mapping(act)) { 351a9c64939SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"queue_mapping\" option is used"); 352a9c64939SIdo Schimmel return -EOPNOTSUPP; 353a9c64939SIdo Schimmel } else if (is_tcf_skbedit_inheritdsfield(act)) { 354a9c64939SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"inheritdsfield\" option is used"); 355a9c64939SIdo Schimmel return -EOPNOTSUPP; 356c54e1d92SBaowen Zheng } else { 357a9c64939SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Unsupported skbedit option offload"); 358c54e1d92SBaowen Zheng return -EOPNOTSUPP; 359c54e1d92SBaowen Zheng } 360c54e1d92SBaowen Zheng *index_inc = 1; 361c54e1d92SBaowen Zheng } else { 3628cbfe939SBaowen Zheng struct flow_offload_action *fl_action = entry_data; 3638cbfe939SBaowen Zheng 3648cbfe939SBaowen Zheng if (is_tcf_skbedit_mark(act)) 3658cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_MARK; 3668cbfe939SBaowen Zheng else if (is_tcf_skbedit_ptype(act)) 3678cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_PTYPE; 3688cbfe939SBaowen Zheng else if (is_tcf_skbedit_priority(act)) 3698cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_PRIORITY; 3708cbfe939SBaowen Zheng else 371c54e1d92SBaowen Zheng return -EOPNOTSUPP; 372c54e1d92SBaowen Zheng } 373c54e1d92SBaowen Zheng 374c54e1d92SBaowen Zheng return 0; 375c54e1d92SBaowen Zheng } 376c54e1d92SBaowen Zheng 377ca9b0e27SAlexander Duyck static struct tc_action_ops act_skbedit_ops = { 378ca9b0e27SAlexander Duyck .kind = "skbedit", 379eddd2cf1SEli Cohen .id = TCA_ID_SKBEDIT, 380ca9b0e27SAlexander Duyck .owner = THIS_MODULE, 38145da1dacSJamal Hadi Salim .act = tcf_skbedit_act, 382837cb17dSPetr Machata .stats_update = tcf_skbedit_stats_update, 383ca9b0e27SAlexander Duyck .dump = tcf_skbedit_dump, 384ca9b0e27SAlexander Duyck .init = tcf_skbedit_init, 385c749cddaSDavide Caratti .cleanup = tcf_skbedit_cleanup, 386ddf97ccdSWANG Cong .walk = tcf_skbedit_walker, 387e1fea322SRoman Mashak .get_fill_size = tcf_skbedit_get_fill_size, 388ddf97ccdSWANG Cong .lookup = tcf_skbedit_search, 389c54e1d92SBaowen Zheng .offload_act_setup = tcf_skbedit_offload_act_setup, 390a85a970aSWANG Cong .size = sizeof(struct tcf_skbedit), 391ddf97ccdSWANG Cong }; 392ddf97ccdSWANG Cong 393ddf97ccdSWANG Cong static __net_init int skbedit_init_net(struct net *net) 394ddf97ccdSWANG Cong { 395ddf97ccdSWANG Cong struct tc_action_net *tn = net_generic(net, skbedit_net_id); 396ddf97ccdSWANG Cong 397981471bdSCong Wang return tc_action_net_init(net, tn, &act_skbedit_ops); 398ddf97ccdSWANG Cong } 399ddf97ccdSWANG Cong 400039af9c6SCong Wang static void __net_exit skbedit_exit_net(struct list_head *net_list) 401ddf97ccdSWANG Cong { 402039af9c6SCong Wang tc_action_net_exit(net_list, skbedit_net_id); 403ddf97ccdSWANG Cong } 404ddf97ccdSWANG Cong 405ddf97ccdSWANG Cong static struct pernet_operations skbedit_net_ops = { 406ddf97ccdSWANG Cong .init = skbedit_init_net, 407039af9c6SCong Wang .exit_batch = skbedit_exit_net, 408ddf97ccdSWANG Cong .id = &skbedit_net_id, 409ddf97ccdSWANG Cong .size = sizeof(struct tc_action_net), 410ca9b0e27SAlexander Duyck }; 411ca9b0e27SAlexander Duyck 412ca9b0e27SAlexander Duyck MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); 413ca9b0e27SAlexander Duyck MODULE_DESCRIPTION("SKB Editing"); 414ca9b0e27SAlexander Duyck MODULE_LICENSE("GPL"); 415ca9b0e27SAlexander Duyck 416ca9b0e27SAlexander Duyck static int __init skbedit_init_module(void) 417ca9b0e27SAlexander Duyck { 418ddf97ccdSWANG Cong return tcf_register_action(&act_skbedit_ops, &skbedit_net_ops); 419ca9b0e27SAlexander Duyck } 420ca9b0e27SAlexander Duyck 421ca9b0e27SAlexander Duyck static void __exit skbedit_cleanup_module(void) 422ca9b0e27SAlexander Duyck { 423ddf97ccdSWANG Cong tcf_unregister_action(&act_skbedit_ops, &skbedit_net_ops); 424ca9b0e27SAlexander Duyck } 425ca9b0e27SAlexander Duyck 426ca9b0e27SAlexander Duyck module_init(skbedit_init_module); 427ca9b0e27SAlexander Duyck module_exit(skbedit_cleanup_module); 428