18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Checksum updating actions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010 Gregoire Baron <baronchon@n7mm.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/netlink.h> 158c2ecf20Sopenharmony_ci#include <net/netlink.h> 168c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <net/ip.h> 218c2ecf20Sopenharmony_ci#include <net/ipv6.h> 228c2ecf20Sopenharmony_ci#include <net/icmp.h> 238c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 248c2ecf20Sopenharmony_ci#include <linux/igmp.h> 258c2ecf20Sopenharmony_ci#include <net/tcp.h> 268c2ecf20Sopenharmony_ci#include <net/udp.h> 278c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 288c2ecf20Sopenharmony_ci#include <net/sctp/checksum.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <net/act_api.h> 318c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/tc_act/tc_csum.h> 348c2ecf20Sopenharmony_ci#include <net/tc_act/tc_csum.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { 378c2ecf20Sopenharmony_ci [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), }, 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic unsigned int csum_net_id; 418c2ecf20Sopenharmony_cistatic struct tc_action_ops act_csum_ops; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int tcf_csum_init(struct net *net, struct nlattr *nla, 448c2ecf20Sopenharmony_ci struct nlattr *est, struct tc_action **a, int ovr, 458c2ecf20Sopenharmony_ci int bind, bool rtnl_held, struct tcf_proto *tp, 468c2ecf20Sopenharmony_ci u32 flags, struct netlink_ext_ack *extack) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct tc_action_net *tn = net_generic(net, csum_net_id); 498c2ecf20Sopenharmony_ci struct tcf_csum_params *params_new; 508c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_CSUM_MAX + 1]; 518c2ecf20Sopenharmony_ci struct tcf_chain *goto_ch = NULL; 528c2ecf20Sopenharmony_ci struct tc_csum *parm; 538c2ecf20Sopenharmony_ci struct tcf_csum *p; 548c2ecf20Sopenharmony_ci int ret = 0, err; 558c2ecf20Sopenharmony_ci u32 index; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (nla == NULL) 588c2ecf20Sopenharmony_ci return -EINVAL; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_CSUM_MAX, nla, csum_policy, 618c2ecf20Sopenharmony_ci NULL); 628c2ecf20Sopenharmony_ci if (err < 0) 638c2ecf20Sopenharmony_ci return err; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (tb[TCA_CSUM_PARMS] == NULL) 668c2ecf20Sopenharmony_ci return -EINVAL; 678c2ecf20Sopenharmony_ci parm = nla_data(tb[TCA_CSUM_PARMS]); 688c2ecf20Sopenharmony_ci index = parm->index; 698c2ecf20Sopenharmony_ci err = tcf_idr_check_alloc(tn, &index, a, bind); 708c2ecf20Sopenharmony_ci if (!err) { 718c2ecf20Sopenharmony_ci ret = tcf_idr_create_from_flags(tn, index, est, a, 728c2ecf20Sopenharmony_ci &act_csum_ops, bind, flags); 738c2ecf20Sopenharmony_ci if (ret) { 748c2ecf20Sopenharmony_ci tcf_idr_cleanup(tn, index); 758c2ecf20Sopenharmony_ci return ret; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci ret = ACT_P_CREATED; 788c2ecf20Sopenharmony_ci } else if (err > 0) { 798c2ecf20Sopenharmony_ci if (bind)/* dont override defaults */ 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci if (!ovr) { 828c2ecf20Sopenharmony_ci tcf_idr_release(*a, bind); 838c2ecf20Sopenharmony_ci return -EEXIST; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci } else { 868c2ecf20Sopenharmony_ci return err; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 908c2ecf20Sopenharmony_ci if (err < 0) 918c2ecf20Sopenharmony_ci goto release_idr; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci p = to_tcf_csum(*a); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); 968c2ecf20Sopenharmony_ci if (unlikely(!params_new)) { 978c2ecf20Sopenharmony_ci err = -ENOMEM; 988c2ecf20Sopenharmony_ci goto put_chain; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci params_new->update_flags = parm->update_flags; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci spin_lock_bh(&p->tcf_lock); 1038c2ecf20Sopenharmony_ci goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); 1048c2ecf20Sopenharmony_ci params_new = rcu_replace_pointer(p->params, params_new, 1058c2ecf20Sopenharmony_ci lockdep_is_held(&p->tcf_lock)); 1068c2ecf20Sopenharmony_ci spin_unlock_bh(&p->tcf_lock); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (goto_ch) 1098c2ecf20Sopenharmony_ci tcf_chain_put_by_act(goto_ch); 1108c2ecf20Sopenharmony_ci if (params_new) 1118c2ecf20Sopenharmony_ci kfree_rcu(params_new, rcu); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ciput_chain: 1158c2ecf20Sopenharmony_ci if (goto_ch) 1168c2ecf20Sopenharmony_ci tcf_chain_put_by_act(goto_ch); 1178c2ecf20Sopenharmony_cirelease_idr: 1188c2ecf20Sopenharmony_ci tcf_idr_release(*a, bind); 1198c2ecf20Sopenharmony_ci return err; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/** 1238c2ecf20Sopenharmony_ci * tcf_csum_skb_nextlayer - Get next layer pointer 1248c2ecf20Sopenharmony_ci * @skb: sk_buff to use 1258c2ecf20Sopenharmony_ci * @ihl: previous summed headers length 1268c2ecf20Sopenharmony_ci * @ipl: complete packet length 1278c2ecf20Sopenharmony_ci * @jhl: next header length 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Check the expected next layer availability in the specified sk_buff. 1308c2ecf20Sopenharmony_ci * Return the next layer pointer if pass, NULL otherwise. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic void *tcf_csum_skb_nextlayer(struct sk_buff *skb, 1338c2ecf20Sopenharmony_ci unsigned int ihl, unsigned int ipl, 1348c2ecf20Sopenharmony_ci unsigned int jhl) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int ntkoff = skb_network_offset(skb); 1378c2ecf20Sopenharmony_ci int hl = ihl + jhl; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, ipl + ntkoff) || (ipl < hl) || 1408c2ecf20Sopenharmony_ci skb_try_make_writable(skb, hl + ntkoff)) 1418c2ecf20Sopenharmony_ci return NULL; 1428c2ecf20Sopenharmony_ci else 1438c2ecf20Sopenharmony_ci return (void *)(skb_network_header(skb) + ihl); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int tcf_csum_ipv4_icmp(struct sk_buff *skb, unsigned int ihl, 1478c2ecf20Sopenharmony_ci unsigned int ipl) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct icmphdr *icmph; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci icmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmph)); 1528c2ecf20Sopenharmony_ci if (icmph == NULL) 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci icmph->checksum = 0; 1568c2ecf20Sopenharmony_ci skb->csum = csum_partial(icmph, ipl - ihl, 0); 1578c2ecf20Sopenharmony_ci icmph->checksum = csum_fold(skb->csum); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return 1; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int tcf_csum_ipv4_igmp(struct sk_buff *skb, 1658c2ecf20Sopenharmony_ci unsigned int ihl, unsigned int ipl) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct igmphdr *igmph; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci igmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*igmph)); 1708c2ecf20Sopenharmony_ci if (igmph == NULL) 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci igmph->csum = 0; 1748c2ecf20Sopenharmony_ci skb->csum = csum_partial(igmph, ipl - ihl, 0); 1758c2ecf20Sopenharmony_ci igmph->csum = csum_fold(skb->csum); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 1; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int tcf_csum_ipv6_icmp(struct sk_buff *skb, unsigned int ihl, 1838c2ecf20Sopenharmony_ci unsigned int ipl) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct icmp6hdr *icmp6h; 1868c2ecf20Sopenharmony_ci const struct ipv6hdr *ip6h; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h)); 1898c2ecf20Sopenharmony_ci if (icmp6h == NULL) 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ip6h = ipv6_hdr(skb); 1938c2ecf20Sopenharmony_ci icmp6h->icmp6_cksum = 0; 1948c2ecf20Sopenharmony_ci skb->csum = csum_partial(icmp6h, ipl - ihl, 0); 1958c2ecf20Sopenharmony_ci icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 1968c2ecf20Sopenharmony_ci ipl - ihl, IPPROTO_ICMPV6, 1978c2ecf20Sopenharmony_ci skb->csum); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 1; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int tcf_csum_ipv4_tcp(struct sk_buff *skb, unsigned int ihl, 2058c2ecf20Sopenharmony_ci unsigned int ipl) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct tcphdr *tcph; 2088c2ecf20Sopenharmony_ci const struct iphdr *iph; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) 2118c2ecf20Sopenharmony_ci return 1; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); 2148c2ecf20Sopenharmony_ci if (tcph == NULL) 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci iph = ip_hdr(skb); 2188c2ecf20Sopenharmony_ci tcph->check = 0; 2198c2ecf20Sopenharmony_ci skb->csum = csum_partial(tcph, ipl - ihl, 0); 2208c2ecf20Sopenharmony_ci tcph->check = tcp_v4_check(ipl - ihl, 2218c2ecf20Sopenharmony_ci iph->saddr, iph->daddr, skb->csum); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 1; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int tcf_csum_ipv6_tcp(struct sk_buff *skb, unsigned int ihl, 2298c2ecf20Sopenharmony_ci unsigned int ipl) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct tcphdr *tcph; 2328c2ecf20Sopenharmony_ci const struct ipv6hdr *ip6h; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) 2358c2ecf20Sopenharmony_ci return 1; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); 2388c2ecf20Sopenharmony_ci if (tcph == NULL) 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ip6h = ipv6_hdr(skb); 2428c2ecf20Sopenharmony_ci tcph->check = 0; 2438c2ecf20Sopenharmony_ci skb->csum = csum_partial(tcph, ipl - ihl, 0); 2448c2ecf20Sopenharmony_ci tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 2458c2ecf20Sopenharmony_ci ipl - ihl, IPPROTO_TCP, 2468c2ecf20Sopenharmony_ci skb->csum); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 1; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int tcf_csum_ipv4_udp(struct sk_buff *skb, unsigned int ihl, 2548c2ecf20Sopenharmony_ci unsigned int ipl, int udplite) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct udphdr *udph; 2578c2ecf20Sopenharmony_ci const struct iphdr *iph; 2588c2ecf20Sopenharmony_ci u16 ul; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) 2618c2ecf20Sopenharmony_ci return 1; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* 2648c2ecf20Sopenharmony_ci * Support both UDP and UDPLITE checksum algorithms, Don't use 2658c2ecf20Sopenharmony_ci * udph->len to get the real length without any protocol check, 2668c2ecf20Sopenharmony_ci * UDPLITE uses udph->len for another thing, 2678c2ecf20Sopenharmony_ci * Use iph->tot_len, or just ipl. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph)); 2718c2ecf20Sopenharmony_ci if (udph == NULL) 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci iph = ip_hdr(skb); 2758c2ecf20Sopenharmony_ci ul = ntohs(udph->len); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (udplite || udph->check) { 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci udph->check = 0; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (udplite) { 2828c2ecf20Sopenharmony_ci if (ul == 0) 2838c2ecf20Sopenharmony_ci skb->csum = csum_partial(udph, ipl - ihl, 0); 2848c2ecf20Sopenharmony_ci else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl)) 2858c2ecf20Sopenharmony_ci skb->csum = csum_partial(udph, ul, 0); 2868c2ecf20Sopenharmony_ci else 2878c2ecf20Sopenharmony_ci goto ignore_obscure_skb; 2888c2ecf20Sopenharmony_ci } else { 2898c2ecf20Sopenharmony_ci if (ul != ipl - ihl) 2908c2ecf20Sopenharmony_ci goto ignore_obscure_skb; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci skb->csum = csum_partial(udph, ul, 0); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, 2968c2ecf20Sopenharmony_ci ul, iph->protocol, 2978c2ecf20Sopenharmony_ci skb->csum); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (!udph->check) 3008c2ecf20Sopenharmony_ci udph->check = CSUM_MANGLED_0; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ciignore_obscure_skb: 3068c2ecf20Sopenharmony_ci return 1; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int tcf_csum_ipv6_udp(struct sk_buff *skb, unsigned int ihl, 3108c2ecf20Sopenharmony_ci unsigned int ipl, int udplite) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct udphdr *udph; 3138c2ecf20Sopenharmony_ci const struct ipv6hdr *ip6h; 3148c2ecf20Sopenharmony_ci u16 ul; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) 3178c2ecf20Sopenharmony_ci return 1; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* 3208c2ecf20Sopenharmony_ci * Support both UDP and UDPLITE checksum algorithms, Don't use 3218c2ecf20Sopenharmony_ci * udph->len to get the real length without any protocol check, 3228c2ecf20Sopenharmony_ci * UDPLITE uses udph->len for another thing, 3238c2ecf20Sopenharmony_ci * Use ip6h->payload_len + sizeof(*ip6h) ... , or just ipl. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph)); 3278c2ecf20Sopenharmony_ci if (udph == NULL) 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ip6h = ipv6_hdr(skb); 3318c2ecf20Sopenharmony_ci ul = ntohs(udph->len); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci udph->check = 0; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (udplite) { 3368c2ecf20Sopenharmony_ci if (ul == 0) 3378c2ecf20Sopenharmony_ci skb->csum = csum_partial(udph, ipl - ihl, 0); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl)) 3408c2ecf20Sopenharmony_ci skb->csum = csum_partial(udph, ul, 0); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci else 3438c2ecf20Sopenharmony_ci goto ignore_obscure_skb; 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci if (ul != ipl - ihl) 3468c2ecf20Sopenharmony_ci goto ignore_obscure_skb; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci skb->csum = csum_partial(udph, ul, 0); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci udph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, ul, 3528c2ecf20Sopenharmony_ci udplite ? IPPROTO_UDPLITE : IPPROTO_UDP, 3538c2ecf20Sopenharmony_ci skb->csum); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!udph->check) 3568c2ecf20Sopenharmony_ci udph->check = CSUM_MANGLED_0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ciignore_obscure_skb: 3618c2ecf20Sopenharmony_ci return 1; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int tcf_csum_sctp(struct sk_buff *skb, unsigned int ihl, 3658c2ecf20Sopenharmony_ci unsigned int ipl) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct sctphdr *sctph; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (skb_is_gso(skb) && skb_is_gso_sctp(skb)) 3708c2ecf20Sopenharmony_ci return 1; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci sctph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*sctph)); 3738c2ecf20Sopenharmony_ci if (!sctph) 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci sctph->checksum = sctp_compute_cksum(skb, 3778c2ecf20Sopenharmony_ci skb_network_offset(skb) + ihl); 3788c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 3798c2ecf20Sopenharmony_ci skb->csum_not_inet = 0; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return 1; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci const struct iphdr *iph; 3878c2ecf20Sopenharmony_ci int ntkoff; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci ntkoff = skb_network_offset(skb); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*iph) + ntkoff)) 3928c2ecf20Sopenharmony_ci goto fail; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci iph = ip_hdr(skb); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) { 3978c2ecf20Sopenharmony_ci case IPPROTO_ICMP: 3988c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP) 3998c2ecf20Sopenharmony_ci if (!tcf_csum_ipv4_icmp(skb, iph->ihl * 4, 4008c2ecf20Sopenharmony_ci ntohs(iph->tot_len))) 4018c2ecf20Sopenharmony_ci goto fail; 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci case IPPROTO_IGMP: 4048c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_IGMP) 4058c2ecf20Sopenharmony_ci if (!tcf_csum_ipv4_igmp(skb, iph->ihl * 4, 4068c2ecf20Sopenharmony_ci ntohs(iph->tot_len))) 4078c2ecf20Sopenharmony_ci goto fail; 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci case IPPROTO_TCP: 4108c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) 4118c2ecf20Sopenharmony_ci if (!tcf_csum_ipv4_tcp(skb, iph->ihl * 4, 4128c2ecf20Sopenharmony_ci ntohs(iph->tot_len))) 4138c2ecf20Sopenharmony_ci goto fail; 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci case IPPROTO_UDP: 4168c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) 4178c2ecf20Sopenharmony_ci if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4, 4188c2ecf20Sopenharmony_ci ntohs(iph->tot_len), 0)) 4198c2ecf20Sopenharmony_ci goto fail; 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci case IPPROTO_UDPLITE: 4228c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) 4238c2ecf20Sopenharmony_ci if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4, 4248c2ecf20Sopenharmony_ci ntohs(iph->tot_len), 1)) 4258c2ecf20Sopenharmony_ci goto fail; 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci case IPPROTO_SCTP: 4288c2ecf20Sopenharmony_ci if ((update_flags & TCA_CSUM_UPDATE_FLAG_SCTP) && 4298c2ecf20Sopenharmony_ci !tcf_csum_sctp(skb, iph->ihl * 4, ntohs(iph->tot_len))) 4308c2ecf20Sopenharmony_ci goto fail; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) { 4358c2ecf20Sopenharmony_ci if (skb_try_make_writable(skb, sizeof(*iph) + ntkoff)) 4368c2ecf20Sopenharmony_ci goto fail; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ip_send_check(ip_hdr(skb)); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 1; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cifail: 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int tcf_csum_ipv6_hopopts(struct ipv6_opt_hdr *ip6xh, unsigned int ixhl, 4488c2ecf20Sopenharmony_ci unsigned int *pl) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci int off, len, optlen; 4518c2ecf20Sopenharmony_ci unsigned char *xh = (void *)ip6xh; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci off = sizeof(*ip6xh); 4548c2ecf20Sopenharmony_ci len = ixhl - off; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci while (len > 1) { 4578c2ecf20Sopenharmony_ci switch (xh[off]) { 4588c2ecf20Sopenharmony_ci case IPV6_TLV_PAD1: 4598c2ecf20Sopenharmony_ci optlen = 1; 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci case IPV6_TLV_JUMBO: 4628c2ecf20Sopenharmony_ci optlen = xh[off + 1] + 2; 4638c2ecf20Sopenharmony_ci if (optlen != 6 || len < 6 || (off & 3) != 2) 4648c2ecf20Sopenharmony_ci /* wrong jumbo option length/alignment */ 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci *pl = ntohl(*(__be32 *)(xh + off + 2)); 4678c2ecf20Sopenharmony_ci goto done; 4688c2ecf20Sopenharmony_ci default: 4698c2ecf20Sopenharmony_ci optlen = xh[off + 1] + 2; 4708c2ecf20Sopenharmony_ci if (optlen > len) 4718c2ecf20Sopenharmony_ci /* ignore obscure options */ 4728c2ecf20Sopenharmony_ci goto done; 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci off += optlen; 4768c2ecf20Sopenharmony_ci len -= optlen; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cidone: 4808c2ecf20Sopenharmony_ci return 1; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct ipv6hdr *ip6h; 4868c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *ip6xh; 4878c2ecf20Sopenharmony_ci unsigned int hl, ixhl; 4888c2ecf20Sopenharmony_ci unsigned int pl; 4898c2ecf20Sopenharmony_ci int ntkoff; 4908c2ecf20Sopenharmony_ci u8 nexthdr; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci ntkoff = skb_network_offset(skb); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci hl = sizeof(*ip6h); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, hl + ntkoff)) 4978c2ecf20Sopenharmony_ci goto fail; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci ip6h = ipv6_hdr(skb); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci pl = ntohs(ip6h->payload_len); 5028c2ecf20Sopenharmony_ci nexthdr = ip6h->nexthdr; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci do { 5058c2ecf20Sopenharmony_ci switch (nexthdr) { 5068c2ecf20Sopenharmony_ci case NEXTHDR_FRAGMENT: 5078c2ecf20Sopenharmony_ci goto ignore_skb; 5088c2ecf20Sopenharmony_ci case NEXTHDR_ROUTING: 5098c2ecf20Sopenharmony_ci case NEXTHDR_HOP: 5108c2ecf20Sopenharmony_ci case NEXTHDR_DEST: 5118c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, hl + sizeof(*ip6xh) + ntkoff)) 5128c2ecf20Sopenharmony_ci goto fail; 5138c2ecf20Sopenharmony_ci ip6xh = (void *)(skb_network_header(skb) + hl); 5148c2ecf20Sopenharmony_ci ixhl = ipv6_optlen(ip6xh); 5158c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, hl + ixhl + ntkoff)) 5168c2ecf20Sopenharmony_ci goto fail; 5178c2ecf20Sopenharmony_ci ip6xh = (void *)(skb_network_header(skb) + hl); 5188c2ecf20Sopenharmony_ci if ((nexthdr == NEXTHDR_HOP) && 5198c2ecf20Sopenharmony_ci !(tcf_csum_ipv6_hopopts(ip6xh, ixhl, &pl))) 5208c2ecf20Sopenharmony_ci goto fail; 5218c2ecf20Sopenharmony_ci nexthdr = ip6xh->nexthdr; 5228c2ecf20Sopenharmony_ci hl += ixhl; 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci case IPPROTO_ICMPV6: 5258c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP) 5268c2ecf20Sopenharmony_ci if (!tcf_csum_ipv6_icmp(skb, 5278c2ecf20Sopenharmony_ci hl, pl + sizeof(*ip6h))) 5288c2ecf20Sopenharmony_ci goto fail; 5298c2ecf20Sopenharmony_ci goto done; 5308c2ecf20Sopenharmony_ci case IPPROTO_TCP: 5318c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) 5328c2ecf20Sopenharmony_ci if (!tcf_csum_ipv6_tcp(skb, 5338c2ecf20Sopenharmony_ci hl, pl + sizeof(*ip6h))) 5348c2ecf20Sopenharmony_ci goto fail; 5358c2ecf20Sopenharmony_ci goto done; 5368c2ecf20Sopenharmony_ci case IPPROTO_UDP: 5378c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) 5388c2ecf20Sopenharmony_ci if (!tcf_csum_ipv6_udp(skb, hl, 5398c2ecf20Sopenharmony_ci pl + sizeof(*ip6h), 0)) 5408c2ecf20Sopenharmony_ci goto fail; 5418c2ecf20Sopenharmony_ci goto done; 5428c2ecf20Sopenharmony_ci case IPPROTO_UDPLITE: 5438c2ecf20Sopenharmony_ci if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) 5448c2ecf20Sopenharmony_ci if (!tcf_csum_ipv6_udp(skb, hl, 5458c2ecf20Sopenharmony_ci pl + sizeof(*ip6h), 1)) 5468c2ecf20Sopenharmony_ci goto fail; 5478c2ecf20Sopenharmony_ci goto done; 5488c2ecf20Sopenharmony_ci case IPPROTO_SCTP: 5498c2ecf20Sopenharmony_ci if ((update_flags & TCA_CSUM_UPDATE_FLAG_SCTP) && 5508c2ecf20Sopenharmony_ci !tcf_csum_sctp(skb, hl, pl + sizeof(*ip6h))) 5518c2ecf20Sopenharmony_ci goto fail; 5528c2ecf20Sopenharmony_ci goto done; 5538c2ecf20Sopenharmony_ci default: 5548c2ecf20Sopenharmony_ci goto ignore_skb; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci } while (pskb_may_pull(skb, hl + 1 + ntkoff)); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cidone: 5598c2ecf20Sopenharmony_ciignore_skb: 5608c2ecf20Sopenharmony_ci return 1; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cifail: 5638c2ecf20Sopenharmony_ci return 0; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int tcf_csum_act(struct sk_buff *skb, const struct tc_action *a, 5678c2ecf20Sopenharmony_ci struct tcf_result *res) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct tcf_csum *p = to_tcf_csum(a); 5708c2ecf20Sopenharmony_ci bool orig_vlan_tag_present = false; 5718c2ecf20Sopenharmony_ci unsigned int vlan_hdr_count = 0; 5728c2ecf20Sopenharmony_ci struct tcf_csum_params *params; 5738c2ecf20Sopenharmony_ci u32 update_flags; 5748c2ecf20Sopenharmony_ci __be16 protocol; 5758c2ecf20Sopenharmony_ci int action; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci params = rcu_dereference_bh(p->params); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci tcf_lastuse_update(&p->tcf_tm); 5808c2ecf20Sopenharmony_ci tcf_action_update_bstats(&p->common, skb); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci action = READ_ONCE(p->tcf_action); 5838c2ecf20Sopenharmony_ci if (unlikely(action == TC_ACT_SHOT)) 5848c2ecf20Sopenharmony_ci goto drop; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci update_flags = params->update_flags; 5878c2ecf20Sopenharmony_ci protocol = skb_protocol(skb, false); 5888c2ecf20Sopenharmony_ciagain: 5898c2ecf20Sopenharmony_ci switch (protocol) { 5908c2ecf20Sopenharmony_ci case cpu_to_be16(ETH_P_IP): 5918c2ecf20Sopenharmony_ci if (!tcf_csum_ipv4(skb, update_flags)) 5928c2ecf20Sopenharmony_ci goto drop; 5938c2ecf20Sopenharmony_ci break; 5948c2ecf20Sopenharmony_ci case cpu_to_be16(ETH_P_IPV6): 5958c2ecf20Sopenharmony_ci if (!tcf_csum_ipv6(skb, update_flags)) 5968c2ecf20Sopenharmony_ci goto drop; 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci case cpu_to_be16(ETH_P_8021AD): 5998c2ecf20Sopenharmony_ci fallthrough; 6008c2ecf20Sopenharmony_ci case cpu_to_be16(ETH_P_8021Q): 6018c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb) && !orig_vlan_tag_present) { 6028c2ecf20Sopenharmony_ci protocol = skb->protocol; 6038c2ecf20Sopenharmony_ci orig_vlan_tag_present = true; 6048c2ecf20Sopenharmony_ci } else { 6058c2ecf20Sopenharmony_ci struct vlan_hdr *vlan = (struct vlan_hdr *)skb->data; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci protocol = vlan->h_vlan_encapsulated_proto; 6088c2ecf20Sopenharmony_ci skb_pull(skb, VLAN_HLEN); 6098c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 6108c2ecf20Sopenharmony_ci vlan_hdr_count++; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci goto again; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ciout: 6168c2ecf20Sopenharmony_ci /* Restore the skb for the pulled VLAN tags */ 6178c2ecf20Sopenharmony_ci while (vlan_hdr_count--) { 6188c2ecf20Sopenharmony_ci skb_push(skb, VLAN_HLEN); 6198c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return action; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cidrop: 6258c2ecf20Sopenharmony_ci tcf_action_inc_drop_qstats(&p->common); 6268c2ecf20Sopenharmony_ci action = TC_ACT_SHOT; 6278c2ecf20Sopenharmony_ci goto out; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int tcf_csum_dump(struct sk_buff *skb, struct tc_action *a, int bind, 6318c2ecf20Sopenharmony_ci int ref) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 6348c2ecf20Sopenharmony_ci struct tcf_csum *p = to_tcf_csum(a); 6358c2ecf20Sopenharmony_ci struct tcf_csum_params *params; 6368c2ecf20Sopenharmony_ci struct tc_csum opt = { 6378c2ecf20Sopenharmony_ci .index = p->tcf_index, 6388c2ecf20Sopenharmony_ci .refcnt = refcount_read(&p->tcf_refcnt) - ref, 6398c2ecf20Sopenharmony_ci .bindcnt = atomic_read(&p->tcf_bindcnt) - bind, 6408c2ecf20Sopenharmony_ci }; 6418c2ecf20Sopenharmony_ci struct tcf_t t; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci spin_lock_bh(&p->tcf_lock); 6448c2ecf20Sopenharmony_ci params = rcu_dereference_protected(p->params, 6458c2ecf20Sopenharmony_ci lockdep_is_held(&p->tcf_lock)); 6468c2ecf20Sopenharmony_ci opt.action = p->tcf_action; 6478c2ecf20Sopenharmony_ci opt.update_flags = params->update_flags; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_CSUM_PARMS, sizeof(opt), &opt)) 6508c2ecf20Sopenharmony_ci goto nla_put_failure; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci tcf_tm_dump(&t, &p->tcf_tm); 6538c2ecf20Sopenharmony_ci if (nla_put_64bit(skb, TCA_CSUM_TM, sizeof(t), &t, TCA_CSUM_PAD)) 6548c2ecf20Sopenharmony_ci goto nla_put_failure; 6558c2ecf20Sopenharmony_ci spin_unlock_bh(&p->tcf_lock); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return skb->len; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cinla_put_failure: 6608c2ecf20Sopenharmony_ci spin_unlock_bh(&p->tcf_lock); 6618c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 6628c2ecf20Sopenharmony_ci return -1; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic void tcf_csum_cleanup(struct tc_action *a) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct tcf_csum *p = to_tcf_csum(a); 6688c2ecf20Sopenharmony_ci struct tcf_csum_params *params; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci params = rcu_dereference_protected(p->params, 1); 6718c2ecf20Sopenharmony_ci if (params) 6728c2ecf20Sopenharmony_ci kfree_rcu(params, rcu); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic int tcf_csum_walker(struct net *net, struct sk_buff *skb, 6768c2ecf20Sopenharmony_ci struct netlink_callback *cb, int type, 6778c2ecf20Sopenharmony_ci const struct tc_action_ops *ops, 6788c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct tc_action_net *tn = net_generic(net, csum_net_id); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci return tcf_generic_walker(tn, skb, cb, type, ops, extack); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic int tcf_csum_search(struct net *net, struct tc_action **a, u32 index) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci struct tc_action_net *tn = net_generic(net, csum_net_id); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return tcf_idr_search(tn, a, index); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic size_t tcf_csum_get_fill_size(const struct tc_action *act) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci return nla_total_size(sizeof(struct tc_csum)); 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic struct tc_action_ops act_csum_ops = { 6988c2ecf20Sopenharmony_ci .kind = "csum", 6998c2ecf20Sopenharmony_ci .id = TCA_ID_CSUM, 7008c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7018c2ecf20Sopenharmony_ci .act = tcf_csum_act, 7028c2ecf20Sopenharmony_ci .dump = tcf_csum_dump, 7038c2ecf20Sopenharmony_ci .init = tcf_csum_init, 7048c2ecf20Sopenharmony_ci .cleanup = tcf_csum_cleanup, 7058c2ecf20Sopenharmony_ci .walk = tcf_csum_walker, 7068c2ecf20Sopenharmony_ci .lookup = tcf_csum_search, 7078c2ecf20Sopenharmony_ci .get_fill_size = tcf_csum_get_fill_size, 7088c2ecf20Sopenharmony_ci .size = sizeof(struct tcf_csum), 7098c2ecf20Sopenharmony_ci}; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic __net_init int csum_init_net(struct net *net) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct tc_action_net *tn = net_generic(net, csum_net_id); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci return tc_action_net_init(net, tn, &act_csum_ops); 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic void __net_exit csum_exit_net(struct list_head *net_list) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci tc_action_net_exit(net_list, csum_net_id); 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic struct pernet_operations csum_net_ops = { 7248c2ecf20Sopenharmony_ci .init = csum_init_net, 7258c2ecf20Sopenharmony_ci .exit_batch = csum_exit_net, 7268c2ecf20Sopenharmony_ci .id = &csum_net_id, 7278c2ecf20Sopenharmony_ci .size = sizeof(struct tc_action_net), 7288c2ecf20Sopenharmony_ci}; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Checksum updating actions"); 7318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic int __init csum_init_module(void) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci return tcf_register_action(&act_csum_ops, &csum_net_ops); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic void __exit csum_cleanup_module(void) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci tcf_unregister_action(&act_csum_ops, &csum_net_ops); 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cimodule_init(csum_init_module); 7448c2ecf20Sopenharmony_cimodule_exit(csum_cleanup_module); 745