18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <net/ip.h> 38c2ecf20Sopenharmony_ci#include <net/udp.h> 48c2ecf20Sopenharmony_ci#include <net/udplite.h> 58c2ecf20Sopenharmony_ci#include <asm/checksum.h> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#ifndef _HAVE_ARCH_IPV6_CSUM 88c2ecf20Sopenharmony_ci__sum16 csum_ipv6_magic(const struct in6_addr *saddr, 98c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 108c2ecf20Sopenharmony_ci __u32 len, __u8 proto, __wsum csum) 118c2ecf20Sopenharmony_ci{ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci int carry; 148c2ecf20Sopenharmony_ci __u32 ulen; 158c2ecf20Sopenharmony_ci __u32 uproto; 168c2ecf20Sopenharmony_ci __u32 sum = (__force u32)csum; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci sum += (__force u32)saddr->s6_addr32[0]; 198c2ecf20Sopenharmony_ci carry = (sum < (__force u32)saddr->s6_addr32[0]); 208c2ecf20Sopenharmony_ci sum += carry; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci sum += (__force u32)saddr->s6_addr32[1]; 238c2ecf20Sopenharmony_ci carry = (sum < (__force u32)saddr->s6_addr32[1]); 248c2ecf20Sopenharmony_ci sum += carry; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci sum += (__force u32)saddr->s6_addr32[2]; 278c2ecf20Sopenharmony_ci carry = (sum < (__force u32)saddr->s6_addr32[2]); 288c2ecf20Sopenharmony_ci sum += carry; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci sum += (__force u32)saddr->s6_addr32[3]; 318c2ecf20Sopenharmony_ci carry = (sum < (__force u32)saddr->s6_addr32[3]); 328c2ecf20Sopenharmony_ci sum += carry; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci sum += (__force u32)daddr->s6_addr32[0]; 358c2ecf20Sopenharmony_ci carry = (sum < (__force u32)daddr->s6_addr32[0]); 368c2ecf20Sopenharmony_ci sum += carry; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci sum += (__force u32)daddr->s6_addr32[1]; 398c2ecf20Sopenharmony_ci carry = (sum < (__force u32)daddr->s6_addr32[1]); 408c2ecf20Sopenharmony_ci sum += carry; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci sum += (__force u32)daddr->s6_addr32[2]; 438c2ecf20Sopenharmony_ci carry = (sum < (__force u32)daddr->s6_addr32[2]); 448c2ecf20Sopenharmony_ci sum += carry; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci sum += (__force u32)daddr->s6_addr32[3]; 478c2ecf20Sopenharmony_ci carry = (sum < (__force u32)daddr->s6_addr32[3]); 488c2ecf20Sopenharmony_ci sum += carry; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci ulen = (__force u32)htonl((__u32) len); 518c2ecf20Sopenharmony_ci sum += ulen; 528c2ecf20Sopenharmony_ci carry = (sum < ulen); 538c2ecf20Sopenharmony_ci sum += carry; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci uproto = (__force u32)htonl(proto); 568c2ecf20Sopenharmony_ci sum += uproto; 578c2ecf20Sopenharmony_ci carry = (sum < uproto); 588c2ecf20Sopenharmony_ci sum += carry; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return csum_fold((__force __wsum)sum); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(csum_ipv6_magic); 638c2ecf20Sopenharmony_ci#endif 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ciint udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int err; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci UDP_SKB_CB(skb)->partial_cov = 0; 708c2ecf20Sopenharmony_ci UDP_SKB_CB(skb)->cscov = skb->len; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (proto == IPPROTO_UDPLITE) { 738c2ecf20Sopenharmony_ci err = udplite_checksum_init(skb, uh); 748c2ecf20Sopenharmony_ci if (err) 758c2ecf20Sopenharmony_ci return err; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (UDP_SKB_CB(skb)->partial_cov) { 788c2ecf20Sopenharmony_ci skb->csum = ip6_compute_pseudo(skb, proto); 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels) 848c2ecf20Sopenharmony_ci * we accept a checksum of zero here. When we find the socket 858c2ecf20Sopenharmony_ci * for the UDP packet we'll check if that socket allows zero checksum 868c2ecf20Sopenharmony_ci * for IPv6 (set by socket option). 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Note, we are only interested in != 0 or == 0, thus the 898c2ecf20Sopenharmony_ci * force to int. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci err = (__force int)skb_checksum_init_zero_check(skb, proto, uh->check, 928c2ecf20Sopenharmony_ci ip6_compute_pseudo); 938c2ecf20Sopenharmony_ci if (err) 948c2ecf20Sopenharmony_ci return err; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE && !skb->csum_valid) { 978c2ecf20Sopenharmony_ci /* If SW calculated the value, we know it's bad */ 988c2ecf20Sopenharmony_ci if (skb->csum_complete_sw) 998c2ecf20Sopenharmony_ci return 1; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* HW says the value is bad. Let's validate that. 1028c2ecf20Sopenharmony_ci * skb->csum is no longer the full packet checksum, 1038c2ecf20Sopenharmony_ci * so don't treat is as such. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci skb_checksum_complete_unset(skb); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(udp6_csum_init); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* Function to set UDP checksum for an IPv6 UDP packet. This is intended 1138c2ecf20Sopenharmony_ci * for the simple case like when setting the checksum for a UDP tunnel. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_civoid udp6_set_csum(bool nocheck, struct sk_buff *skb, 1168c2ecf20Sopenharmony_ci const struct in6_addr *saddr, 1178c2ecf20Sopenharmony_ci const struct in6_addr *daddr, int len) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct udphdr *uh = udp_hdr(skb); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (nocheck) 1228c2ecf20Sopenharmony_ci uh->check = 0; 1238c2ecf20Sopenharmony_ci else if (skb_is_gso(skb)) 1248c2ecf20Sopenharmony_ci uh->check = ~udp_v6_check(len, saddr, daddr, 0); 1258c2ecf20Sopenharmony_ci else if (skb->ip_summed == CHECKSUM_PARTIAL) { 1268c2ecf20Sopenharmony_ci uh->check = 0; 1278c2ecf20Sopenharmony_ci uh->check = udp_v6_check(len, saddr, daddr, lco_csum(skb)); 1288c2ecf20Sopenharmony_ci if (uh->check == 0) 1298c2ecf20Sopenharmony_ci uh->check = CSUM_MANGLED_0; 1308c2ecf20Sopenharmony_ci } else { 1318c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_PARTIAL; 1328c2ecf20Sopenharmony_ci skb->csum_start = skb_transport_header(skb) - skb->head; 1338c2ecf20Sopenharmony_ci skb->csum_offset = offsetof(struct udphdr, check); 1348c2ecf20Sopenharmony_ci uh->check = ~udp_v6_check(len, saddr, daddr, 0); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(udp6_set_csum); 138