1 // SPDX-License-Identifier: GPL-2.0
4 #include <net/udplite.h>
5 #include <asm/checksum.h>
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
)
16 __u32 sum
= (__force u32
)csum
;
18 sum
+= (__force u32
)saddr
->s6_addr32
[0];
19 carry
= (sum
< (__force u32
)saddr
->s6_addr32
[0]);
22 sum
+= (__force u32
)saddr
->s6_addr32
[1];
23 carry
= (sum
< (__force u32
)saddr
->s6_addr32
[1]);
26 sum
+= (__force u32
)saddr
->s6_addr32
[2];
27 carry
= (sum
< (__force u32
)saddr
->s6_addr32
[2]);
30 sum
+= (__force u32
)saddr
->s6_addr32
[3];
31 carry
= (sum
< (__force u32
)saddr
->s6_addr32
[3]);
34 sum
+= (__force u32
)daddr
->s6_addr32
[0];
35 carry
= (sum
< (__force u32
)daddr
->s6_addr32
[0]);
38 sum
+= (__force u32
)daddr
->s6_addr32
[1];
39 carry
= (sum
< (__force u32
)daddr
->s6_addr32
[1]);
42 sum
+= (__force u32
)daddr
->s6_addr32
[2];
43 carry
= (sum
< (__force u32
)daddr
->s6_addr32
[2]);
46 sum
+= (__force u32
)daddr
->s6_addr32
[3];
47 carry
= (sum
< (__force u32
)daddr
->s6_addr32
[3]);
50 ulen
= (__force u32
)htonl((__u32
) len
);
55 uproto
= (__force u32
)htonl(proto
);
57 carry
= (sum
< uproto
);
60 return csum_fold((__force __wsum
)sum
);
62 EXPORT_SYMBOL(csum_ipv6_magic
);
65 int udp6_csum_init(struct sk_buff
*skb
, struct udphdr
*uh
, int proto
)
69 UDP_SKB_CB(skb
)->partial_cov
= 0;
70 UDP_SKB_CB(skb
)->cscov
= skb
->len
;
72 if (proto
== IPPROTO_UDPLITE
) {
73 err
= udplite_checksum_init(skb
, uh
);
77 if (UDP_SKB_CB(skb
)->partial_cov
) {
78 skb
->csum
= ip6_compute_pseudo(skb
, proto
);
83 /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels)
84 * we accept a checksum of zero here. When we find the socket
85 * for the UDP packet we'll check if that socket allows zero checksum
86 * for IPv6 (set by socket option).
88 * Note, we are only interested in != 0 or == 0, thus the
91 err
= (__force
int)skb_checksum_init_zero_check(skb
, proto
, uh
->check
,
96 if (skb
->ip_summed
== CHECKSUM_COMPLETE
&& !skb
->csum_valid
) {
97 /* If SW calculated the value, we know it's bad */
98 if (skb
->csum_complete_sw
)
101 /* HW says the value is bad. Let's validate that.
102 * skb->csum is no longer the full packet checksum,
103 * so don't treat is as such.
105 skb_checksum_complete_unset(skb
);
110 EXPORT_SYMBOL(udp6_csum_init
);
112 /* Function to set UDP checksum for an IPv6 UDP packet. This is intended
113 * for the simple case like when setting the checksum for a UDP tunnel.
115 void udp6_set_csum(bool nocheck
, struct sk_buff
*skb
,
116 const struct in6_addr
*saddr
,
117 const struct in6_addr
*daddr
, int len
)
119 struct udphdr
*uh
= udp_hdr(skb
);
123 else if (skb_is_gso(skb
))
124 uh
->check
= ~udp_v6_check(len
, saddr
, daddr
, 0);
125 else if (skb
->ip_summed
== CHECKSUM_PARTIAL
) {
127 uh
->check
= udp_v6_check(len
, saddr
, daddr
, lco_csum(skb
));
129 uh
->check
= CSUM_MANGLED_0
;
131 skb
->ip_summed
= CHECKSUM_PARTIAL
;
132 skb
->csum_start
= skb_transport_header(skb
) - skb
->head
;
133 skb
->csum_offset
= offsetof(struct udphdr
, check
);
134 uh
->check
= ~udp_v6_check(len
, saddr
, daddr
, 0);
137 EXPORT_SYMBOL(udp6_set_csum
);