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, 9acb3e041SCong Wang __u32 len, unsigned short proto, 10acb3e041SCong Wang __wsum csum) 11acb3e041SCong Wang { 12acb3e041SCong Wang 13acb3e041SCong Wang int carry; 14acb3e041SCong Wang __u32 ulen; 15acb3e041SCong Wang __u32 uproto; 16acb3e041SCong Wang __u32 sum = (__force u32)csum; 17acb3e041SCong Wang 18acb3e041SCong Wang sum += (__force u32)saddr->s6_addr32[0]; 19acb3e041SCong Wang carry = (sum < (__force u32)saddr->s6_addr32[0]); 20acb3e041SCong Wang sum += carry; 21acb3e041SCong Wang 22acb3e041SCong Wang sum += (__force u32)saddr->s6_addr32[1]; 23acb3e041SCong Wang carry = (sum < (__force u32)saddr->s6_addr32[1]); 24acb3e041SCong Wang sum += carry; 25acb3e041SCong Wang 26acb3e041SCong Wang sum += (__force u32)saddr->s6_addr32[2]; 27acb3e041SCong Wang carry = (sum < (__force u32)saddr->s6_addr32[2]); 28acb3e041SCong Wang sum += carry; 29acb3e041SCong Wang 30acb3e041SCong Wang sum += (__force u32)saddr->s6_addr32[3]; 31acb3e041SCong Wang carry = (sum < (__force u32)saddr->s6_addr32[3]); 32acb3e041SCong Wang sum += carry; 33acb3e041SCong Wang 34acb3e041SCong Wang sum += (__force u32)daddr->s6_addr32[0]; 35acb3e041SCong Wang carry = (sum < (__force u32)daddr->s6_addr32[0]); 36acb3e041SCong Wang sum += carry; 37acb3e041SCong Wang 38acb3e041SCong Wang sum += (__force u32)daddr->s6_addr32[1]; 39acb3e041SCong Wang carry = (sum < (__force u32)daddr->s6_addr32[1]); 40acb3e041SCong Wang sum += carry; 41acb3e041SCong Wang 42acb3e041SCong Wang sum += (__force u32)daddr->s6_addr32[2]; 43acb3e041SCong Wang carry = (sum < (__force u32)daddr->s6_addr32[2]); 44acb3e041SCong Wang sum += carry; 45acb3e041SCong Wang 46acb3e041SCong Wang sum += (__force u32)daddr->s6_addr32[3]; 47acb3e041SCong Wang carry = (sum < (__force u32)daddr->s6_addr32[3]); 48acb3e041SCong Wang sum += carry; 49acb3e041SCong Wang 50acb3e041SCong Wang ulen = (__force u32)htonl((__u32) len); 51acb3e041SCong Wang sum += ulen; 52acb3e041SCong Wang carry = (sum < ulen); 53acb3e041SCong Wang sum += carry; 54acb3e041SCong Wang 55acb3e041SCong Wang uproto = (__force u32)htonl(proto); 56acb3e041SCong Wang sum += uproto; 57acb3e041SCong Wang carry = (sum < uproto); 58acb3e041SCong Wang sum += carry; 59acb3e041SCong Wang 60acb3e041SCong Wang return csum_fold((__force __wsum)sum); 61acb3e041SCong Wang } 62acb3e041SCong Wang EXPORT_SYMBOL(csum_ipv6_magic); 63acb3e041SCong Wang #endif 64acb3e041SCong Wang 65acb3e041SCong Wang int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) 66acb3e041SCong Wang { 67acb3e041SCong Wang int err; 68acb3e041SCong Wang 69acb3e041SCong Wang UDP_SKB_CB(skb)->partial_cov = 0; 70acb3e041SCong Wang UDP_SKB_CB(skb)->cscov = skb->len; 71acb3e041SCong Wang 72acb3e041SCong Wang if (proto == IPPROTO_UDPLITE) { 73acb3e041SCong Wang err = udplite_checksum_init(skb, uh); 74acb3e041SCong Wang if (err) 75acb3e041SCong Wang return err; 76acb3e041SCong Wang } 77acb3e041SCong Wang 784068579eSTom Herbert /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels) 794068579eSTom Herbert * we accept a checksum of zero here. When we find the socket 804068579eSTom Herbert * for the UDP packet we'll check if that socket allows zero checksum 814068579eSTom Herbert * for IPv6 (set by socket option). 82acb3e041SCong Wang */ 834068579eSTom Herbert return skb_checksum_init_zero_check(skb, proto, uh->check, 844068579eSTom Herbert ip6_compute_pseudo); 85acb3e041SCong Wang } 86acb3e041SCong Wang EXPORT_SYMBOL(udp6_csum_init); 87af5fcba7STom Herbert 88af5fcba7STom Herbert /* Function to set UDP checksum for an IPv6 UDP packet. This is intended 89af5fcba7STom Herbert * for the simple case like when setting the checksum for a UDP tunnel. 90af5fcba7STom Herbert */ 91af5fcba7STom Herbert void udp6_set_csum(bool nocheck, struct sk_buff *skb, 92af5fcba7STom Herbert const struct in6_addr *saddr, 93af5fcba7STom Herbert const struct in6_addr *daddr, int len) 94af5fcba7STom Herbert { 95af5fcba7STom Herbert struct udphdr *uh = udp_hdr(skb); 96af5fcba7STom Herbert 97af5fcba7STom Herbert if (nocheck) 98af5fcba7STom Herbert uh->check = 0; 99af5fcba7STom Herbert else if (skb_is_gso(skb)) 100af5fcba7STom Herbert uh->check = ~udp_v6_check(len, saddr, daddr, 0); 101*179bc67fSEdward Cree else if (skb->ip_summed == CHECKSUM_PARTIAL) { 102*179bc67fSEdward Cree uh->check = 0; 103*179bc67fSEdward Cree uh->check = udp_v6_check(len, saddr, daddr, lco_csum(skb)); 104*179bc67fSEdward Cree if (uh->check == 0) 105*179bc67fSEdward Cree uh->check = CSUM_MANGLED_0; 106*179bc67fSEdward Cree } else if (skb_dst(skb) && skb_dst(skb)->dev && 107af5fcba7STom Herbert (skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) { 108af5fcba7STom Herbert skb->ip_summed = CHECKSUM_PARTIAL; 109af5fcba7STom Herbert skb->csum_start = skb_transport_header(skb) - skb->head; 110af5fcba7STom Herbert skb->csum_offset = offsetof(struct udphdr, check); 111af5fcba7STom Herbert uh->check = ~udp_v6_check(len, saddr, daddr, 0); 112af5fcba7STom Herbert } else { 113af5fcba7STom Herbert __wsum csum; 114af5fcba7STom Herbert 115af5fcba7STom Herbert uh->check = 0; 116af5fcba7STom Herbert csum = skb_checksum(skb, 0, len, 0); 117af5fcba7STom Herbert uh->check = udp_v6_check(len, saddr, daddr, csum); 118af5fcba7STom Herbert if (uh->check == 0) 119af5fcba7STom Herbert uh->check = CSUM_MANGLED_0; 120af5fcba7STom Herbert 121af5fcba7STom Herbert skb->ip_summed = CHECKSUM_UNNECESSARY; 122af5fcba7STom Herbert } 123af5fcba7STom Herbert } 124af5fcba7STom Herbert EXPORT_SYMBOL(udp6_set_csum); 125