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