Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0 2 : #include <net/ip.h> 3 : #include <net/udp.h> 4 : #include <net/udplite.h> 5 : #include <asm/checksum.h> 6 : 7 : #ifndef _HAVE_ARCH_IPV6_CSUM 8 : __sum16 csum_ipv6_magic(const struct in6_addr *saddr, 9 : const struct in6_addr *daddr, 10 : __u32 len, __u8 proto, __wsum csum) 11 : { 12 : 13 : int carry; 14 : __u32 ulen; 15 : __u32 uproto; 16 : __u32 sum = (__force u32)csum; 17 : 18 : sum += (__force u32)saddr->s6_addr32[0]; 19 : carry = (sum < (__force u32)saddr->s6_addr32[0]); 20 : sum += carry; 21 : 22 : sum += (__force u32)saddr->s6_addr32[1]; 23 : carry = (sum < (__force u32)saddr->s6_addr32[1]); 24 : sum += carry; 25 : 26 : sum += (__force u32)saddr->s6_addr32[2]; 27 : carry = (sum < (__force u32)saddr->s6_addr32[2]); 28 : sum += carry; 29 : 30 : sum += (__force u32)saddr->s6_addr32[3]; 31 : carry = (sum < (__force u32)saddr->s6_addr32[3]); 32 : sum += carry; 33 : 34 : sum += (__force u32)daddr->s6_addr32[0]; 35 : carry = (sum < (__force u32)daddr->s6_addr32[0]); 36 : sum += carry; 37 : 38 : sum += (__force u32)daddr->s6_addr32[1]; 39 : carry = (sum < (__force u32)daddr->s6_addr32[1]); 40 : sum += carry; 41 : 42 : sum += (__force u32)daddr->s6_addr32[2]; 43 : carry = (sum < (__force u32)daddr->s6_addr32[2]); 44 : sum += carry; 45 : 46 : sum += (__force u32)daddr->s6_addr32[3]; 47 : carry = (sum < (__force u32)daddr->s6_addr32[3]); 48 : sum += carry; 49 : 50 : ulen = (__force u32)htonl((__u32) len); 51 : sum += ulen; 52 : carry = (sum < ulen); 53 : sum += carry; 54 : 55 : uproto = (__force u32)htonl(proto); 56 : sum += uproto; 57 : carry = (sum < uproto); 58 : sum += carry; 59 : 60 : return csum_fold((__force __wsum)sum); 61 : } 62 : EXPORT_SYMBOL(csum_ipv6_magic); 63 : #endif 64 : 65 0 : int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) 66 : { 67 0 : int err; 68 : 69 0 : UDP_SKB_CB(skb)->partial_cov = 0; 70 0 : UDP_SKB_CB(skb)->cscov = skb->len; 71 : 72 0 : if (proto == IPPROTO_UDPLITE) { 73 0 : err = udplite_checksum_init(skb, uh); 74 0 : if (err) 75 : return err; 76 : 77 0 : if (UDP_SKB_CB(skb)->partial_cov) { 78 0 : skb->csum = ip6_compute_pseudo(skb, proto); 79 0 : return 0; 80 : } 81 : } 82 : 83 : /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels) 84 : * we accept a checksum of zero here. When we find the socket 85 : * for the UDP packet we'll check if that socket allows zero checksum 86 : * for IPv6 (set by socket option). 87 : * 88 : * Note, we are only interested in != 0 or == 0, thus the 89 : * force to int. 90 : */ 91 0 : err = (__force int)skb_checksum_init_zero_check(skb, proto, uh->check, 92 : ip6_compute_pseudo); 93 0 : if (err) 94 : return err; 95 : 96 0 : if (skb->ip_summed == CHECKSUM_COMPLETE && !skb->csum_valid) { 97 : /* If SW calculated the value, we know it's bad */ 98 0 : if (skb->csum_complete_sw) 99 : return 1; 100 : 101 : /* HW says the value is bad. Let's validate that. 102 : * skb->csum is no longer the full packet checksum, 103 : * so don't treat is as such. 104 : */ 105 0 : skb_checksum_complete_unset(skb); 106 : } 107 : 108 : return 0; 109 : } 110 : EXPORT_SYMBOL(udp6_csum_init); 111 : 112 : /* Function to set UDP checksum for an IPv6 UDP packet. This is intended 113 : * for the simple case like when setting the checksum for a UDP tunnel. 114 : */ 115 0 : void udp6_set_csum(bool nocheck, struct sk_buff *skb, 116 : const struct in6_addr *saddr, 117 : const struct in6_addr *daddr, int len) 118 : { 119 0 : struct udphdr *uh = udp_hdr(skb); 120 : 121 0 : if (nocheck) 122 0 : uh->check = 0; 123 0 : else if (skb_is_gso(skb)) 124 0 : uh->check = ~udp_v6_check(len, saddr, daddr, 0); 125 0 : else if (skb->ip_summed == CHECKSUM_PARTIAL) { 126 0 : uh->check = 0; 127 0 : uh->check = udp_v6_check(len, saddr, daddr, lco_csum(skb)); 128 0 : if (uh->check == 0) 129 0 : uh->check = CSUM_MANGLED_0; 130 : } else { 131 0 : skb->ip_summed = CHECKSUM_PARTIAL; 132 0 : skb->csum_start = skb_transport_header(skb) - skb->head; 133 0 : skb->csum_offset = offsetof(struct udphdr, check); 134 0 : uh->check = ~udp_v6_check(len, saddr, daddr, 0); 135 : } 136 0 : } 137 : EXPORT_SYMBOL(udp6_set_csum);