linux/net/ipv6/ip6_checksum.c
<<
>>
Prefs
   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}
  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         * Note, we are only interested in != 0 or == 0, thus the
  84         * force to int.
  85         */
  86        return (__force int)skb_checksum_init_zero_check(skb, proto, uh->check,
  87                                                         ip6_compute_pseudo);
  88}
  89EXPORT_SYMBOL(udp6_csum_init);
  90
  91/* Function to set UDP checksum for an IPv6 UDP packet. This is intended
  92 * for the simple case like when setting the checksum for a UDP tunnel.
  93 */
  94void udp6_set_csum(bool nocheck, struct sk_buff *skb,
  95                   const struct in6_addr *saddr,
  96                   const struct in6_addr *daddr, int len)
  97{
  98        struct udphdr *uh = udp_hdr(skb);
  99
 100        if (nocheck)
 101                uh->check = 0;
 102        else if (skb_is_gso(skb))
 103                uh->check = ~udp_v6_check(len, saddr, daddr, 0);
 104        else if (skb->ip_summed == CHECKSUM_PARTIAL) {
 105                uh->check = 0;
 106                uh->check = udp_v6_check(len, saddr, daddr, lco_csum(skb));
 107                if (uh->check == 0)
 108                        uh->check = CSUM_MANGLED_0;
 109        } else {
 110                skb->ip_summed = CHECKSUM_PARTIAL;
 111                skb->csum_start = skb_transport_header(skb) - skb->head;
 112                skb->csum_offset = offsetof(struct udphdr, check);
 113                uh->check = ~udp_v6_check(len, saddr, daddr, 0);
 114        }
 115}
 116EXPORT_SYMBOL(udp6_set_csum);
 117