Merge tag 'gpio-v5.3-5' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux...
[sfrench/cifs-2.6.git] / net / sched / act_csum.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Checksum updating actions
4  *
5  * Copyright (c) 2010 Gregoire Baron <baronchon@n7mm.org>
6  */
7
8 #include <linux/types.h>
9 #include <linux/init.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/spinlock.h>
13
14 #include <linux/netlink.h>
15 #include <net/netlink.h>
16 #include <linux/rtnetlink.h>
17
18 #include <linux/skbuff.h>
19
20 #include <net/ip.h>
21 #include <net/ipv6.h>
22 #include <net/icmp.h>
23 #include <linux/icmpv6.h>
24 #include <linux/igmp.h>
25 #include <net/tcp.h>
26 #include <net/udp.h>
27 #include <net/ip6_checksum.h>
28 #include <net/sctp/checksum.h>
29
30 #include <net/act_api.h>
31 #include <net/pkt_cls.h>
32
33 #include <linux/tc_act/tc_csum.h>
34 #include <net/tc_act/tc_csum.h>
35
36 static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = {
37         [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), },
38 };
39
40 static unsigned int csum_net_id;
41 static struct tc_action_ops act_csum_ops;
42
43 static int tcf_csum_init(struct net *net, struct nlattr *nla,
44                          struct nlattr *est, struct tc_action **a, int ovr,
45                          int bind, bool rtnl_held, struct tcf_proto *tp,
46                          struct netlink_ext_ack *extack)
47 {
48         struct tc_action_net *tn = net_generic(net, csum_net_id);
49         struct tcf_csum_params *params_new;
50         struct nlattr *tb[TCA_CSUM_MAX + 1];
51         struct tcf_chain *goto_ch = NULL;
52         struct tc_csum *parm;
53         struct tcf_csum *p;
54         int ret = 0, err;
55         u32 index;
56
57         if (nla == NULL)
58                 return -EINVAL;
59
60         err = nla_parse_nested_deprecated(tb, TCA_CSUM_MAX, nla, csum_policy,
61                                           NULL);
62         if (err < 0)
63                 return err;
64
65         if (tb[TCA_CSUM_PARMS] == NULL)
66                 return -EINVAL;
67         parm = nla_data(tb[TCA_CSUM_PARMS]);
68         index = parm->index;
69         err = tcf_idr_check_alloc(tn, &index, a, bind);
70         if (!err) {
71                 ret = tcf_idr_create(tn, index, est, a,
72                                      &act_csum_ops, bind, true);
73                 if (ret) {
74                         tcf_idr_cleanup(tn, index);
75                         return ret;
76                 }
77                 ret = ACT_P_CREATED;
78         } else if (err > 0) {
79                 if (bind)/* dont override defaults */
80                         return 0;
81                 if (!ovr) {
82                         tcf_idr_release(*a, bind);
83                         return -EEXIST;
84                 }
85         } else {
86                 return err;
87         }
88
89         err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
90         if (err < 0)
91                 goto release_idr;
92
93         p = to_tcf_csum(*a);
94
95         params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
96         if (unlikely(!params_new)) {
97                 err = -ENOMEM;
98                 goto put_chain;
99         }
100         params_new->update_flags = parm->update_flags;
101
102         spin_lock_bh(&p->tcf_lock);
103         goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
104         rcu_swap_protected(p->params, params_new,
105                            lockdep_is_held(&p->tcf_lock));
106         spin_unlock_bh(&p->tcf_lock);
107
108         if (goto_ch)
109                 tcf_chain_put_by_act(goto_ch);
110         if (params_new)
111                 kfree_rcu(params_new, rcu);
112
113         if (ret == ACT_P_CREATED)
114                 tcf_idr_insert(tn, *a);
115
116         return ret;
117 put_chain:
118         if (goto_ch)
119                 tcf_chain_put_by_act(goto_ch);
120 release_idr:
121         tcf_idr_release(*a, bind);
122         return err;
123 }
124
125 /**
126  * tcf_csum_skb_nextlayer - Get next layer pointer
127  * @skb: sk_buff to use
128  * @ihl: previous summed headers length
129  * @ipl: complete packet length
130  * @jhl: next header length
131  *
132  * Check the expected next layer availability in the specified sk_buff.
133  * Return the next layer pointer if pass, NULL otherwise.
134  */
135 static void *tcf_csum_skb_nextlayer(struct sk_buff *skb,
136                                     unsigned int ihl, unsigned int ipl,
137                                     unsigned int jhl)
138 {
139         int ntkoff = skb_network_offset(skb);
140         int hl = ihl + jhl;
141
142         if (!pskb_may_pull(skb, ipl + ntkoff) || (ipl < hl) ||
143             skb_try_make_writable(skb, hl + ntkoff))
144                 return NULL;
145         else
146                 return (void *)(skb_network_header(skb) + ihl);
147 }
148
149 static int tcf_csum_ipv4_icmp(struct sk_buff *skb, unsigned int ihl,
150                               unsigned int ipl)
151 {
152         struct icmphdr *icmph;
153
154         icmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmph));
155         if (icmph == NULL)
156                 return 0;
157
158         icmph->checksum = 0;
159         skb->csum = csum_partial(icmph, ipl - ihl, 0);
160         icmph->checksum = csum_fold(skb->csum);
161
162         skb->ip_summed = CHECKSUM_NONE;
163
164         return 1;
165 }
166
167 static int tcf_csum_ipv4_igmp(struct sk_buff *skb,
168                               unsigned int ihl, unsigned int ipl)
169 {
170         struct igmphdr *igmph;
171
172         igmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*igmph));
173         if (igmph == NULL)
174                 return 0;
175
176         igmph->csum = 0;
177         skb->csum = csum_partial(igmph, ipl - ihl, 0);
178         igmph->csum = csum_fold(skb->csum);
179
180         skb->ip_summed = CHECKSUM_NONE;
181
182         return 1;
183 }
184
185 static int tcf_csum_ipv6_icmp(struct sk_buff *skb, unsigned int ihl,
186                               unsigned int ipl)
187 {
188         struct icmp6hdr *icmp6h;
189         const struct ipv6hdr *ip6h;
190
191         icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h));
192         if (icmp6h == NULL)
193                 return 0;
194
195         ip6h = ipv6_hdr(skb);
196         icmp6h->icmp6_cksum = 0;
197         skb->csum = csum_partial(icmp6h, ipl - ihl, 0);
198         icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
199                                               ipl - ihl, IPPROTO_ICMPV6,
200                                               skb->csum);
201
202         skb->ip_summed = CHECKSUM_NONE;
203
204         return 1;
205 }
206
207 static int tcf_csum_ipv4_tcp(struct sk_buff *skb, unsigned int ihl,
208                              unsigned int ipl)
209 {
210         struct tcphdr *tcph;
211         const struct iphdr *iph;
212
213         if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
214                 return 1;
215
216         tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
217         if (tcph == NULL)
218                 return 0;
219
220         iph = ip_hdr(skb);
221         tcph->check = 0;
222         skb->csum = csum_partial(tcph, ipl - ihl, 0);
223         tcph->check = tcp_v4_check(ipl - ihl,
224                                    iph->saddr, iph->daddr, skb->csum);
225
226         skb->ip_summed = CHECKSUM_NONE;
227
228         return 1;
229 }
230
231 static int tcf_csum_ipv6_tcp(struct sk_buff *skb, unsigned int ihl,
232                              unsigned int ipl)
233 {
234         struct tcphdr *tcph;
235         const struct ipv6hdr *ip6h;
236
237         if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
238                 return 1;
239
240         tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
241         if (tcph == NULL)
242                 return 0;
243
244         ip6h = ipv6_hdr(skb);
245         tcph->check = 0;
246         skb->csum = csum_partial(tcph, ipl - ihl, 0);
247         tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
248                                       ipl - ihl, IPPROTO_TCP,
249                                       skb->csum);
250
251         skb->ip_summed = CHECKSUM_NONE;
252
253         return 1;
254 }
255
256 static int tcf_csum_ipv4_udp(struct sk_buff *skb, unsigned int ihl,
257                              unsigned int ipl, int udplite)
258 {
259         struct udphdr *udph;
260         const struct iphdr *iph;
261         u16 ul;
262
263         if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
264                 return 1;
265
266         /*
267          * Support both UDP and UDPLITE checksum algorithms, Don't use
268          * udph->len to get the real length without any protocol check,
269          * UDPLITE uses udph->len for another thing,
270          * Use iph->tot_len, or just ipl.
271          */
272
273         udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph));
274         if (udph == NULL)
275                 return 0;
276
277         iph = ip_hdr(skb);
278         ul = ntohs(udph->len);
279
280         if (udplite || udph->check) {
281
282                 udph->check = 0;
283
284                 if (udplite) {
285                         if (ul == 0)
286                                 skb->csum = csum_partial(udph, ipl - ihl, 0);
287                         else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl))
288                                 skb->csum = csum_partial(udph, ul, 0);
289                         else
290                                 goto ignore_obscure_skb;
291                 } else {
292                         if (ul != ipl - ihl)
293                                 goto ignore_obscure_skb;
294
295                         skb->csum = csum_partial(udph, ul, 0);
296                 }
297
298                 udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
299                                                 ul, iph->protocol,
300                                                 skb->csum);
301
302                 if (!udph->check)
303                         udph->check = CSUM_MANGLED_0;
304         }
305
306         skb->ip_summed = CHECKSUM_NONE;
307
308 ignore_obscure_skb:
309         return 1;
310 }
311
312 static int tcf_csum_ipv6_udp(struct sk_buff *skb, unsigned int ihl,
313                              unsigned int ipl, int udplite)
314 {
315         struct udphdr *udph;
316         const struct ipv6hdr *ip6h;
317         u16 ul;
318
319         if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
320                 return 1;
321
322         /*
323          * Support both UDP and UDPLITE checksum algorithms, Don't use
324          * udph->len to get the real length without any protocol check,
325          * UDPLITE uses udph->len for another thing,
326          * Use ip6h->payload_len + sizeof(*ip6h) ... , or just ipl.
327          */
328
329         udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph));
330         if (udph == NULL)
331                 return 0;
332
333         ip6h = ipv6_hdr(skb);
334         ul = ntohs(udph->len);
335
336         udph->check = 0;
337
338         if (udplite) {
339                 if (ul == 0)
340                         skb->csum = csum_partial(udph, ipl - ihl, 0);
341
342                 else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl))
343                         skb->csum = csum_partial(udph, ul, 0);
344
345                 else
346                         goto ignore_obscure_skb;
347         } else {
348                 if (ul != ipl - ihl)
349                         goto ignore_obscure_skb;
350
351                 skb->csum = csum_partial(udph, ul, 0);
352         }
353
354         udph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, ul,
355                                       udplite ? IPPROTO_UDPLITE : IPPROTO_UDP,
356                                       skb->csum);
357
358         if (!udph->check)
359                 udph->check = CSUM_MANGLED_0;
360
361         skb->ip_summed = CHECKSUM_NONE;
362
363 ignore_obscure_skb:
364         return 1;
365 }
366
367 static int tcf_csum_sctp(struct sk_buff *skb, unsigned int ihl,
368                          unsigned int ipl)
369 {
370         struct sctphdr *sctph;
371
372         if (skb_is_gso(skb) && skb_is_gso_sctp(skb))
373                 return 1;
374
375         sctph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*sctph));
376         if (!sctph)
377                 return 0;
378
379         sctph->checksum = sctp_compute_cksum(skb,
380                                              skb_network_offset(skb) + ihl);
381         skb->ip_summed = CHECKSUM_NONE;
382         skb->csum_not_inet = 0;
383
384         return 1;
385 }
386
387 static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
388 {
389         const struct iphdr *iph;
390         int ntkoff;
391
392         ntkoff = skb_network_offset(skb);
393
394         if (!pskb_may_pull(skb, sizeof(*iph) + ntkoff))
395                 goto fail;
396
397         iph = ip_hdr(skb);
398
399         switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
400         case IPPROTO_ICMP:
401                 if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP)
402                         if (!tcf_csum_ipv4_icmp(skb, iph->ihl * 4,
403                                                 ntohs(iph->tot_len)))
404                                 goto fail;
405                 break;
406         case IPPROTO_IGMP:
407                 if (update_flags & TCA_CSUM_UPDATE_FLAG_IGMP)
408                         if (!tcf_csum_ipv4_igmp(skb, iph->ihl * 4,
409                                                 ntohs(iph->tot_len)))
410                                 goto fail;
411                 break;
412         case IPPROTO_TCP:
413                 if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP)
414                         if (!tcf_csum_ipv4_tcp(skb, iph->ihl * 4,
415                                                ntohs(iph->tot_len)))
416                                 goto fail;
417                 break;
418         case IPPROTO_UDP:
419                 if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP)
420                         if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4,
421                                                ntohs(iph->tot_len), 0))
422                                 goto fail;
423                 break;
424         case IPPROTO_UDPLITE:
425                 if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE)
426                         if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4,
427                                                ntohs(iph->tot_len), 1))
428                                 goto fail;
429                 break;
430         case IPPROTO_SCTP:
431                 if ((update_flags & TCA_CSUM_UPDATE_FLAG_SCTP) &&
432                     !tcf_csum_sctp(skb, iph->ihl * 4, ntohs(iph->tot_len)))
433                         goto fail;
434                 break;
435         }
436
437         if (update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) {
438                 if (skb_try_make_writable(skb, sizeof(*iph) + ntkoff))
439                         goto fail;
440
441                 ip_send_check(ip_hdr(skb));
442         }
443
444         return 1;
445
446 fail:
447         return 0;
448 }
449
450 static int tcf_csum_ipv6_hopopts(struct ipv6_opt_hdr *ip6xh, unsigned int ixhl,
451                                  unsigned int *pl)
452 {
453         int off, len, optlen;
454         unsigned char *xh = (void *)ip6xh;
455
456         off = sizeof(*ip6xh);
457         len = ixhl - off;
458
459         while (len > 1) {
460                 switch (xh[off]) {
461                 case IPV6_TLV_PAD1:
462                         optlen = 1;
463                         break;
464                 case IPV6_TLV_JUMBO:
465                         optlen = xh[off + 1] + 2;
466                         if (optlen != 6 || len < 6 || (off & 3) != 2)
467                                 /* wrong jumbo option length/alignment */
468                                 return 0;
469                         *pl = ntohl(*(__be32 *)(xh + off + 2));
470                         goto done;
471                 default:
472                         optlen = xh[off + 1] + 2;
473                         if (optlen > len)
474                                 /* ignore obscure options */
475                                 goto done;
476                         break;
477                 }
478                 off += optlen;
479                 len -= optlen;
480         }
481
482 done:
483         return 1;
484 }
485
486 static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags)
487 {
488         struct ipv6hdr *ip6h;
489         struct ipv6_opt_hdr *ip6xh;
490         unsigned int hl, ixhl;
491         unsigned int pl;
492         int ntkoff;
493         u8 nexthdr;
494
495         ntkoff = skb_network_offset(skb);
496
497         hl = sizeof(*ip6h);
498
499         if (!pskb_may_pull(skb, hl + ntkoff))
500                 goto fail;
501
502         ip6h = ipv6_hdr(skb);
503
504         pl = ntohs(ip6h->payload_len);
505         nexthdr = ip6h->nexthdr;
506
507         do {
508                 switch (nexthdr) {
509                 case NEXTHDR_FRAGMENT:
510                         goto ignore_skb;
511                 case NEXTHDR_ROUTING:
512                 case NEXTHDR_HOP:
513                 case NEXTHDR_DEST:
514                         if (!pskb_may_pull(skb, hl + sizeof(*ip6xh) + ntkoff))
515                                 goto fail;
516                         ip6xh = (void *)(skb_network_header(skb) + hl);
517                         ixhl = ipv6_optlen(ip6xh);
518                         if (!pskb_may_pull(skb, hl + ixhl + ntkoff))
519                                 goto fail;
520                         ip6xh = (void *)(skb_network_header(skb) + hl);
521                         if ((nexthdr == NEXTHDR_HOP) &&
522                             !(tcf_csum_ipv6_hopopts(ip6xh, ixhl, &pl)))
523                                 goto fail;
524                         nexthdr = ip6xh->nexthdr;
525                         hl += ixhl;
526                         break;
527                 case IPPROTO_ICMPV6:
528                         if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP)
529                                 if (!tcf_csum_ipv6_icmp(skb,
530                                                         hl, pl + sizeof(*ip6h)))
531                                         goto fail;
532                         goto done;
533                 case IPPROTO_TCP:
534                         if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP)
535                                 if (!tcf_csum_ipv6_tcp(skb,
536                                                        hl, pl + sizeof(*ip6h)))
537                                         goto fail;
538                         goto done;
539                 case IPPROTO_UDP:
540                         if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP)
541                                 if (!tcf_csum_ipv6_udp(skb, hl,
542                                                        pl + sizeof(*ip6h), 0))
543                                         goto fail;
544                         goto done;
545                 case IPPROTO_UDPLITE:
546                         if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE)
547                                 if (!tcf_csum_ipv6_udp(skb, hl,
548                                                        pl + sizeof(*ip6h), 1))
549                                         goto fail;
550                         goto done;
551                 case IPPROTO_SCTP:
552                         if ((update_flags & TCA_CSUM_UPDATE_FLAG_SCTP) &&
553                             !tcf_csum_sctp(skb, hl, pl + sizeof(*ip6h)))
554                                 goto fail;
555                         goto done;
556                 default:
557                         goto ignore_skb;
558                 }
559         } while (pskb_may_pull(skb, hl + 1 + ntkoff));
560
561 done:
562 ignore_skb:
563         return 1;
564
565 fail:
566         return 0;
567 }
568
569 static int tcf_csum_act(struct sk_buff *skb, const struct tc_action *a,
570                         struct tcf_result *res)
571 {
572         struct tcf_csum *p = to_tcf_csum(a);
573         bool orig_vlan_tag_present = false;
574         unsigned int vlan_hdr_count = 0;
575         struct tcf_csum_params *params;
576         u32 update_flags;
577         __be16 protocol;
578         int action;
579
580         params = rcu_dereference_bh(p->params);
581
582         tcf_lastuse_update(&p->tcf_tm);
583         bstats_cpu_update(this_cpu_ptr(p->common.cpu_bstats), skb);
584
585         action = READ_ONCE(p->tcf_action);
586         if (unlikely(action == TC_ACT_SHOT))
587                 goto drop;
588
589         update_flags = params->update_flags;
590         protocol = tc_skb_protocol(skb);
591 again:
592         switch (protocol) {
593         case cpu_to_be16(ETH_P_IP):
594                 if (!tcf_csum_ipv4(skb, update_flags))
595                         goto drop;
596                 break;
597         case cpu_to_be16(ETH_P_IPV6):
598                 if (!tcf_csum_ipv6(skb, update_flags))
599                         goto drop;
600                 break;
601         case cpu_to_be16(ETH_P_8021AD): /* fall through */
602         case cpu_to_be16(ETH_P_8021Q):
603                 if (skb_vlan_tag_present(skb) && !orig_vlan_tag_present) {
604                         protocol = skb->protocol;
605                         orig_vlan_tag_present = true;
606                 } else {
607                         struct vlan_hdr *vlan = (struct vlan_hdr *)skb->data;
608
609                         protocol = vlan->h_vlan_encapsulated_proto;
610                         skb_pull(skb, VLAN_HLEN);
611                         skb_reset_network_header(skb);
612                         vlan_hdr_count++;
613                 }
614                 goto again;
615         }
616
617 out:
618         /* Restore the skb for the pulled VLAN tags */
619         while (vlan_hdr_count--) {
620                 skb_push(skb, VLAN_HLEN);
621                 skb_reset_network_header(skb);
622         }
623
624         return action;
625
626 drop:
627         qstats_drop_inc(this_cpu_ptr(p->common.cpu_qstats));
628         action = TC_ACT_SHOT;
629         goto out;
630 }
631
632 static int tcf_csum_dump(struct sk_buff *skb, struct tc_action *a, int bind,
633                          int ref)
634 {
635         unsigned char *b = skb_tail_pointer(skb);
636         struct tcf_csum *p = to_tcf_csum(a);
637         struct tcf_csum_params *params;
638         struct tc_csum opt = {
639                 .index   = p->tcf_index,
640                 .refcnt  = refcount_read(&p->tcf_refcnt) - ref,
641                 .bindcnt = atomic_read(&p->tcf_bindcnt) - bind,
642         };
643         struct tcf_t t;
644
645         spin_lock_bh(&p->tcf_lock);
646         params = rcu_dereference_protected(p->params,
647                                            lockdep_is_held(&p->tcf_lock));
648         opt.action = p->tcf_action;
649         opt.update_flags = params->update_flags;
650
651         if (nla_put(skb, TCA_CSUM_PARMS, sizeof(opt), &opt))
652                 goto nla_put_failure;
653
654         tcf_tm_dump(&t, &p->tcf_tm);
655         if (nla_put_64bit(skb, TCA_CSUM_TM, sizeof(t), &t, TCA_CSUM_PAD))
656                 goto nla_put_failure;
657         spin_unlock_bh(&p->tcf_lock);
658
659         return skb->len;
660
661 nla_put_failure:
662         spin_unlock_bh(&p->tcf_lock);
663         nlmsg_trim(skb, b);
664         return -1;
665 }
666
667 static void tcf_csum_cleanup(struct tc_action *a)
668 {
669         struct tcf_csum *p = to_tcf_csum(a);
670         struct tcf_csum_params *params;
671
672         params = rcu_dereference_protected(p->params, 1);
673         if (params)
674                 kfree_rcu(params, rcu);
675 }
676
677 static int tcf_csum_walker(struct net *net, struct sk_buff *skb,
678                            struct netlink_callback *cb, int type,
679                            const struct tc_action_ops *ops,
680                            struct netlink_ext_ack *extack)
681 {
682         struct tc_action_net *tn = net_generic(net, csum_net_id);
683
684         return tcf_generic_walker(tn, skb, cb, type, ops, extack);
685 }
686
687 static int tcf_csum_search(struct net *net, struct tc_action **a, u32 index)
688 {
689         struct tc_action_net *tn = net_generic(net, csum_net_id);
690
691         return tcf_idr_search(tn, a, index);
692 }
693
694 static size_t tcf_csum_get_fill_size(const struct tc_action *act)
695 {
696         return nla_total_size(sizeof(struct tc_csum));
697 }
698
699 static struct tc_action_ops act_csum_ops = {
700         .kind           = "csum",
701         .id             = TCA_ID_CSUM,
702         .owner          = THIS_MODULE,
703         .act            = tcf_csum_act,
704         .dump           = tcf_csum_dump,
705         .init           = tcf_csum_init,
706         .cleanup        = tcf_csum_cleanup,
707         .walk           = tcf_csum_walker,
708         .lookup         = tcf_csum_search,
709         .get_fill_size  = tcf_csum_get_fill_size,
710         .size           = sizeof(struct tcf_csum),
711 };
712
713 static __net_init int csum_init_net(struct net *net)
714 {
715         struct tc_action_net *tn = net_generic(net, csum_net_id);
716
717         return tc_action_net_init(net, tn, &act_csum_ops);
718 }
719
720 static void __net_exit csum_exit_net(struct list_head *net_list)
721 {
722         tc_action_net_exit(net_list, csum_net_id);
723 }
724
725 static struct pernet_operations csum_net_ops = {
726         .init = csum_init_net,
727         .exit_batch = csum_exit_net,
728         .id   = &csum_net_id,
729         .size = sizeof(struct tc_action_net),
730 };
731
732 MODULE_DESCRIPTION("Checksum updating actions");
733 MODULE_LICENSE("GPL");
734
735 static int __init csum_init_module(void)
736 {
737         return tcf_register_action(&act_csum_ops, &csum_net_ops);
738 }
739
740 static void __exit csum_cleanup_module(void)
741 {
742         tcf_unregister_action(&act_csum_ops, &csum_net_ops);
743 }
744
745 module_init(csum_init_module);
746 module_exit(csum_cleanup_module);