Branch data 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 : 3 : int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) 66 : : { 67 : : int err; 68 : : 69 : 3 : UDP_SKB_CB(skb)->partial_cov = 0; 70 : 3 : UDP_SKB_CB(skb)->cscov = skb->len; 71 : : 72 : 3 : 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 : 3 : err = (__force int)skb_checksum_init_zero_check(skb, proto, uh->check, 92 : : ip6_compute_pseudo); 93 : 3 : if (err) 94 : : return err; 95 : : 96 : 3 : 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 : : 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 : : 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);