xref: /netbsd-src/sys/netinet6/in6_cksum.c (revision e86be17a4fbf4acb1af2f89ef9350561d55f86ec)
1 /*	$NetBSD: in6_cksum.c,v 1.28 2011/04/25 22:05:05 yamt Exp $	*/
2 
3 /*-
4  * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: in6_cksum.c,v 1.28 2011/04/25 22:05:05 yamt Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/mbuf.h>
37 #include <netinet/in.h>
38 #include <netinet/ip6.h>
39 
40 /*
41  * Checksum of the IPv6 pseudo header.
42  *
43  * off is supposed to be the skipped IPv6 header, len is the payload size.
44  */
45 
46 int
in6_cksum(struct mbuf * m,u_int8_t nxt,uint32_t off,uint32_t len)47 in6_cksum(struct mbuf *m, u_int8_t nxt, uint32_t off, uint32_t len)
48 {
49 	union {
50 		uint16_t words[16];
51 		struct {
52 			struct in6_addr ip6_src;
53 			struct in6_addr ip6_dst;
54 		} addrs;
55 	} u;
56 	const struct in6_addr *in6_src;
57 	const struct in6_addr *in6_dst;
58 	const struct ip6_hdr *ip6;
59 	uint32_t sum;
60 	const uint16_t *w;
61 	const char *cp;
62 
63 	if (nxt == 0)
64 		return cpu_in_cksum(m, len, off, 0);
65 
66 	if (__predict_false(off < sizeof(struct ip6_hdr)))
67 		panic("in6_cksum: offset too short for IPv6 header");
68 	if (__predict_false(m->m_len < sizeof(struct ip6_hdr)))
69 		panic("in6_cksum: mbuf too short for IPv6 header");
70 
71 	/*
72 	 * Compute the equivalent of:
73 	 * struct ip6_hdr_pseudo ip6;
74 	 *
75 	 * bzero(sizeof(*ip6));
76 	 * ip6.ip6ph_nxt = nxt;
77 	 * ip6.ip6ph_len = htonl(len);
78 	 * ipv6.ip6ph_src = mtod(m, struct ip6_hdr *)->ip6_src;
79 	 * in6_clearscope(&ip6->ip6ph_src);
80 	 * ipv6.ip6ph_dst = mtod(m, struct ip6_hdr *)->ip6_dst;
81 	 * in6_clearscope(&ip6->ip6ph_dst);
82 	 * sum = one_add(&ip6);
83 	 */
84 
85 #if BYTE_ORDER == LITTLE_ENDIAN
86 	sum = ((len & 0xffff) + ((len >> 16) & 0xffff) + nxt) << 8;
87 #else
88 	sum = (len & 0xffff) + ((len >> 16) & 0xffff) + nxt;
89 #endif
90 	cp = mtod(m, const char *);
91 	w = (const uint16_t *)(cp + offsetof(struct ip6_hdr, ip6_src));
92 	ip6 = (const void *)cp;
93 	if (__predict_true((uintptr_t)w % 2 == 0)) {
94 		in6_src = &ip6->ip6_src;
95 		in6_dst = &ip6->ip6_dst;
96 	} else {
97 		memcpy(&u, &ip6->ip6_src, 32);
98 		w = u.words;
99 		in6_src = &u.addrs.ip6_src;
100 		in6_dst = &u.addrs.ip6_dst;
101 	}
102 
103 	sum += w[0];
104 	if (!IN6_IS_SCOPE_EMBEDDABLE(in6_src))
105 		sum += w[1];
106 	sum += w[2];
107 	sum += w[3];
108 	sum += w[4];
109 	sum += w[5];
110 	sum += w[6];
111 	sum += w[7];
112 	w += 8;
113 	sum += w[0];
114 	if (!IN6_IS_SCOPE_EMBEDDABLE(in6_dst))
115 		sum += w[1];
116 	sum += w[2];
117 	sum += w[3];
118 	sum += w[4];
119 	sum += w[5];
120 	sum += w[6];
121 	sum += w[7];
122 
123 	return cpu_in_cksum(m, len, off, sum);
124 }
125