1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * net/sched/act_pedit.c Generic packet editor
4 *
5 * Authors: Jamal Hadi Salim (2002-4)
6 */
7
8 #include <linux/types.h>
9 #include <linux/kernel.h>
10 #include <linux/string.h>
11 #include <linux/errno.h>
12 #include <linux/skbuff.h>
13 #include <linux/rtnetlink.h>
14 #include <linux/module.h>
15 #include <linux/init.h>
16 #include <linux/ip.h>
17 #include <linux/ipv6.h>
18 #include <linux/slab.h>
19 #include <linux/overflow.h>
20 #include <linux/unaligned.h>
21 #include <net/ip.h>
22 #include <net/ipv6.h>
23 #include <net/netlink.h>
24 #include <net/pkt_sched.h>
25 #include <linux/tc_act/tc_pedit.h>
26 #include <net/tc_act/tc_pedit.h>
27 #include <uapi/linux/tc_act/tc_pedit.h>
28 #include <net/pkt_cls.h>
29 #include <net/tc_wrapper.h>
30
31 static struct tc_action_ops act_pedit_ops;
32
33 static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
34 [TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) },
35 [TCA_PEDIT_PARMS_EX] = { .len = sizeof(struct tc_pedit) },
36 [TCA_PEDIT_KEYS_EX] = { .type = NLA_NESTED },
37 };
38
39 static const struct nla_policy pedit_key_ex_policy[TCA_PEDIT_KEY_EX_MAX + 1] = {
40 [TCA_PEDIT_KEY_EX_HTYPE] =
41 NLA_POLICY_MAX(NLA_U16, TCA_PEDIT_HDR_TYPE_MAX),
42 [TCA_PEDIT_KEY_EX_CMD] = NLA_POLICY_MAX(NLA_U16, TCA_PEDIT_CMD_MAX),
43 };
44
tcf_pedit_keys_ex_parse(struct nlattr * nla,u8 n,struct netlink_ext_ack * extack)45 static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla,
46 u8 n, struct netlink_ext_ack *extack)
47 {
48 struct tcf_pedit_key_ex *keys_ex;
49 struct tcf_pedit_key_ex *k;
50 const struct nlattr *ka;
51 int err = -EINVAL;
52 int rem;
53
54 if (!nla)
55 return NULL;
56
57 keys_ex = kzalloc_objs(*k, n);
58 if (!keys_ex)
59 return ERR_PTR(-ENOMEM);
60
61 k = keys_ex;
62
63 nla_for_each_nested(ka, nla, rem) {
64 struct nlattr *tb[TCA_PEDIT_KEY_EX_MAX + 1];
65
66 if (!n) {
67 NL_SET_ERR_MSG_MOD(extack, "Can't parse more extended keys than requested");
68 err = -EINVAL;
69 goto err_out;
70 }
71 n--;
72
73 if (nla_type(ka) != TCA_PEDIT_KEY_EX) {
74 NL_SET_ERR_MSG_ATTR(extack, ka, "Unknown attribute, expected extended key");
75 err = -EINVAL;
76 goto err_out;
77 }
78
79 err = nla_parse_nested_deprecated(tb, TCA_PEDIT_KEY_EX_MAX,
80 ka, pedit_key_ex_policy,
81 NULL);
82 if (err)
83 goto err_out;
84
85 if (NL_REQ_ATTR_CHECK(extack, nla, tb, TCA_PEDIT_KEY_EX_HTYPE)) {
86 NL_SET_ERR_MSG(extack, "Missing required attribute");
87 err = -EINVAL;
88 goto err_out;
89 }
90
91 if (NL_REQ_ATTR_CHECK(extack, nla, tb, TCA_PEDIT_KEY_EX_CMD)) {
92 NL_SET_ERR_MSG(extack, "Missing required attribute");
93 err = -EINVAL;
94 goto err_out;
95 }
96
97 k->htype = nla_get_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
98 k->cmd = nla_get_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
99
100 k++;
101 }
102
103 if (n) {
104 NL_SET_ERR_MSG_MOD(extack, "Not enough extended keys to parse");
105 err = -EINVAL;
106 goto err_out;
107 }
108
109 return keys_ex;
110
111 err_out:
112 kfree(keys_ex);
113 return ERR_PTR(err);
114 }
115
tcf_pedit_key_ex_dump(struct sk_buff * skb,struct tcf_pedit_key_ex * keys_ex,int n)116 static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
117 struct tcf_pedit_key_ex *keys_ex, int n)
118 {
119 struct nlattr *keys_start = nla_nest_start_noflag(skb,
120 TCA_PEDIT_KEYS_EX);
121
122 if (!keys_start)
123 goto nla_failure;
124 for (; n > 0; n--) {
125 struct nlattr *key_start;
126
127 key_start = nla_nest_start_noflag(skb, TCA_PEDIT_KEY_EX);
128 if (!key_start)
129 goto nla_failure;
130
131 if (nla_put_u16(skb, TCA_PEDIT_KEY_EX_HTYPE, keys_ex->htype) ||
132 nla_put_u16(skb, TCA_PEDIT_KEY_EX_CMD, keys_ex->cmd))
133 goto nla_failure;
134
135 nla_nest_end(skb, key_start);
136
137 keys_ex++;
138 }
139
140 nla_nest_end(skb, keys_start);
141
142 return 0;
143 nla_failure:
144 nla_nest_cancel(skb, keys_start);
145 return -EINVAL;
146 }
147
tcf_pedit_cleanup_rcu(struct rcu_head * head)148 static void tcf_pedit_cleanup_rcu(struct rcu_head *head)
149 {
150 struct tcf_pedit_parms *parms =
151 container_of(head, struct tcf_pedit_parms, rcu);
152
153 kfree(parms->tcfp_keys_ex);
154 kfree(parms->tcfp_keys);
155
156 kfree(parms);
157 }
158
tcf_pedit_init(struct net * net,struct nlattr * nla,struct nlattr * est,struct tc_action ** a,struct tcf_proto * tp,u32 flags,struct netlink_ext_ack * extack)159 static int tcf_pedit_init(struct net *net, struct nlattr *nla,
160 struct nlattr *est, struct tc_action **a,
161 struct tcf_proto *tp, u32 flags,
162 struct netlink_ext_ack *extack)
163 {
164 struct tc_action_net *tn = net_generic(net, act_pedit_ops.net_id);
165 bool bind = flags & TCA_ACT_FLAGS_BIND;
166 struct tcf_chain *goto_ch = NULL;
167 struct tcf_pedit_parms *oparms, *nparms;
168 struct nlattr *tb[TCA_PEDIT_MAX + 1];
169 struct tc_pedit *parm;
170 struct nlattr *pattr;
171 struct tcf_pedit *p;
172 int ret = 0, err;
173 int i, ksize;
174 u32 index;
175
176 if (!nla) {
177 NL_SET_ERR_MSG_MOD(extack, "Pedit requires attributes to be passed");
178 return -EINVAL;
179 }
180
181 err = nla_parse_nested_deprecated(tb, TCA_PEDIT_MAX, nla,
182 pedit_policy, NULL);
183 if (err < 0)
184 return err;
185
186 pattr = tb[TCA_PEDIT_PARMS];
187 if (!pattr)
188 pattr = tb[TCA_PEDIT_PARMS_EX];
189 if (!pattr) {
190 NL_SET_ERR_MSG_MOD(extack, "Missing required TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute");
191 return -EINVAL;
192 }
193
194 parm = nla_data(pattr);
195
196 index = parm->index;
197 err = tcf_idr_check_alloc(tn, &index, a, bind);
198 if (!err) {
199 ret = tcf_idr_create_from_flags(tn, index, est, a,
200 &act_pedit_ops, bind, flags);
201 if (ret) {
202 tcf_idr_cleanup(tn, index);
203 return ret;
204 }
205 ret = ACT_P_CREATED;
206 } else if (err > 0) {
207 if (bind)
208 return ACT_P_BOUND;
209 if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
210 ret = -EEXIST;
211 goto out_release;
212 }
213 } else {
214 return err;
215 }
216
217 if (!parm->nkeys) {
218 NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
219 ret = -EINVAL;
220 goto out_release;
221 }
222 ksize = parm->nkeys * sizeof(struct tc_pedit_key);
223 if (nla_len(pattr) < sizeof(*parm) + ksize) {
224 NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid");
225 ret = -EINVAL;
226 goto out_release;
227 }
228
229 nparms = kzalloc_obj(*nparms);
230 if (!nparms) {
231 ret = -ENOMEM;
232 goto out_release;
233 }
234
235 nparms->tcfp_keys_ex =
236 tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys, extack);
237 if (IS_ERR(nparms->tcfp_keys_ex)) {
238 ret = PTR_ERR(nparms->tcfp_keys_ex);
239 goto out_free;
240 }
241
242 err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
243 if (err < 0) {
244 ret = err;
245 goto out_free_ex;
246 }
247
248 nparms->tcfp_flags = parm->flags;
249 nparms->tcfp_nkeys = parm->nkeys;
250
251 nparms->tcfp_keys = kmemdup(parm->keys, ksize, GFP_KERNEL);
252 if (!nparms->tcfp_keys) {
253 ret = -ENOMEM;
254 goto put_chain;
255 }
256
257 for (i = 0; i < nparms->tcfp_nkeys; ++i) {
258 u32 offmask = nparms->tcfp_keys[i].offmask;
259 u32 cur = nparms->tcfp_keys[i].off;
260
261 /* The AT option can be added to static offsets in the datapath */
262 if (!offmask && cur % 4) {
263 NL_SET_ERR_MSG_MOD(extack, "Offsets must be on 32bit boundaries");
264 ret = -EINVAL;
265 goto out_free_keys;
266 }
267
268 /* sanitize the shift value for any later use */
269 nparms->tcfp_keys[i].shift = min_t(size_t,
270 BITS_PER_TYPE(int) - 1,
271 nparms->tcfp_keys[i].shift);
272
273 }
274
275 p = to_pedit(*a);
276 nparms->action = parm->action;
277 spin_lock_bh(&p->tcf_lock);
278 goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
279 oparms = rcu_replace_pointer(p->parms, nparms, 1);
280 spin_unlock_bh(&p->tcf_lock);
281
282 if (oparms)
283 call_rcu(&oparms->rcu, tcf_pedit_cleanup_rcu);
284
285 if (goto_ch)
286 tcf_chain_put_by_act(goto_ch);
287
288 return ret;
289
290 out_free_keys:
291 kfree(nparms->tcfp_keys);
292 put_chain:
293 if (goto_ch)
294 tcf_chain_put_by_act(goto_ch);
295 out_free_ex:
296 kfree(nparms->tcfp_keys_ex);
297 out_free:
298 kfree(nparms);
299 out_release:
300 tcf_idr_release(*a, bind);
301 return ret;
302 }
303
tcf_pedit_cleanup(struct tc_action * a)304 static void tcf_pedit_cleanup(struct tc_action *a)
305 {
306 struct tcf_pedit *p = to_pedit(a);
307 struct tcf_pedit_parms *parms;
308
309 parms = rcu_dereference_protected(p->parms, 1);
310
311 if (parms)
312 call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu);
313 }
314
offset_valid(struct sk_buff * skb,int offset,int len)315 static bool offset_valid(struct sk_buff *skb, int offset, int len)
316 {
317 if (offset < -(int)skb_headroom(skb))
318 return false;
319
320 return offset <= (int)skb->len - len;
321 }
322
pedit_l4_skb_offset(struct sk_buff * skb,int * hoffset,const int header_type)323 static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type)
324 {
325 const int noff = skb_network_offset(skb);
326 int ret = -EINVAL;
327 struct iphdr _iph;
328
329 switch (skb->protocol) {
330 case htons(ETH_P_IP): {
331 const struct iphdr *iph = skb_header_pointer(skb, noff, sizeof(_iph), &_iph);
332
333 if (!iph)
334 goto out;
335 if (iph->ihl < 5 || iph->protocol != header_type ||
336 (iph->frag_off & htons(IP_OFFSET)))
337 goto out;
338 *hoffset = noff + iph->ihl * 4;
339 ret = 0;
340 break;
341 }
342 case htons(ETH_P_IPV6):
343 ret = ipv6_find_hdr(skb, hoffset, header_type, NULL, NULL) == header_type ? 0 : -EINVAL;
344 break;
345 }
346 out:
347 return ret;
348 }
349
pedit_skb_hdr_offset(struct sk_buff * skb,enum pedit_header_type htype,int * hoffset)350 static int pedit_skb_hdr_offset(struct sk_buff *skb,
351 enum pedit_header_type htype, int *hoffset)
352 {
353 int ret = -EINVAL;
354 /* 'htype' is validated in the netlink parsing */
355 switch (htype) {
356 case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
357 if (skb_mac_header_was_set(skb)) {
358 *hoffset = skb_mac_offset(skb);
359 ret = 0;
360 }
361 break;
362 case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
363 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
364 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
365 *hoffset = skb_network_offset(skb);
366 ret = 0;
367 break;
368 case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
369 ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_TCP);
370 break;
371 case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
372 ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_UDP);
373 break;
374 default:
375 break;
376 }
377 return ret;
378 }
379
tcf_pedit_act(struct sk_buff * skb,const struct tc_action * a,struct tcf_result * res)380 TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb,
381 const struct tc_action *a,
382 struct tcf_result *res)
383 {
384 enum pedit_header_type htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
385 enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
386 struct tcf_pedit *p = to_pedit(a);
387 struct tcf_pedit_key_ex *tkey_ex;
388 struct tcf_pedit_parms *parms;
389 struct tc_pedit_key *tkey;
390 int i;
391
392 parms = rcu_dereference_bh(p->parms);
393
394 tcf_lastuse_update(&p->tcf_tm);
395 tcf_action_update_bstats(&p->common, skb);
396
397 tkey = parms->tcfp_keys;
398 tkey_ex = parms->tcfp_keys_ex;
399
400 for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
401 int write_offset, write_len;
402 int offset = tkey->off;
403 int hoffset = 0;
404 u32 cur_val, val;
405 u32 *ptr;
406 int rc;
407
408 if (tkey_ex) {
409 htype = tkey_ex->htype;
410 cmd = tkey_ex->cmd;
411
412 tkey_ex++;
413 }
414
415 rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
416 if (rc) {
417 pr_info_ratelimited("tc action pedit unable to extract header offset for header type (0x%x)\n", htype);
418 goto bad;
419 }
420
421 if (tkey->offmask) {
422 u8 *d, _d;
423 int at_offset;
424
425 if (check_add_overflow(hoffset, (int)tkey->at, &at_offset) ||
426 !offset_valid(skb, at_offset, sizeof(_d))) {
427 pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n",
428 hoffset + tkey->at);
429 goto bad;
430 }
431 d = skb_header_pointer(skb, at_offset,
432 sizeof(_d), &_d);
433 if (!d)
434 goto bad;
435
436 offset += (*d & tkey->offmask) >> tkey->shift;
437 if (offset % 4) {
438 pr_info_ratelimited("tc action pedit offset must be on 32 bit boundaries\n");
439 goto bad;
440 }
441 }
442
443 if (check_add_overflow(hoffset, offset, &write_offset)) {
444 pr_info_ratelimited("tc action pedit offset overflow\n");
445 goto bad;
446 }
447
448 if (!offset_valid(skb, write_offset, sizeof(*ptr))) {
449 pr_info_ratelimited("tc action pedit offset %d out of bounds\n",
450 write_offset);
451 goto bad;
452 }
453
454 if (write_offset < 0) {
455 if (skb_cow(skb, -write_offset))
456 goto bad;
457 if (write_offset + (int)sizeof(*ptr) > 0) {
458 if (skb_ensure_writable(skb,
459 min_t(int, skb->len,
460 write_offset + (int)sizeof(*ptr))))
461 goto bad;
462 }
463 } else {
464 if (check_add_overflow(write_offset, (int)sizeof(*ptr),
465 &write_len))
466 goto bad;
467 if (skb_ensure_writable(skb, min_t(int, skb->len,
468 write_len)))
469 goto bad;
470 }
471
472 ptr = (u32 *)(skb->data + write_offset);
473 cur_val = get_unaligned(ptr);
474 /* just do it, baby */
475 switch (cmd) {
476 case TCA_PEDIT_KEY_EX_CMD_SET:
477 val = tkey->val;
478 break;
479 case TCA_PEDIT_KEY_EX_CMD_ADD:
480 val = (cur_val + tkey->val) & ~tkey->mask;
481 break;
482 default:
483 pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd);
484 goto bad;
485 }
486
487 put_unaligned((cur_val & tkey->mask) ^ val, ptr);
488 }
489
490 goto done;
491
492 bad:
493 tcf_action_inc_overlimit_qstats(&p->common);
494 done:
495 return parms->action;
496 }
497
tcf_pedit_stats_update(struct tc_action * a,u64 bytes,u64 packets,u64 drops,u64 lastuse,bool hw)498 static void tcf_pedit_stats_update(struct tc_action *a, u64 bytes, u64 packets,
499 u64 drops, u64 lastuse, bool hw)
500 {
501 struct tcf_pedit *d = to_pedit(a);
502 struct tcf_t *tm = &d->tcf_tm;
503
504 tcf_action_update_stats(a, bytes, packets, drops, hw);
505 tm->lastuse = max_t(u64, tm->lastuse, lastuse);
506 }
507
tcf_pedit_dump(struct sk_buff * skb,struct tc_action * a,int bind,int ref)508 static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
509 int bind, int ref)
510 {
511 unsigned char *b = skb_tail_pointer(skb);
512 const struct tcf_pedit *p = to_pedit(a);
513 const struct tcf_pedit_parms *parms;
514 struct tc_pedit *opt;
515 struct tcf_t t;
516 int s;
517
518 rcu_read_lock();
519 parms = rcu_dereference(p->parms);
520 s = struct_size(opt, keys, parms->tcfp_nkeys);
521
522 opt = kzalloc(s, GFP_ATOMIC);
523 if (unlikely(!opt)) {
524 rcu_read_unlock();
525 return -ENOBUFS;
526 }
527 opt->nkeys = parms->tcfp_nkeys;
528
529 memcpy(opt->keys, parms->tcfp_keys,
530 flex_array_size(opt, keys, parms->tcfp_nkeys));
531 opt->index = p->tcf_index;
532 opt->flags = parms->tcfp_flags;
533 opt->action = parms->action;
534 opt->refcnt = refcount_read(&p->tcf_refcnt) - ref;
535 opt->bindcnt = atomic_read(&p->tcf_bindcnt) - bind;
536
537 if (parms->tcfp_keys_ex) {
538 if (tcf_pedit_key_ex_dump(skb, parms->tcfp_keys_ex,
539 parms->tcfp_nkeys))
540 goto nla_put_failure;
541
542 if (nla_put(skb, TCA_PEDIT_PARMS_EX, s, opt))
543 goto nla_put_failure;
544 } else {
545 if (nla_put(skb, TCA_PEDIT_PARMS, s, opt))
546 goto nla_put_failure;
547 }
548
549 tcf_tm_dump(&t, &p->tcf_tm);
550 if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))
551 goto nla_put_failure;
552 rcu_read_unlock();
553
554 kfree(opt);
555 return skb->len;
556
557 nla_put_failure:
558 rcu_read_unlock();
559 nlmsg_trim(skb, b);
560 kfree(opt);
561 return -1;
562 }
563
tcf_pedit_offload_act_setup(struct tc_action * act,void * entry_data,u32 * index_inc,bool bind,struct netlink_ext_ack * extack)564 static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data,
565 u32 *index_inc, bool bind,
566 struct netlink_ext_ack *extack)
567 {
568 if (bind) {
569 struct flow_action_entry *entry = entry_data;
570 int nkeys = tcf_pedit_nkeys_locked(act);
571 int k;
572
573 /* If the required keys exceed the remaining capacity return
574 * -ENOSPC to abort the offload and fallback to software.
575 */
576 if (nkeys > *index_inc) {
577 NL_SET_ERR_MSG_MOD(extack, "Not enough space to offload all pedit keys");
578 return -ENOSPC;
579 }
580
581 for (k = 0; k < nkeys; k++) {
582 switch (tcf_pedit_cmd(act, k)) {
583 case TCA_PEDIT_KEY_EX_CMD_SET:
584 entry->id = FLOW_ACTION_MANGLE;
585 break;
586 case TCA_PEDIT_KEY_EX_CMD_ADD:
587 entry->id = FLOW_ACTION_ADD;
588 break;
589 default:
590 NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload");
591 return -EOPNOTSUPP;
592 }
593 entry->mangle.htype = tcf_pedit_htype(act, k);
594 entry->mangle.mask = tcf_pedit_mask(act, k);
595 entry->mangle.val = tcf_pedit_val(act, k);
596 entry->mangle.offset = tcf_pedit_offset(act, k);
597 entry->hw_stats = tc_act_hw_stats(act->hw_stats);
598 entry++;
599 }
600 *index_inc = k;
601 } else {
602 struct flow_offload_action *fl_action = entry_data;
603 u32 cmd = tcf_pedit_cmd(act, 0);
604 int k;
605
606 switch (cmd) {
607 case TCA_PEDIT_KEY_EX_CMD_SET:
608 fl_action->id = FLOW_ACTION_MANGLE;
609 break;
610 case TCA_PEDIT_KEY_EX_CMD_ADD:
611 fl_action->id = FLOW_ACTION_ADD;
612 break;
613 default:
614 NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload");
615 return -EOPNOTSUPP;
616 }
617
618 for (k = 1; k < tcf_pedit_nkeys_locked(act); k++) {
619 if (cmd != tcf_pedit_cmd(act, k)) {
620 NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload");
621 return -EOPNOTSUPP;
622 }
623 }
624 }
625
626 return 0;
627 }
628
tcf_pedit_get_fill_size(const struct tc_action * act)629 static size_t tcf_pedit_get_fill_size(const struct tc_action *act)
630 {
631 const struct tcf_pedit_parms *parms;
632 size_t size;
633
634 rcu_read_lock();
635 parms = rcu_dereference(to_pedit(act)->parms);
636 size = nla_total_size(struct_size_t(struct tc_pedit, keys,
637 parms->tcfp_nkeys));
638 if (parms->tcfp_keys_ex) {
639 /* TCA_PEDIT_KEYS_EX, holding one TCA_PEDIT_KEY_EX nest with a
640 * HTYPE and a CMD attribute per key.
641 */
642 size += nla_total_size(0)
643 + parms->tcfp_nkeys * (nla_total_size(0)
644 + nla_total_size(sizeof(u16))
645 + nla_total_size(sizeof(u16)));
646 }
647 rcu_read_unlock();
648
649 return size;
650 }
651
652 static struct tc_action_ops act_pedit_ops = {
653 .kind = "pedit",
654 .id = TCA_ID_PEDIT,
655 .owner = THIS_MODULE,
656 .act = tcf_pedit_act,
657 .stats_update = tcf_pedit_stats_update,
658 .dump = tcf_pedit_dump,
659 .cleanup = tcf_pedit_cleanup,
660 .init = tcf_pedit_init,
661 .get_fill_size = tcf_pedit_get_fill_size,
662 .offload_act_setup = tcf_pedit_offload_act_setup,
663 .size = sizeof(struct tcf_pedit),
664 };
665 MODULE_ALIAS_NET_ACT("pedit");
666
pedit_init_net(struct net * net)667 static __net_init int pedit_init_net(struct net *net)
668 {
669 struct tc_action_net *tn = net_generic(net, act_pedit_ops.net_id);
670
671 return tc_action_net_init(net, tn, &act_pedit_ops);
672 }
673
pedit_exit_net(struct list_head * net_list)674 static void __net_exit pedit_exit_net(struct list_head *net_list)
675 {
676 tc_action_net_exit(net_list, act_pedit_ops.net_id);
677 }
678
679 static struct pernet_operations pedit_net_ops = {
680 .init = pedit_init_net,
681 .exit_batch = pedit_exit_net,
682 .id = &act_pedit_ops.net_id,
683 .size = sizeof(struct tc_action_net),
684 };
685
686 MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
687 MODULE_DESCRIPTION("Generic Packet Editor actions");
688 MODULE_LICENSE("GPL");
689
pedit_init_module(void)690 static int __init pedit_init_module(void)
691 {
692 return tcf_register_action(&act_pedit_ops, &pedit_net_ops);
693 }
694
pedit_cleanup_module(void)695 static void __exit pedit_cleanup_module(void)
696 {
697 tcf_unregister_action(&act_pedit_ops, &pedit_net_ops);
698 }
699
700 module_init(pedit_init_module);
701 module_exit(pedit_cleanup_module);
702