1acb3e041SCong Wang #include <net/ip.h> 2acb3e041SCong Wang #include <net/udp.h> 3acb3e041SCong Wang #include <net/udplite.h> 4acb3e041SCong Wang #include <asm/checksum.h> 5acb3e041SCong Wang 6acb3e041SCong Wang #ifndef _HAVE_ARCH_IPV6_CSUM 7acb3e041SCong Wang __sum16 csum_ipv6_magic(const struct in6_addr *saddr, 8acb3e041SCong Wang const struct in6_addr *daddr, 9*1e940829SAlexander Duyck __u32 len, __u8 proto, __wsum csum) 10acb3e041SCong Wang { 11acb3e041SCong Wang 12acb3e041SCong Wang int carry; 13acb3e041SCong Wang __u32 ulen; 14acb3e041SCong Wang __u32 uproto; 15acb3e041SCong Wang __u32 sum = (__force u32)csum; 16acb3e041SCong Wang 17acb3e041SCong Wang sum += (__force u32)saddr->s6_addr32[0]; 18acb3e041SCong Wang carry = (sum < (__force u32)saddr->s6_addr32[0]); 19acb3e041SCong Wang sum += carry; 20acb3e041SCong Wang 21acb3e041SCong Wang sum += (__force u32)saddr->s6_addr32[1]; 22acb3e041SCong Wang carry = (sum < (__force u32)saddr->s6_addr32[1]); 23acb3e041SCong Wang sum += carry; 24acb3e041SCong Wang 25acb3e041SCong Wang sum += (__force u32)saddr->s6_addr32[2]; 26acb3e041SCong Wang carry = (sum < (__force u32)saddr->s6_addr32[2]); 27acb3e041SCong Wang sum += carry; 28acb3e041SCong Wang 29acb3e041SCong Wang sum += (__force u32)saddr->s6_addr32[3]; 30acb3e041SCong Wang carry = (sum < (__force u32)saddr->s6_addr32[3]); 31acb3e041SCong Wang sum += carry; 32acb3e041SCong Wang 33acb3e041SCong Wang sum += (__force u32)daddr->s6_addr32[0]; 34acb3e041SCong Wang carry = (sum < (__force u32)daddr->s6_addr32[0]); 35acb3e041SCong Wang sum += carry; 36acb3e041SCong Wang 37acb3e041SCong Wang sum += (__force u32)daddr->s6_addr32[1]; 38acb3e041SCong Wang carry = (sum < (__force u32)daddr->s6_addr32[1]); 39acb3e041SCong Wang sum += carry; 40acb3e041SCong Wang 41acb3e041SCong Wang sum += (__force u32)daddr->s6_addr32[2]; 42acb3e041SCong Wang carry = (sum < (__force u32)daddr->s6_addr32[2]); 43acb3e041SCong Wang sum += carry; 44acb3e041SCong Wang 45acb3e041SCong Wang sum += (__force u32)daddr->s6_addr32[3]; 46acb3e041SCong Wang carry = (sum < (__force u32)daddr->s6_addr32[3]); 47acb3e041SCong Wang sum += carry; 48acb3e041SCong Wang 49acb3e041SCong Wang ulen = (__force u32)htonl((__u32) len); 50acb3e041SCong Wang sum += ulen; 51acb3e041SCong Wang carry = (sum < ulen); 52acb3e041SCong Wang sum += carry; 53acb3e041SCong Wang 54acb3e041SCong Wang uproto = (__force u32)htonl(proto); 55acb3e041SCong Wang sum += uproto; 56acb3e041SCong Wang carry = (sum < uproto); 57acb3e041SCong Wang sum += carry; 58acb3e041SCong Wang 59acb3e041SCong Wang return csum_fold((__force __wsum)sum); 60acb3e041SCong Wang } 61acb3e041SCong Wang EXPORT_SYMBOL(csum_ipv6_magic); 62acb3e041SCong Wang #endif 63acb3e041SCong Wang 64acb3e041SCong Wang int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) 65acb3e041SCong Wang { 66acb3e041SCong Wang int err; 67acb3e041SCong Wang 68acb3e041SCong Wang UDP_SKB_CB(skb)->partial_cov = 0; 69acb3e041SCong Wang UDP_SKB_CB(skb)->cscov = skb->len; 70acb3e041SCong Wang 71acb3e041SCong Wang if (proto == IPPROTO_UDPLITE) { 72acb3e041SCong Wang err = udplite_checksum_init(skb, uh); 73acb3e041SCong Wang if (err) 74acb3e041SCong Wang return err; 75acb3e041SCong Wang } 76acb3e041SCong Wang 774068579eSTom Herbert /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels) 784068579eSTom Herbert * we accept a checksum of zero here. When we find the socket 794068579eSTom Herbert * for the UDP packet we'll check if that socket allows zero checksum 804068579eSTom Herbert * for IPv6 (set by socket option). 81acb3e041SCong Wang */ 824068579eSTom Herbert return skb_checksum_init_zero_check(skb, proto, uh->check, 834068579eSTom Herbert ip6_compute_pseudo); 84acb3e041SCong Wang } 85acb3e041SCong Wang EXPORT_SYMBOL(udp6_csum_init); 86af5fcba7STom Herbert 87af5fcba7STom Herbert /* Function to set UDP checksum for an IPv6 UDP packet. This is intended 88af5fcba7STom Herbert * for the simple case like when setting the checksum for a UDP tunnel. 89af5fcba7STom Herbert */ 90af5fcba7STom Herbert void udp6_set_csum(bool nocheck, struct sk_buff *skb, 91af5fcba7STom Herbert const struct in6_addr *saddr, 92af5fcba7STom Herbert const struct in6_addr *daddr, int len) 93af5fcba7STom Herbert { 94af5fcba7STom Herbert struct udphdr *uh = udp_hdr(skb); 95af5fcba7STom Herbert 96af5fcba7STom Herbert if (nocheck) 97af5fcba7STom Herbert uh->check = 0; 98af5fcba7STom Herbert else if (skb_is_gso(skb)) 99af5fcba7STom Herbert uh->check = ~udp_v6_check(len, saddr, daddr, 0); 100179bc67fSEdward Cree else if (skb->ip_summed == CHECKSUM_PARTIAL) { 101179bc67fSEdward Cree uh->check = 0; 102179bc67fSEdward Cree uh->check = udp_v6_check(len, saddr, daddr, lco_csum(skb)); 103179bc67fSEdward Cree if (uh->check == 0) 104179bc67fSEdward Cree uh->check = CSUM_MANGLED_0; 105d75f1306SEdward Cree } else { 106af5fcba7STom Herbert skb->ip_summed = CHECKSUM_PARTIAL; 107af5fcba7STom Herbert skb->csum_start = skb_transport_header(skb) - skb->head; 108af5fcba7STom Herbert skb->csum_offset = offsetof(struct udphdr, check); 109af5fcba7STom Herbert uh->check = ~udp_v6_check(len, saddr, daddr, 0); 110af5fcba7STom Herbert } 111af5fcba7STom Herbert } 112af5fcba7STom Herbert EXPORT_SYMBOL(udp6_set_csum); 113