Merge tag 'v6.4-p2' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-block.git] / net / sched / act_pedit.c
CommitLineData
2874c5fd 1// SPDX-License-Identifier: GPL-2.0-or-later
1da177e4 2/*
0c6965dd 3 * net/sched/act_pedit.c Generic packet editor
1da177e4 4 *
1da177e4
LT
5 * Authors: Jamal Hadi Salim (2002-4)
6 */
7
1da177e4
LT
8#include <linux/types.h>
9#include <linux/kernel.h>
1da177e4 10#include <linux/string.h>
1da177e4 11#include <linux/errno.h>
1da177e4
LT
12#include <linux/skbuff.h>
13#include <linux/rtnetlink.h>
14#include <linux/module.h>
15#include <linux/init.h>
5a0e3ad6 16#include <linux/slab.h>
dc5fc579 17#include <net/netlink.h>
1da177e4
LT
18#include <net/pkt_sched.h>
19#include <linux/tc_act/tc_pedit.h>
20#include <net/tc_act/tc_pedit.h>
71d0ed70 21#include <uapi/linux/tc_act/tc_pedit.h>
6ac86ca3 22#include <net/pkt_cls.h>
871cf386 23#include <net/tc_wrapper.h>
1da177e4 24
a85a970a 25static struct tc_action_ops act_pedit_ops;
ddf97ccd 26
53b2bf3f 27static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
53f7e35f 28 [TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) },
71d0ed70 29 [TCA_PEDIT_KEYS_EX] = { .type = NLA_NESTED },
53b2bf3f
PM
30};
31
71d0ed70 32static const struct nla_policy pedit_key_ex_policy[TCA_PEDIT_KEY_EX_MAX + 1] = {
50360345
PT
33 [TCA_PEDIT_KEY_EX_HTYPE] =
34 NLA_POLICY_MAX(NLA_U16, TCA_PEDIT_HDR_TYPE_MAX),
35 [TCA_PEDIT_KEY_EX_CMD] = NLA_POLICY_MAX(NLA_U16, TCA_PEDIT_CMD_MAX),
71d0ed70
AV
36};
37
38static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla,
0c83c521 39 u8 n, struct netlink_ext_ack *extack)
71d0ed70
AV
40{
41 struct tcf_pedit_key_ex *keys_ex;
42 struct tcf_pedit_key_ex *k;
43 const struct nlattr *ka;
44 int err = -EINVAL;
45 int rem;
46
f67169fe 47 if (!nla)
71d0ed70
AV
48 return NULL;
49
50 keys_ex = kcalloc(n, sizeof(*k), GFP_KERNEL);
51 if (!keys_ex)
52 return ERR_PTR(-ENOMEM);
53
54 k = keys_ex;
55
56 nla_for_each_nested(ka, nla, rem) {
57 struct nlattr *tb[TCA_PEDIT_KEY_EX_MAX + 1];
58
59 if (!n) {
0c83c521 60 NL_SET_ERR_MSG_MOD(extack, "Can't parse more extended keys than requested");
71d0ed70
AV
61 err = -EINVAL;
62 goto err_out;
63 }
64 n--;
65
66 if (nla_type(ka) != TCA_PEDIT_KEY_EX) {
0c83c521 67 NL_SET_ERR_MSG_ATTR(extack, ka, "Unknown attribute, expected extended key");
71d0ed70
AV
68 err = -EINVAL;
69 goto err_out;
70 }
71
8cb08174
JB
72 err = nla_parse_nested_deprecated(tb, TCA_PEDIT_KEY_EX_MAX,
73 ka, pedit_key_ex_policy,
74 NULL);
71d0ed70
AV
75 if (err)
76 goto err_out;
77
0c83c521
PT
78 if (NL_REQ_ATTR_CHECK(extack, nla, tb, TCA_PEDIT_KEY_EX_HTYPE)) {
79 NL_SET_ERR_MSG(extack, "Missing required attribute");
80 err = -EINVAL;
81 goto err_out;
82 }
83
84 if (NL_REQ_ATTR_CHECK(extack, nla, tb, TCA_PEDIT_KEY_EX_CMD)) {
85 NL_SET_ERR_MSG(extack, "Missing required attribute");
71d0ed70
AV
86 err = -EINVAL;
87 goto err_out;
88 }
89
90 k->htype = nla_get_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
853a14ba 91 k->cmd = nla_get_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
71d0ed70 92
71d0ed70
AV
93 k++;
94 }
95
c4f65b09 96 if (n) {
0c83c521 97 NL_SET_ERR_MSG_MOD(extack, "Not enough extended keys to parse");
c4f65b09 98 err = -EINVAL;
71d0ed70 99 goto err_out;
c4f65b09 100 }
71d0ed70
AV
101
102 return keys_ex;
103
104err_out:
105 kfree(keys_ex);
106 return ERR_PTR(err);
107}
108
109static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
110 struct tcf_pedit_key_ex *keys_ex, int n)
111{
ae0be8de
MK
112 struct nlattr *keys_start = nla_nest_start_noflag(skb,
113 TCA_PEDIT_KEYS_EX);
71d0ed70 114
85eb9af1
DC
115 if (!keys_start)
116 goto nla_failure;
71d0ed70
AV
117 for (; n > 0; n--) {
118 struct nlattr *key_start;
119
ae0be8de 120 key_start = nla_nest_start_noflag(skb, TCA_PEDIT_KEY_EX);
85eb9af1
DC
121 if (!key_start)
122 goto nla_failure;
71d0ed70 123
853a14ba 124 if (nla_put_u16(skb, TCA_PEDIT_KEY_EX_HTYPE, keys_ex->htype) ||
85eb9af1
DC
125 nla_put_u16(skb, TCA_PEDIT_KEY_EX_CMD, keys_ex->cmd))
126 goto nla_failure;
71d0ed70
AV
127
128 nla_nest_end(skb, key_start);
129
130 keys_ex++;
131 }
132
133 nla_nest_end(skb, keys_start);
134
135 return 0;
85eb9af1
DC
136nla_failure:
137 nla_nest_cancel(skb, keys_start);
138 return -EINVAL;
71d0ed70
AV
139}
140
52cf89f7
PT
141static void tcf_pedit_cleanup_rcu(struct rcu_head *head)
142{
143 struct tcf_pedit_parms *parms =
144 container_of(head, struct tcf_pedit_parms, rcu);
145
146 kfree(parms->tcfp_keys_ex);
147 kfree(parms->tcfp_keys);
148
149 kfree(parms);
150}
151
c1b52739 152static int tcf_pedit_init(struct net *net, struct nlattr *nla,
a85a970a 153 struct nlattr *est, struct tc_action **a,
abbb0d33
VB
154 struct tcf_proto *tp, u32 flags,
155 struct netlink_ext_ack *extack)
1da177e4 156{
acd0a7ab 157 struct tc_action_net *tn = net_generic(net, act_pedit_ops.net_id);
695176bf 158 bool bind = flags & TCA_ACT_FLAGS_BIND;
6ac86ca3 159 struct tcf_chain *goto_ch = NULL;
52cf89f7
PT
160 struct tcf_pedit_parms *oparms, *nparms;
161 struct nlattr *tb[TCA_PEDIT_MAX + 1];
80f0f574
RM
162 struct tc_pedit *parm;
163 struct nlattr *pattr;
164 struct tcf_pedit *p;
165 int ret = 0, err;
8b796475 166 int i, ksize;
7be8ef2c 167 u32 index;
1da177e4 168
9868c0b2
RM
169 if (!nla) {
170 NL_SET_ERR_MSG_MOD(extack, "Pedit requires attributes to be passed");
1da177e4 171 return -EINVAL;
9868c0b2 172 }
1da177e4 173
8cb08174
JB
174 err = nla_parse_nested_deprecated(tb, TCA_PEDIT_MAX, nla,
175 pedit_policy, NULL);
cee63723
PM
176 if (err < 0)
177 return err;
178
71d0ed70
AV
179 pattr = tb[TCA_PEDIT_PARMS];
180 if (!pattr)
181 pattr = tb[TCA_PEDIT_PARMS_EX];
9868c0b2
RM
182 if (!pattr) {
183 NL_SET_ERR_MSG_MOD(extack, "Missing required TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute");
1da177e4 184 return -EINVAL;
9868c0b2 185 }
71d0ed70
AV
186
187 parm = nla_data(pattr);
71d0ed70 188
7be8ef2c
DL
189 index = parm->index;
190 err = tcf_idr_check_alloc(tn, &index, a, bind);
0190c1d4 191 if (!err) {
52cf89f7
PT
192 ret = tcf_idr_create_from_flags(tn, index, est, a,
193 &act_pedit_ops, bind, flags);
0190c1d4 194 if (ret) {
7be8ef2c 195 tcf_idr_cleanup(tn, index);
e9e42292 196 return ret;
0190c1d4 197 }
1da177e4 198 ret = ACT_P_CREATED;
0190c1d4 199 } else if (err > 0) {
1a29321e 200 if (bind)
e9e42292 201 return 0;
695176bf 202 if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
30e99ed6 203 ret = -EEXIST;
67b0c1a3 204 goto out_release;
1da177e4 205 }
0190c1d4 206 } else {
e9e42292
PT
207 return err;
208 }
209
210 if (!parm->nkeys) {
211 NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
212 ret = -EINVAL;
213 goto out_release;
214 }
215 ksize = parm->nkeys * sizeof(struct tc_pedit_key);
216 if (nla_len(pattr) < sizeof(*parm) + ksize) {
217 NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid");
218 ret = -EINVAL;
219 goto out_release;
220 }
221
222 nparms = kzalloc(sizeof(*nparms), GFP_KERNEL);
223 if (!nparms) {
224 ret = -ENOMEM;
225 goto out_release;
226 }
227
228 nparms->tcfp_keys_ex =
0c83c521 229 tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys, extack);
e9e42292
PT
230 if (IS_ERR(nparms->tcfp_keys_ex)) {
231 ret = PTR_ERR(nparms->tcfp_keys_ex);
232 goto out_free;
1da177e4
LT
233 }
234
6ac86ca3
DC
235 err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
236 if (err < 0) {
237 ret = err;
e9e42292 238 goto out_free_ex;
6ac86ca3 239 }
67b0c1a3 240
52cf89f7
PT
241 nparms->tcfp_off_max_hint = 0;
242 nparms->tcfp_flags = parm->flags;
243 nparms->tcfp_nkeys = parm->nkeys;
244
245 nparms->tcfp_keys = kmalloc(ksize, GFP_KERNEL);
246 if (!nparms->tcfp_keys) {
247 ret = -ENOMEM;
248 goto put_chain;
1da177e4 249 }
52cf89f7
PT
250
251 memcpy(nparms->tcfp_keys, parm->keys, ksize);
252
253 for (i = 0; i < nparms->tcfp_nkeys; ++i) {
e1201bc7 254 u32 offmask = nparms->tcfp_keys[i].offmask;
52cf89f7 255 u32 cur = nparms->tcfp_keys[i].off;
8b796475 256
e1201bc7
PT
257 /* The AT option can be added to static offsets in the datapath */
258 if (!offmask && cur % 4) {
259 NL_SET_ERR_MSG_MOD(extack, "Offsets must be on 32bit boundaries");
260 ret = -EINVAL;
1b483d9f 261 goto out_free_keys;
e1201bc7
PT
262 }
263
4d42d54a 264 /* sanitize the shift value for any later use */
52cf89f7
PT
265 nparms->tcfp_keys[i].shift = min_t(size_t,
266 BITS_PER_TYPE(int) - 1,
267 nparms->tcfp_keys[i].shift);
4d42d54a 268
8b796475
PA
269 /* The AT option can read a single byte, we can bound the actual
270 * value with uchar max.
271 */
e1201bc7 272 cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift;
8b796475
PA
273
274 /* Each key touches 4 bytes starting from the computed offset */
52cf89f7
PT
275 nparms->tcfp_off_max_hint =
276 max(nparms->tcfp_off_max_hint, cur + 4);
8b796475 277 }
71d0ed70 278
52cf89f7
PT
279 p = to_pedit(*a);
280
281 spin_lock_bh(&p->tcf_lock);
6ac86ca3 282 goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
52cf89f7
PT
283 oparms = rcu_replace_pointer(p->parms, nparms, 1);
284 spin_unlock_bh(&p->tcf_lock);
67b0c1a3 285
52cf89f7
PT
286 if (oparms)
287 call_rcu(&oparms->rcu, tcf_pedit_cleanup_rcu);
71d0ed70 288
6ac86ca3
DC
289 if (goto_ch)
290 tcf_chain_put_by_act(goto_ch);
52cf89f7 291
1da177e4 292 return ret;
67b0c1a3 293
1b483d9f
PT
294out_free_keys:
295 kfree(nparms->tcfp_keys);
6ac86ca3
DC
296put_chain:
297 if (goto_ch)
298 tcf_chain_put_by_act(goto_ch);
52cf89f7
PT
299out_free_ex:
300 kfree(nparms->tcfp_keys_ex);
30e99ed6 301out_free:
52cf89f7 302 kfree(nparms);
e9e42292
PT
303out_release:
304 tcf_idr_release(*a, bind);
30e99ed6 305 return ret;
1da177e4
LT
306}
307
9a63b255 308static void tcf_pedit_cleanup(struct tc_action *a)
1da177e4 309{
a85a970a 310 struct tcf_pedit *p = to_pedit(a);
52cf89f7 311 struct tcf_pedit_parms *parms;
80f0f574 312
52cf89f7
PT
313 parms = rcu_dereference_protected(p->parms, 1);
314
315 if (parms)
316 call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu);
1da177e4
LT
317}
318
95c2027b
AV
319static bool offset_valid(struct sk_buff *skb, int offset)
320{
321 if (offset > 0 && offset > skb->len)
322 return false;
323
324 if (offset < 0 && -offset > skb_headroom(skb))
325 return false;
326
327 return true;
328}
329
57714018
PT
330static void pedit_skb_hdr_offset(struct sk_buff *skb,
331 enum pedit_header_type htype, int *hoffset)
71d0ed70 332{
57714018 333 /* 'htype' is validated in the netlink parsing */
71d0ed70
AV
334 switch (htype) {
335 case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
57714018 336 if (skb_mac_header_was_set(skb))
71d0ed70 337 *hoffset = skb_mac_offset(skb);
71d0ed70
AV
338 break;
339 case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
340 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
341 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
342 *hoffset = skb_network_offset(skb);
71d0ed70
AV
343 break;
344 case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
345 case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
57714018 346 if (skb_transport_header_was_set(skb))
71d0ed70 347 *hoffset = skb_transport_offset(skb);
71d0ed70
AV
348 break;
349 default:
71d0ed70 350 break;
0a80848e 351 }
71d0ed70
AV
352}
353
871cf386
PT
354TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb,
355 const struct tc_action *a,
356 struct tcf_result *res)
1da177e4 357{
95b06938
PT
358 enum pedit_header_type htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
359 enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
a85a970a 360 struct tcf_pedit *p = to_pedit(a);
95b06938 361 struct tcf_pedit_key_ex *tkey_ex;
52cf89f7 362 struct tcf_pedit_parms *parms;
95b06938 363 struct tc_pedit_key *tkey;
8b796475 364 u32 max_offset;
4749c3ef 365 int i;
1da177e4 366
52cf89f7 367 parms = rcu_dereference_bh(p->parms);
1da177e4 368
8b796475
PA
369 max_offset = (skb_transport_header_was_set(skb) ?
370 skb_transport_offset(skb) :
371 skb_network_offset(skb)) +
52cf89f7 372 parms->tcfp_off_max_hint;
8b796475 373 if (skb_ensure_writable(skb, min(skb->len, max_offset)))
52cf89f7 374 goto done;
8b796475 375
9c4a4e48 376 tcf_lastuse_update(&p->tcf_tm);
52cf89f7 377 tcf_action_update_bstats(&p->common, skb);
1da177e4 378
95b06938
PT
379 tkey = parms->tcfp_keys;
380 tkey_ex = parms->tcfp_keys_ex;
71d0ed70 381
95b06938
PT
382 for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
383 int offset = tkey->off;
57714018 384 int hoffset = 0;
95b06938 385 u32 *ptr, hdata;
95b06938 386 u32 val;
1da177e4 387
95b06938
PT
388 if (tkey_ex) {
389 htype = tkey_ex->htype;
390 cmd = tkey_ex->cmd;
1da177e4 391
95b06938
PT
392 tkey_ex++;
393 }
95c2027b 394
57714018 395 pedit_skb_hdr_offset(skb, htype, &hoffset);
1da177e4 396
95b06938
PT
397 if (tkey->offmask) {
398 u8 *d, _d;
399
400 if (!offset_valid(skb, hoffset + tkey->at)) {
e3c9673e
PT
401 pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n",
402 hoffset + tkey->at);
853a14ba
AV
403 goto bad;
404 }
95b06938
PT
405 d = skb_header_pointer(skb, hoffset + tkey->at,
406 sizeof(_d), &_d);
407 if (!d)
408 goto bad;
853a14ba 409
e1201bc7
PT
410 offset += (*d & tkey->offmask) >> tkey->shift;
411 if (offset % 4) {
e3c9673e 412 pr_info_ratelimited("tc action pedit offset must be on 32 bit boundaries\n");
e1201bc7
PT
413 goto bad;
414 }
1da177e4 415 }
10297b99 416
95b06938 417 if (!offset_valid(skb, hoffset + offset)) {
e3c9673e 418 pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset);
95b06938
PT
419 goto bad;
420 }
421
422 ptr = skb_header_pointer(skb, hoffset + offset,
423 sizeof(hdata), &hdata);
424 if (!ptr)
425 goto bad;
426 /* just do it, baby */
427 switch (cmd) {
428 case TCA_PEDIT_KEY_EX_CMD_SET:
429 val = tkey->val;
430 break;
431 case TCA_PEDIT_KEY_EX_CMD_ADD:
432 val = (*ptr + tkey->val) & ~tkey->mask;
433 break;
434 default:
e3c9673e 435 pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd);
95b06938
PT
436 goto bad;
437 }
438
439 *ptr = ((*ptr & tkey->mask) ^ val);
440 if (ptr == &hdata)
441 skb_store_bits(skb, hoffset + offset, ptr, 4);
80f0f574 442 }
1da177e4 443
95b06938
PT
444 goto done;
445
1da177e4 446bad:
2d2e75d2 447 tcf_action_inc_overlimit_qstats(&p->common);
52cf89f7 448done:
e9ce1cd3 449 return p->tcf_action;
1da177e4
LT
450}
451
4b61d3e8
PL
452static void tcf_pedit_stats_update(struct tc_action *a, u64 bytes, u64 packets,
453 u64 drops, u64 lastuse, bool hw)
d4d9d9c5
PM
454{
455 struct tcf_pedit *d = to_pedit(a);
456 struct tcf_t *tm = &d->tcf_tm;
457
4b61d3e8 458 tcf_action_update_stats(a, bytes, packets, drops, hw);
d4d9d9c5
PM
459 tm->lastuse = max_t(u64, tm->lastuse, lastuse);
460}
461
e9ce1cd3
DM
462static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
463 int bind, int ref)
1da177e4 464{
27a884dc 465 unsigned char *b = skb_tail_pointer(skb);
a85a970a 466 struct tcf_pedit *p = to_pedit(a);
52cf89f7 467 struct tcf_pedit_parms *parms;
1da177e4 468 struct tc_pedit *opt;
1da177e4 469 struct tcf_t t;
10297b99
YH
470 int s;
471
52cf89f7
PT
472 spin_lock_bh(&p->tcf_lock);
473 parms = rcu_dereference_protected(p->parms, 1);
474 s = struct_size(opt, keys, parms->tcfp_nkeys);
1da177e4 475
0da974f4 476 opt = kzalloc(s, GFP_ATOMIC);
52cf89f7
PT
477 if (unlikely(!opt)) {
478 spin_unlock_bh(&p->tcf_lock);
1da177e4 479 return -ENOBUFS;
52cf89f7 480 }
1da177e4 481
52cf89f7
PT
482 memcpy(opt->keys, parms->tcfp_keys,
483 flex_array_size(opt, keys, parms->tcfp_nkeys));
e9ce1cd3 484 opt->index = p->tcf_index;
52cf89f7
PT
485 opt->nkeys = parms->tcfp_nkeys;
486 opt->flags = parms->tcfp_flags;
e9ce1cd3 487 opt->action = p->tcf_action;
036bb443
VB
488 opt->refcnt = refcount_read(&p->tcf_refcnt) - ref;
489 opt->bindcnt = atomic_read(&p->tcf_bindcnt) - bind;
1da177e4 490
52cf89f7
PT
491 if (parms->tcfp_keys_ex) {
492 if (tcf_pedit_key_ex_dump(skb, parms->tcfp_keys_ex,
493 parms->tcfp_nkeys))
85eb9af1 494 goto nla_put_failure;
71d0ed70
AV
495
496 if (nla_put(skb, TCA_PEDIT_PARMS_EX, s, opt))
497 goto nla_put_failure;
498 } else {
499 if (nla_put(skb, TCA_PEDIT_PARMS, s, opt))
500 goto nla_put_failure;
501 }
48d8ee16
JHS
502
503 tcf_tm_dump(&t, &p->tcf_tm);
9854518e 504 if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))
1b34ec43 505 goto nla_put_failure;
67b0c1a3 506 spin_unlock_bh(&p->tcf_lock);
48d8ee16 507
541673c8 508 kfree(opt);
1da177e4
LT
509 return skb->len;
510
7ba699c6 511nla_put_failure:
67b0c1a3 512 spin_unlock_bh(&p->tcf_lock);
dc5fc579 513 nlmsg_trim(skb, b);
541673c8 514 kfree(opt);
1da177e4
LT
515 return -1;
516}
517
c54e1d92 518static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data,
c2ccf84e
IS
519 u32 *index_inc, bool bind,
520 struct netlink_ext_ack *extack)
c54e1d92
BZ
521{
522 if (bind) {
523 struct flow_action_entry *entry = entry_data;
524 int k;
525
526 for (k = 0; k < tcf_pedit_nkeys(act); k++) {
527 switch (tcf_pedit_cmd(act, k)) {
528 case TCA_PEDIT_KEY_EX_CMD_SET:
529 entry->id = FLOW_ACTION_MANGLE;
530 break;
531 case TCA_PEDIT_KEY_EX_CMD_ADD:
532 entry->id = FLOW_ACTION_ADD;
533 break;
534 default:
bf3b99e4 535 NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload");
c54e1d92
BZ
536 return -EOPNOTSUPP;
537 }
538 entry->mangle.htype = tcf_pedit_htype(act, k);
539 entry->mangle.mask = tcf_pedit_mask(act, k);
540 entry->mangle.val = tcf_pedit_val(act, k);
541 entry->mangle.offset = tcf_pedit_offset(act, k);
542 entry->hw_stats = tc_act_hw_stats(act->hw_stats);
543 entry++;
544 }
545 *index_inc = k;
546 } else {
3320f36f
OS
547 struct flow_offload_action *fl_action = entry_data;
548 u32 cmd = tcf_pedit_cmd(act, 0);
549 int k;
550
551 switch (cmd) {
552 case TCA_PEDIT_KEY_EX_CMD_SET:
553 fl_action->id = FLOW_ACTION_MANGLE;
554 break;
555 case TCA_PEDIT_KEY_EX_CMD_ADD:
556 fl_action->id = FLOW_ACTION_ADD;
557 break;
558 default:
559 NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload");
560 return -EOPNOTSUPP;
561 }
562
563 for (k = 1; k < tcf_pedit_nkeys(act); k++) {
564 if (cmd != tcf_pedit_cmd(act, k)) {
565 NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload");
566 return -EOPNOTSUPP;
567 }
568 }
c54e1d92
BZ
569 }
570
571 return 0;
572}
573
e9ce1cd3 574static struct tc_action_ops act_pedit_ops = {
1da177e4 575 .kind = "pedit",
eddd2cf1 576 .id = TCA_ID_PEDIT,
1da177e4 577 .owner = THIS_MODULE,
6a2b401c 578 .act = tcf_pedit_act,
d4d9d9c5 579 .stats_update = tcf_pedit_stats_update,
1da177e4
LT
580 .dump = tcf_pedit_dump,
581 .cleanup = tcf_pedit_cleanup,
1da177e4 582 .init = tcf_pedit_init,
c54e1d92 583 .offload_act_setup = tcf_pedit_offload_act_setup,
a85a970a 584 .size = sizeof(struct tcf_pedit),
ddf97ccd
WC
585};
586
587static __net_init int pedit_init_net(struct net *net)
588{
acd0a7ab 589 struct tc_action_net *tn = net_generic(net, act_pedit_ops.net_id);
ddf97ccd 590
981471bd 591 return tc_action_net_init(net, tn, &act_pedit_ops);
ddf97ccd
WC
592}
593
039af9c6 594static void __net_exit pedit_exit_net(struct list_head *net_list)
ddf97ccd 595{
acd0a7ab 596 tc_action_net_exit(net_list, act_pedit_ops.net_id);
ddf97ccd
WC
597}
598
599static struct pernet_operations pedit_net_ops = {
600 .init = pedit_init_net,
039af9c6 601 .exit_batch = pedit_exit_net,
acd0a7ab 602 .id = &act_pedit_ops.net_id,
ddf97ccd 603 .size = sizeof(struct tc_action_net),
1da177e4
LT
604};
605
606MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
607MODULE_DESCRIPTION("Generic Packet Editor actions");
608MODULE_LICENSE("GPL");
609
e9ce1cd3 610static int __init pedit_init_module(void)
1da177e4 611{
ddf97ccd 612 return tcf_register_action(&act_pedit_ops, &pedit_net_ops);
1da177e4
LT
613}
614
e9ce1cd3 615static void __exit pedit_cleanup_module(void)
1da177e4 616{
ddf97ccd 617 tcf_unregister_action(&act_pedit_ops, &pedit_net_ops);
1da177e4
LT
618}
619
620module_init(pedit_init_module);
621module_exit(pedit_cleanup_module);