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>
19871cf386SPedro Tammela #include <net/tc_wrapper.h>
20ca9b0e27SAlexander Duyck
21ca9b0e27SAlexander Duyck #include <linux/tc_act/tc_skbedit.h>
22ca9b0e27SAlexander Duyck #include <net/tc_act/tc_skbedit.h>
23ca9b0e27SAlexander Duyck
24a85a970aSWANG Cong static struct tc_action_ops act_skbedit_ops;
25ddf97ccdSWANG Cong
tcf_skbedit_hash(struct tcf_skbedit_params * params,struct sk_buff * skb)2638a6f086STonghao Zhang static u16 tcf_skbedit_hash(struct tcf_skbedit_params *params,
2738a6f086STonghao Zhang struct sk_buff *skb)
2838a6f086STonghao Zhang {
2938a6f086STonghao Zhang u16 queue_mapping = params->queue_mapping;
3038a6f086STonghao Zhang
3138a6f086STonghao Zhang if (params->flags & SKBEDIT_F_TXQ_SKBHASH) {
3238a6f086STonghao Zhang u32 hash = skb_get_hash(skb);
3338a6f086STonghao Zhang
3438a6f086STonghao Zhang queue_mapping += hash % params->mapping_mod;
3538a6f086STonghao Zhang }
3638a6f086STonghao Zhang
3738a6f086STonghao Zhang return netdev_cap_txqueue(skb->dev, queue_mapping);
3838a6f086STonghao Zhang }
3938a6f086STonghao Zhang
tcf_skbedit_act(struct sk_buff * skb,const struct tc_action * a,struct tcf_result * res)40871cf386SPedro Tammela TC_INDIRECT_SCOPE int tcf_skbedit_act(struct sk_buff *skb,
41871cf386SPedro Tammela const struct tc_action *a,
42ca9b0e27SAlexander Duyck struct tcf_result *res)
43ca9b0e27SAlexander Duyck {
44a85a970aSWANG Cong struct tcf_skbedit *d = to_skbedit(a);
45c749cddaSDavide Caratti struct tcf_skbedit_params *params;
46c749cddaSDavide Caratti int action;
47ca9b0e27SAlexander Duyck
489c4a4e48SJamal Hadi Salim tcf_lastuse_update(&d->tcf_tm);
4950dc9a85SAhmed S. Darwish bstats_update(this_cpu_ptr(d->common.cpu_bstats), skb);
50ca9b0e27SAlexander Duyck
517fd4b288SPaolo Abeni params = rcu_dereference_bh(d->params);
52c749cddaSDavide Caratti action = READ_ONCE(d->tcf_action);
53c749cddaSDavide Caratti
54c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_PRIORITY)
55c749cddaSDavide Caratti skb->priority = params->priority;
56c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_INHERITDSFIELD) {
57e7e3728bSQiaobin Fu int wlen = skb_network_offset(skb);
58e7e3728bSQiaobin Fu
59d7bf2ebeSToke Høiland-Jørgensen switch (skb_protocol(skb, true)) {
60e7e3728bSQiaobin Fu case htons(ETH_P_IP):
61e7e3728bSQiaobin Fu wlen += sizeof(struct iphdr);
62e7e3728bSQiaobin Fu if (!pskb_may_pull(skb, wlen))
63e7e3728bSQiaobin Fu goto err;
64e7e3728bSQiaobin Fu skb->priority = ipv4_get_dsfield(ip_hdr(skb)) >> 2;
65e7e3728bSQiaobin Fu break;
66e7e3728bSQiaobin Fu
67e7e3728bSQiaobin Fu case htons(ETH_P_IPV6):
68e7e3728bSQiaobin Fu wlen += sizeof(struct ipv6hdr);
69e7e3728bSQiaobin Fu if (!pskb_may_pull(skb, wlen))
70e7e3728bSQiaobin Fu goto err;
71e7e3728bSQiaobin Fu skb->priority = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2;
72e7e3728bSQiaobin Fu break;
73e7e3728bSQiaobin Fu }
74e7e3728bSQiaobin Fu }
75c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_QUEUE_MAPPING &&
762f1e85b1STonghao Zhang skb->dev->real_num_tx_queues > params->queue_mapping) {
772f1e85b1STonghao Zhang #ifdef CONFIG_NET_EGRESS
782f1e85b1STonghao Zhang netdev_xmit_skip_txqueue(true);
792f1e85b1STonghao Zhang #endif
8038a6f086STonghao Zhang skb_set_queue_mapping(skb, tcf_skbedit_hash(params, skb));
812f1e85b1STonghao Zhang }
82c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_MARK) {
83c749cddaSDavide Caratti skb->mark &= ~params->mask;
84c749cddaSDavide Caratti skb->mark |= params->mark & params->mask;
854fe77d82SAntonio Quartulli }
86c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_PTYPE)
87c749cddaSDavide Caratti skb->pkt_type = params->ptype;
88c749cddaSDavide Caratti return action;
897fd4b288SPaolo Abeni
90e7e3728bSQiaobin Fu err:
916f3dfb0dSDavide Caratti qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats));
927fd4b288SPaolo Abeni return TC_ACT_SHOT;
93ca9b0e27SAlexander Duyck }
94ca9b0e27SAlexander Duyck
tcf_skbedit_stats_update(struct tc_action * a,u64 bytes,u64 packets,u64 drops,u64 lastuse,bool hw)95837cb17dSPetr Machata static void tcf_skbedit_stats_update(struct tc_action *a, u64 bytes,
964b61d3e8SPo Liu u64 packets, u64 drops,
974b61d3e8SPo Liu u64 lastuse, bool hw)
98837cb17dSPetr Machata {
99837cb17dSPetr Machata struct tcf_skbedit *d = to_skbedit(a);
100837cb17dSPetr Machata struct tcf_t *tm = &d->tcf_tm;
101837cb17dSPetr Machata
1024b61d3e8SPo Liu tcf_action_update_stats(a, bytes, packets, drops, hw);
103837cb17dSPetr Machata tm->lastuse = max_t(u64, tm->lastuse, lastuse);
104837cb17dSPetr Machata }
105837cb17dSPetr Machata
106ca9b0e27SAlexander Duyck static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = {
107ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) },
108ca9b0e27SAlexander Duyck [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) },
109ca9b0e27SAlexander Duyck [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) },
1101c55d62eSjamal [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) },
111ff202ee1SJamal Hadi Salim [TCA_SKBEDIT_PTYPE] = { .len = sizeof(u16) },
1124fe77d82SAntonio Quartulli [TCA_SKBEDIT_MASK] = { .len = sizeof(u32) },
113e7e3728bSQiaobin Fu [TCA_SKBEDIT_FLAGS] = { .len = sizeof(u64) },
11438a6f086STonghao Zhang [TCA_SKBEDIT_QUEUE_MAPPING_MAX] = { .len = sizeof(u16) },
115ca9b0e27SAlexander Duyck };
116ca9b0e27SAlexander Duyck
tcf_skbedit_init(struct net * net,struct nlattr * nla,struct nlattr * est,struct tc_action ** a,struct tcf_proto * tp,u32 act_flags,struct netlink_ext_ack * extack)117c1b52739SBenjamin LaHaise static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
118a85a970aSWANG Cong struct nlattr *est, struct tc_action **a,
119abbb0d33SVlad Buslov struct tcf_proto *tp, u32 act_flags,
120789871bbSVlad Buslov struct netlink_ext_ack *extack)
121ca9b0e27SAlexander Duyck {
122acd0a7abSZhengchao Shao struct tc_action_net *tn = net_generic(net, act_skbedit_ops.net_id);
123695176bfSCong Wang bool bind = act_flags & TCA_ACT_FLAGS_BIND;
1246d7a8df6SVlad Buslov struct tcf_skbedit_params *params_new;
125ca9b0e27SAlexander Duyck struct nlattr *tb[TCA_SKBEDIT_MAX + 1];
126ec7727bbSDavide Caratti struct tcf_chain *goto_ch = NULL;
127ca9b0e27SAlexander Duyck struct tc_skbedit *parm;
128ca9b0e27SAlexander Duyck struct tcf_skbedit *d;
1294fe77d82SAntonio Quartulli u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL;
130ff202ee1SJamal Hadi Salim u16 *queue_mapping = NULL, *ptype = NULL;
13138a6f086STonghao Zhang u16 mapping_mod = 1;
132b2313077SWANG Cong bool exists = false;
133b2313077SWANG Cong int ret = 0, err;
1347be8ef2cSDmytro Linkin u32 index;
135ca9b0e27SAlexander Duyck
136ca9b0e27SAlexander Duyck if (nla == NULL)
137ca9b0e27SAlexander Duyck return -EINVAL;
138ca9b0e27SAlexander Duyck
1398cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_SKBEDIT_MAX, nla,
1408cb08174SJohannes Berg skbedit_policy, NULL);
141ca9b0e27SAlexander Duyck if (err < 0)
142ca9b0e27SAlexander Duyck return err;
143ca9b0e27SAlexander Duyck
144ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PARMS] == NULL)
145ca9b0e27SAlexander Duyck return -EINVAL;
146ca9b0e27SAlexander Duyck
147ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_PRIORITY] != NULL) {
148ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_PRIORITY;
149ca9b0e27SAlexander Duyck priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]);
150ca9b0e27SAlexander Duyck }
151ca9b0e27SAlexander Duyck
152ca9b0e27SAlexander Duyck if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
1534a6a676fSAmritha Nambiar if (is_tcf_skbedit_ingress(act_flags) &&
1544a6a676fSAmritha Nambiar !(act_flags & TCA_ACT_FLAGS_SKIP_SW)) {
1554a6a676fSAmritha Nambiar NL_SET_ERR_MSG_MOD(extack, "\"queue_mapping\" option on receive side is hardware only, use skip_sw");
1564a6a676fSAmritha Nambiar return -EOPNOTSUPP;
1574a6a676fSAmritha Nambiar }
158ca9b0e27SAlexander Duyck flags |= SKBEDIT_F_QUEUE_MAPPING;
159ca9b0e27SAlexander Duyck queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]);
160ca9b0e27SAlexander Duyck }
1611c55d62eSjamal
162ff202ee1SJamal Hadi Salim if (tb[TCA_SKBEDIT_PTYPE] != NULL) {
163ff202ee1SJamal Hadi Salim ptype = nla_data(tb[TCA_SKBEDIT_PTYPE]);
164ff202ee1SJamal Hadi Salim if (!skb_pkt_type_ok(*ptype))
165ff202ee1SJamal Hadi Salim return -EINVAL;
166ff202ee1SJamal Hadi Salim flags |= SKBEDIT_F_PTYPE;
167ff202ee1SJamal Hadi Salim }
168ff202ee1SJamal Hadi Salim
1691c55d62eSjamal if (tb[TCA_SKBEDIT_MARK] != NULL) {
1701c55d62eSjamal flags |= SKBEDIT_F_MARK;
1711c55d62eSjamal mark = nla_data(tb[TCA_SKBEDIT_MARK]);
1721c55d62eSjamal }
1731c55d62eSjamal
1744fe77d82SAntonio Quartulli if (tb[TCA_SKBEDIT_MASK] != NULL) {
1754fe77d82SAntonio Quartulli flags |= SKBEDIT_F_MASK;
1764fe77d82SAntonio Quartulli mask = nla_data(tb[TCA_SKBEDIT_MASK]);
1774fe77d82SAntonio Quartulli }
1784fe77d82SAntonio Quartulli
179e7e3728bSQiaobin Fu if (tb[TCA_SKBEDIT_FLAGS] != NULL) {
180e7e3728bSQiaobin Fu u64 *pure_flags = nla_data(tb[TCA_SKBEDIT_FLAGS]);
181e7e3728bSQiaobin Fu
18238a6f086STonghao Zhang if (*pure_flags & SKBEDIT_F_TXQ_SKBHASH) {
18338a6f086STonghao Zhang u16 *queue_mapping_max;
18438a6f086STonghao Zhang
18538a6f086STonghao Zhang if (!tb[TCA_SKBEDIT_QUEUE_MAPPING] ||
18638a6f086STonghao Zhang !tb[TCA_SKBEDIT_QUEUE_MAPPING_MAX]) {
18738a6f086STonghao Zhang NL_SET_ERR_MSG_MOD(extack, "Missing required range of queue_mapping.");
18838a6f086STonghao Zhang return -EINVAL;
18938a6f086STonghao Zhang }
19038a6f086STonghao Zhang
19138a6f086STonghao Zhang queue_mapping_max =
19238a6f086STonghao Zhang nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING_MAX]);
19338a6f086STonghao Zhang if (*queue_mapping_max < *queue_mapping) {
19438a6f086STonghao Zhang NL_SET_ERR_MSG_MOD(extack, "The range of queue_mapping is invalid, max < min.");
19538a6f086STonghao Zhang return -EINVAL;
19638a6f086STonghao Zhang }
19738a6f086STonghao Zhang
19838a6f086STonghao Zhang mapping_mod = *queue_mapping_max - *queue_mapping + 1;
19938a6f086STonghao Zhang flags |= SKBEDIT_F_TXQ_SKBHASH;
20038a6f086STonghao Zhang }
201e7e3728bSQiaobin Fu if (*pure_flags & SKBEDIT_F_INHERITDSFIELD)
202e7e3728bSQiaobin Fu flags |= SKBEDIT_F_INHERITDSFIELD;
203e7e3728bSQiaobin Fu }
204e7e3728bSQiaobin Fu
205ca9b0e27SAlexander Duyck parm = nla_data(tb[TCA_SKBEDIT_PARMS]);
2067be8ef2cSDmytro Linkin index = parm->index;
2077be8ef2cSDmytro Linkin err = tcf_idr_check_alloc(tn, &index, a, bind);
2080190c1d4SVlad Buslov if (err < 0)
2090190c1d4SVlad Buslov return err;
2100190c1d4SVlad Buslov exists = err;
2115e1567aeSJamal Hadi Salim if (exists && bind)
212c2a67de9SPedro Tammela return ACT_P_BOUND;
2135e1567aeSJamal Hadi Salim
2145e1567aeSJamal Hadi Salim if (!flags) {
215af5d0184SRoman Mashak if (exists)
21665a206c0SChris Mi tcf_idr_release(*a, bind);
2170190c1d4SVlad Buslov else
2187be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index);
2195e1567aeSJamal Hadi Salim return -EINVAL;
2205e1567aeSJamal Hadi Salim }
2215e1567aeSJamal Hadi Salim
2225e1567aeSJamal Hadi Salim if (!exists) {
2237be8ef2cSDmytro Linkin ret = tcf_idr_create(tn, index, est, a,
22440bd094dSBaowen Zheng &act_skbedit_ops, bind, true, act_flags);
2250190c1d4SVlad Buslov if (ret) {
2267be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index);
22786062033SWANG Cong return ret;
2280190c1d4SVlad Buslov }
229ca9b0e27SAlexander Duyck
230a85a970aSWANG Cong d = to_skbedit(*a);
231ca9b0e27SAlexander Duyck ret = ACT_P_CREATED;
232ca9b0e27SAlexander Duyck } else {
233a85a970aSWANG Cong d = to_skbedit(*a);
234695176bfSCong Wang if (!(act_flags & TCA_ACT_FLAGS_REPLACE)) {
23565a206c0SChris Mi tcf_idr_release(*a, bind);
236ca9b0e27SAlexander Duyck return -EEXIST;
237ca9b0e27SAlexander Duyck }
2384e8ddd7fSVlad Buslov }
239ec7727bbSDavide Caratti err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
240ec7727bbSDavide Caratti if (err < 0)
241ec7727bbSDavide Caratti goto release_idr;
242ca9b0e27SAlexander Duyck
243c749cddaSDavide Caratti params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
244c749cddaSDavide Caratti if (unlikely(!params_new)) {
245ec7727bbSDavide Caratti err = -ENOMEM;
246ec7727bbSDavide Caratti goto put_chain;
247c749cddaSDavide Caratti }
248c749cddaSDavide Caratti
249c749cddaSDavide Caratti params_new->flags = flags;
250ca9b0e27SAlexander Duyck if (flags & SKBEDIT_F_PRIORITY)
251c749cddaSDavide Caratti params_new->priority = *priority;
25238a6f086STonghao Zhang if (flags & SKBEDIT_F_QUEUE_MAPPING) {
253c749cddaSDavide Caratti params_new->queue_mapping = *queue_mapping;
25438a6f086STonghao Zhang params_new->mapping_mod = mapping_mod;
25538a6f086STonghao Zhang }
2561c55d62eSjamal if (flags & SKBEDIT_F_MARK)
257c749cddaSDavide Caratti params_new->mark = *mark;
258ff202ee1SJamal Hadi Salim if (flags & SKBEDIT_F_PTYPE)
259c749cddaSDavide Caratti params_new->ptype = *ptype;
2604fe77d82SAntonio Quartulli /* default behaviour is to use all the bits */
261c749cddaSDavide Caratti params_new->mask = 0xffffffff;
2624fe77d82SAntonio Quartulli if (flags & SKBEDIT_F_MASK)
263c749cddaSDavide Caratti params_new->mask = *mask;
2641c55d62eSjamal
2656d7a8df6SVlad Buslov spin_lock_bh(&d->tcf_lock);
266ec7727bbSDavide Caratti goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
267445d3749SPaul E. McKenney params_new = rcu_replace_pointer(d->params, params_new,
2686d7a8df6SVlad Buslov lockdep_is_held(&d->tcf_lock));
2696d7a8df6SVlad Buslov spin_unlock_bh(&d->tcf_lock);
2706d7a8df6SVlad Buslov if (params_new)
2716d7a8df6SVlad Buslov kfree_rcu(params_new, rcu);
272ec7727bbSDavide Caratti if (goto_ch)
273ec7727bbSDavide Caratti tcf_chain_put_by_act(goto_ch);
274ca9b0e27SAlexander Duyck
275ca9b0e27SAlexander Duyck return ret;
276ec7727bbSDavide Caratti put_chain:
277ec7727bbSDavide Caratti if (goto_ch)
278ec7727bbSDavide Caratti tcf_chain_put_by_act(goto_ch);
279ec7727bbSDavide Caratti release_idr:
280ec7727bbSDavide Caratti tcf_idr_release(*a, bind);
281ec7727bbSDavide Caratti return err;
282ca9b0e27SAlexander Duyck }
283ca9b0e27SAlexander Duyck
tcf_skbedit_dump(struct sk_buff * skb,struct tc_action * a,int bind,int ref)284cc7ec456SEric Dumazet static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a,
285ca9b0e27SAlexander Duyck int bind, int ref)
286ca9b0e27SAlexander Duyck {
287ca9b0e27SAlexander Duyck unsigned char *b = skb_tail_pointer(skb);
288a85a970aSWANG Cong struct tcf_skbedit *d = to_skbedit(a);
289c749cddaSDavide Caratti struct tcf_skbedit_params *params;
2901c40be12SEric Dumazet struct tc_skbedit opt = {
2911c40be12SEric Dumazet .index = d->tcf_index,
292036bb443SVlad Buslov .refcnt = refcount_read(&d->tcf_refcnt) - ref,
293036bb443SVlad Buslov .bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
2941c40be12SEric Dumazet };
295e7e3728bSQiaobin Fu u64 pure_flags = 0;
296c749cddaSDavide Caratti struct tcf_t t;
297c749cddaSDavide Caratti
2986d7a8df6SVlad Buslov spin_lock_bh(&d->tcf_lock);
2996d7a8df6SVlad Buslov params = rcu_dereference_protected(d->params,
3006d7a8df6SVlad Buslov lockdep_is_held(&d->tcf_lock));
3016d7a8df6SVlad Buslov opt.action = d->tcf_action;
302ca9b0e27SAlexander Duyck
3031b34ec43SDavid S. Miller if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt))
3041b34ec43SDavid S. Miller goto nla_put_failure;
305c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_PRIORITY) &&
306c749cddaSDavide Caratti nla_put_u32(skb, TCA_SKBEDIT_PRIORITY, params->priority))
3071b34ec43SDavid S. Miller goto nla_put_failure;
308c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_QUEUE_MAPPING) &&
309c749cddaSDavide Caratti nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING, params->queue_mapping))
3101b34ec43SDavid S. Miller goto nla_put_failure;
311c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_MARK) &&
312c749cddaSDavide Caratti nla_put_u32(skb, TCA_SKBEDIT_MARK, params->mark))
3131b34ec43SDavid S. Miller goto nla_put_failure;
314c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_PTYPE) &&
315c749cddaSDavide Caratti nla_put_u16(skb, TCA_SKBEDIT_PTYPE, params->ptype))
316ff202ee1SJamal Hadi Salim goto nla_put_failure;
317c749cddaSDavide Caratti if ((params->flags & SKBEDIT_F_MASK) &&
318c749cddaSDavide Caratti nla_put_u32(skb, TCA_SKBEDIT_MASK, params->mask))
3194fe77d82SAntonio Quartulli goto nla_put_failure;
320c749cddaSDavide Caratti if (params->flags & SKBEDIT_F_INHERITDSFIELD)
321e7e3728bSQiaobin Fu pure_flags |= SKBEDIT_F_INHERITDSFIELD;
32238a6f086STonghao Zhang if (params->flags & SKBEDIT_F_TXQ_SKBHASH) {
32338a6f086STonghao Zhang if (nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING_MAX,
32438a6f086STonghao Zhang params->queue_mapping + params->mapping_mod - 1))
32538a6f086STonghao Zhang goto nla_put_failure;
32638a6f086STonghao Zhang
32738a6f086STonghao Zhang pure_flags |= SKBEDIT_F_TXQ_SKBHASH;
32838a6f086STonghao Zhang }
329e7e3728bSQiaobin Fu if (pure_flags != 0 &&
330e7e3728bSQiaobin Fu nla_put(skb, TCA_SKBEDIT_FLAGS, sizeof(pure_flags), &pure_flags))
331e7e3728bSQiaobin Fu goto nla_put_failure;
33248d8ee16SJamal Hadi Salim
33348d8ee16SJamal Hadi Salim tcf_tm_dump(&t, &d->tcf_tm);
3349854518eSNicolas Dichtel if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD))
3351b34ec43SDavid S. Miller goto nla_put_failure;
3366d7a8df6SVlad Buslov spin_unlock_bh(&d->tcf_lock);
3376d7a8df6SVlad Buslov
338ca9b0e27SAlexander Duyck return skb->len;
339ca9b0e27SAlexander Duyck
340ca9b0e27SAlexander Duyck nla_put_failure:
3416d7a8df6SVlad Buslov spin_unlock_bh(&d->tcf_lock);
342ca9b0e27SAlexander Duyck nlmsg_trim(skb, b);
343ca9b0e27SAlexander Duyck return -1;
344ca9b0e27SAlexander Duyck }
345ca9b0e27SAlexander Duyck
tcf_skbedit_cleanup(struct tc_action * a)346c749cddaSDavide Caratti static void tcf_skbedit_cleanup(struct tc_action *a)
347c749cddaSDavide Caratti {
348c749cddaSDavide Caratti struct tcf_skbedit *d = to_skbedit(a);
349c749cddaSDavide Caratti struct tcf_skbedit_params *params;
350c749cddaSDavide Caratti
351c749cddaSDavide Caratti params = rcu_dereference_protected(d->params, 1);
352c749cddaSDavide Caratti if (params)
353c749cddaSDavide Caratti kfree_rcu(params, rcu);
354c749cddaSDavide Caratti }
355c749cddaSDavide Caratti
tcf_skbedit_get_fill_size(const struct tc_action * act)356e1fea322SRoman Mashak static size_t tcf_skbedit_get_fill_size(const struct tc_action *act)
357e1fea322SRoman Mashak {
358e1fea322SRoman Mashak return nla_total_size(sizeof(struct tc_skbedit))
359e1fea322SRoman Mashak + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_PRIORITY */
360e1fea322SRoman Mashak + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_QUEUE_MAPPING */
36138a6f086STonghao Zhang + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_QUEUE_MAPPING_MAX */
362e1fea322SRoman Mashak + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MARK */
363e1fea322SRoman Mashak + nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_PTYPE */
364e1fea322SRoman Mashak + nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MASK */
365e1fea322SRoman Mashak + nla_total_size_64bit(sizeof(u64)); /* TCA_SKBEDIT_FLAGS */
366e1fea322SRoman Mashak }
367e1fea322SRoman Mashak
tcf_skbedit_offload_act_setup(struct tc_action * act,void * entry_data,u32 * index_inc,bool bind,struct netlink_ext_ack * extack)368c54e1d92SBaowen Zheng static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data,
369c2ccf84eSIdo Schimmel u32 *index_inc, bool bind,
370c2ccf84eSIdo Schimmel struct netlink_ext_ack *extack)
371c54e1d92SBaowen Zheng {
372c54e1d92SBaowen Zheng if (bind) {
373c54e1d92SBaowen Zheng struct flow_action_entry *entry = entry_data;
374c54e1d92SBaowen Zheng
375c54e1d92SBaowen Zheng if (is_tcf_skbedit_mark(act)) {
376c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_MARK;
377c54e1d92SBaowen Zheng entry->mark = tcf_skbedit_mark(act);
378c54e1d92SBaowen Zheng } else if (is_tcf_skbedit_ptype(act)) {
379c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_PTYPE;
380c54e1d92SBaowen Zheng entry->ptype = tcf_skbedit_ptype(act);
381c54e1d92SBaowen Zheng } else if (is_tcf_skbedit_priority(act)) {
382c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_PRIORITY;
383c54e1d92SBaowen Zheng entry->priority = tcf_skbedit_priority(act);
3844a6a676fSAmritha Nambiar } else if (is_tcf_skbedit_tx_queue_mapping(act)) {
3854a6a676fSAmritha Nambiar NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"queue_mapping\" option is used on transmit side");
386a9c64939SIdo Schimmel return -EOPNOTSUPP;
3874a6a676fSAmritha Nambiar } else if (is_tcf_skbedit_rx_queue_mapping(act)) {
3884a6a676fSAmritha Nambiar entry->id = FLOW_ACTION_RX_QUEUE_MAPPING;
3894a6a676fSAmritha Nambiar entry->rx_queue = tcf_skbedit_rx_queue_mapping(act);
390a9c64939SIdo Schimmel } else if (is_tcf_skbedit_inheritdsfield(act)) {
391a9c64939SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"inheritdsfield\" option is used");
392a9c64939SIdo Schimmel return -EOPNOTSUPP;
393c54e1d92SBaowen Zheng } else {
394a9c64939SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Unsupported skbedit option offload");
395c54e1d92SBaowen Zheng return -EOPNOTSUPP;
396c54e1d92SBaowen Zheng }
397c54e1d92SBaowen Zheng *index_inc = 1;
398c54e1d92SBaowen Zheng } else {
3998cbfe939SBaowen Zheng struct flow_offload_action *fl_action = entry_data;
4008cbfe939SBaowen Zheng
4018cbfe939SBaowen Zheng if (is_tcf_skbedit_mark(act))
4028cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_MARK;
4038cbfe939SBaowen Zheng else if (is_tcf_skbedit_ptype(act))
4048cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_PTYPE;
4058cbfe939SBaowen Zheng else if (is_tcf_skbedit_priority(act))
4068cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_PRIORITY;
4074a6a676fSAmritha Nambiar else if (is_tcf_skbedit_rx_queue_mapping(act))
4084a6a676fSAmritha Nambiar fl_action->id = FLOW_ACTION_RX_QUEUE_MAPPING;
4098cbfe939SBaowen Zheng else
410c54e1d92SBaowen Zheng return -EOPNOTSUPP;
411c54e1d92SBaowen Zheng }
412c54e1d92SBaowen Zheng
413c54e1d92SBaowen Zheng return 0;
414c54e1d92SBaowen Zheng }
415c54e1d92SBaowen Zheng
416ca9b0e27SAlexander Duyck static struct tc_action_ops act_skbedit_ops = {
417ca9b0e27SAlexander Duyck .kind = "skbedit",
418eddd2cf1SEli Cohen .id = TCA_ID_SKBEDIT,
419ca9b0e27SAlexander Duyck .owner = THIS_MODULE,
42045da1dacSJamal Hadi Salim .act = tcf_skbedit_act,
421837cb17dSPetr Machata .stats_update = tcf_skbedit_stats_update,
422ca9b0e27SAlexander Duyck .dump = tcf_skbedit_dump,
423ca9b0e27SAlexander Duyck .init = tcf_skbedit_init,
424c749cddaSDavide Caratti .cleanup = tcf_skbedit_cleanup,
425e1fea322SRoman Mashak .get_fill_size = tcf_skbedit_get_fill_size,
426c54e1d92SBaowen Zheng .offload_act_setup = tcf_skbedit_offload_act_setup,
427a85a970aSWANG Cong .size = sizeof(struct tcf_skbedit),
428ddf97ccdSWANG Cong };
429*241a94abSMichal Koutný MODULE_ALIAS_NET_ACT("skbedit");
430ddf97ccdSWANG Cong
skbedit_init_net(struct net * net)431ddf97ccdSWANG Cong static __net_init int skbedit_init_net(struct net *net)
432ddf97ccdSWANG Cong {
433acd0a7abSZhengchao Shao struct tc_action_net *tn = net_generic(net, act_skbedit_ops.net_id);
434ddf97ccdSWANG Cong
435981471bdSCong Wang return tc_action_net_init(net, tn, &act_skbedit_ops);
436ddf97ccdSWANG Cong }
437ddf97ccdSWANG Cong
skbedit_exit_net(struct list_head * net_list)438039af9c6SCong Wang static void __net_exit skbedit_exit_net(struct list_head *net_list)
439ddf97ccdSWANG Cong {
440acd0a7abSZhengchao Shao tc_action_net_exit(net_list, act_skbedit_ops.net_id);
441ddf97ccdSWANG Cong }
442ddf97ccdSWANG Cong
443ddf97ccdSWANG Cong static struct pernet_operations skbedit_net_ops = {
444ddf97ccdSWANG Cong .init = skbedit_init_net,
445039af9c6SCong Wang .exit_batch = skbedit_exit_net,
446acd0a7abSZhengchao Shao .id = &act_skbedit_ops.net_id,
447ddf97ccdSWANG Cong .size = sizeof(struct tc_action_net),
448ca9b0e27SAlexander Duyck };
449ca9b0e27SAlexander Duyck
450ca9b0e27SAlexander Duyck MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>");
451ca9b0e27SAlexander Duyck MODULE_DESCRIPTION("SKB Editing");
452ca9b0e27SAlexander Duyck MODULE_LICENSE("GPL");
453ca9b0e27SAlexander Duyck
skbedit_init_module(void)454ca9b0e27SAlexander Duyck static int __init skbedit_init_module(void)
455ca9b0e27SAlexander Duyck {
456ddf97ccdSWANG Cong return tcf_register_action(&act_skbedit_ops, &skbedit_net_ops);
457ca9b0e27SAlexander Duyck }
458ca9b0e27SAlexander Duyck
skbedit_cleanup_module(void)459ca9b0e27SAlexander Duyck static void __exit skbedit_cleanup_module(void)
460ca9b0e27SAlexander Duyck {
461ddf97ccdSWANG Cong tcf_unregister_action(&act_skbedit_ops, &skbedit_net_ops);
462ca9b0e27SAlexander Duyck }
463ca9b0e27SAlexander Duyck
464ca9b0e27SAlexander Duyck module_init(skbedit_init_module);
465ca9b0e27SAlexander Duyck module_exit(skbedit_cleanup_module);
466