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, unsigned short proto,
  10                        __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}
  62EXPORT_SYMBOL(csum_ipv6_magic);
  63#endif
  64
  65int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto)
  66{
  67        int err;
  68
  69        UDP_SKB_CB(skb)->partial_cov = 0;
  70        UDP_SKB_CB(skb)->cscov = skb->len;
  71
  72        if (proto == IPPROTO_UDPLITE) {
  73                err = udplite_checksum_init(skb, uh);
  74                if (err)
  75                        return err;
  76        }
  77
  78        /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels)
  79         * we accept a checksum of zero here. When we find the socket
  80         * for the UDP packet we'll check if that socket allows zero checksum
  81         * for IPv6 (set by socket option).
  82         */
  83        return skb_checksum_init_zero_check(skb, proto, uh->check,
  84                                           ip6_compute_pseudo);
  85}
  86EXPORT_SYMBOL(udp6_csum_init);
  87
  88/* Function to set UDP checksum for an IPv6 UDP packet. This is intended
  89 * for the simple case like when setting the checksum for a UDP tunnel.
  90 */
  91void udp6_set_csum(bool nocheck, struct sk_buff *skb,
  92                   const struct in6_addr *saddr,
  93                   const struct in6_addr *daddr, int len)
  94{
  95        struct udphdr *uh = udp_hdr(skb);
  96
  97        if (nocheck)
  98                uh->check = 0;
  99        else if (skb_is_gso(skb))
 100                uh->check = ~udp_v6_check(len, saddr, daddr, 0);
 101        else if (skb_dst(skb) && skb_dst(skb)->dev &&
 102                 (skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) {
 103
 104                BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
 105
 106                skb->ip_summed = CHECKSUM_PARTIAL;
 107                skb->csum_start = skb_transport_header(skb) - skb->head;
 108                skb->csum_offset = offsetof(struct udphdr, check);
 109                uh->check = ~udp_v6_check(len, saddr, daddr, 0);
 110        } else {
 111                __wsum csum;
 112
 113                BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
 114
 115                uh->check = 0;
 116                csum = skb_checksum(skb, 0, len, 0);
 117                uh->check = udp_v6_check(len, saddr, daddr, csum);
 118                if (uh->check == 0)
 119                        uh->check = CSUM_MANGLED_0;
 120
 121                skb->ip_summed = CHECKSUM_UNNECESSARY;
 122        }
 123}
 124EXPORT_SYMBOL(udp6_set_csum);
 125