X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/9bccf70c0258c7cac2dcb80011b2a964d884c552..527f99514973766e9c0382a4d8550dfb00f54939:/bsd/netinet6/in6_cksum.c diff --git a/bsd/netinet6/in6_cksum.c b/bsd/netinet6/in6_cksum.c index 60cdf2e5b..394ea35b2 100644 --- a/bsd/netinet6/in6_cksum.c +++ b/bsd/netinet6/in6_cksum.c @@ -1,5 +1,30 @@ -/* $FreeBSD: src/sys/netinet6/in6_cksum.c,v 1.1.2.3 2001/07/03 11:01:52 ume Exp $ */ -/* $KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $ */ +/* + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -65,13 +90,43 @@ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 */ +/*- + * Copyright (c) 2008 Joerg Sonnenberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + #include +#include #include #include +#include #include #include - -#include +#include /* * Checksum routine for Internet Protocol family headers (Portable Version). @@ -80,242 +135,144 @@ * code and should be modified for each CPU to be as fast as possible. */ -#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) -#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} - /* - * m MUST contain a continuous IP6 header. - * off is a offset where TCP/UDP/ICMP6 header starts. - * len is a total length of a transport segment. - * (e.g. TCP header + TCP payload) + * Compute IPv6 pseudo-header checksum; assumes 16-bit aligned pointers. */ - -int -in6_cksum(m, nxt, off, len) - struct mbuf *m; - u_int8_t nxt; - u_int32_t off, len; +uint16_t +in6_pseudo(const struct in6_addr *src, const struct in6_addr *dst, uint32_t x) { - u_int16_t *w; - int sum = 0; - int mlen = 0; - int byte_swapped = 0; -#if 0 - int srcifid = 0, dstifid = 0; -#endif - struct ip6_hdr *ip6; - union { - u_int16_t phs[4]; - struct { - u_int32_t ph_len; - u_int8_t ph_zero[3]; - u_int8_t ph_nxt; - } ph __attribute__((__packed__)); - } uph; - union { - u_int8_t c[2]; - u_int16_t s; - } s_util; - union { - u_int16_t s[2]; - u_int32_t l; - } l_util; - - /* sanity check */ - if (m->m_pkthdr.len < off + len) { - panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)\n", - m->m_pkthdr.len, off, len); - } - - bzero(&uph, sizeof(uph)); + uint32_t sum = 0; + const uint16_t *w; /* - * First create IP6 pseudo header and calculate a summary. + * IPv6 source address */ - ip6 = mtod(m, struct ip6_hdr *); -#if 0 - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { - srcifid = ip6->ip6_src.s6_addr16[1]; - ip6->ip6_src.s6_addr16[1] = 0; - } - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { - dstifid = ip6->ip6_dst.s6_addr16[1]; - ip6->ip6_dst.s6_addr16[1] = 0; - } -#endif - w = (u_int16_t *)&ip6->ip6_src; - uph.ph.ph_len = htonl(len); - uph.ph.ph_nxt = nxt; - - /* IPv6 source address */ + w = (const uint16_t *)src; sum += w[0]; - if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + if (!IN6_IS_SCOPE_EMBED(src)) sum += w[1]; sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; - /* IPv6 destination address */ - sum += w[8]; - if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) - sum += w[9]; - sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13]; - sum += w[14]; sum += w[15]; - /* Payload length and upper layer identifier */ - sum += uph.phs[0]; sum += uph.phs[1]; - sum += uph.phs[2]; sum += uph.phs[3]; -#if 0 - if (srcifid) - ip6->ip6_src.s6_addr16[1] = srcifid; - if (dstifid) - ip6->ip6_dst.s6_addr16[1] = dstifid; -#endif - /* - * Secondly calculate a summary of the first mbuf excluding offset. - */ - while (m != NULL && off > 0) { - if (m->m_len <= off) - off -= m->m_len; - else - break; - m = m->m_next; - } - w = (u_int16_t *)(mtod(m, u_char *) + off); - mlen = m->m_len - off; - if (len < mlen) - mlen = len; - len -= mlen; /* - * Force to even boundary. + * IPv6 destination address */ - if ((1 & (long) w) && (mlen > 0)) { - REDUCE; - sum <<= 8; - s_util.c[0] = *(u_char *)w; - w = (u_int16_t *)((char *)w + 1); - mlen--; - byte_swapped = 1; - } - /* - * Unroll the loop to make overhead from - * branches &c small. - */ - while ((mlen -= 32) >= 0) { - sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; - sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; - sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; - sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; - w += 16; - } - mlen += 32; - while ((mlen -= 8) >= 0) { - sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; - w += 4; - } - mlen += 8; - if (mlen == 0 && byte_swapped == 0) - goto next; - REDUCE; - while ((mlen -= 2) >= 0) { - sum += *w++; - } - if (byte_swapped) { - REDUCE; - sum <<= 8; - byte_swapped = 0; - if (mlen == -1) { - s_util.c[1] = *(char *)w; - sum += s_util.s; - mlen = 0; - } else - mlen = -1; - } else if (mlen == -1) - s_util.c[0] = *(char *)w; - next: - m = m->m_next; + w = (const uint16_t *)dst; + sum += w[0]; + if (!IN6_IS_SCOPE_EMBED(dst)) + sum += w[1]; + sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; + sum += w[6]; sum += w[7]; /* - * Lastly calculate a summary of the rest of mbufs. - */ - - for (;m && len; m = m->m_next) { - if (m->m_len == 0) - continue; - w = mtod(m, u_int16_t *); - if (mlen == -1) { - /* - * The first byte of this mbuf is the continuation - * of a word spanning between this mbuf and the - * last mbuf. - * - * s_util.c[0] is already saved when scanning previous - * mbuf. - */ - s_util.c[1] = *(char *)w; - sum += s_util.s; - w = (u_int16_t *)((char *)w + 1); - mlen = m->m_len - 1; - len--; - } else - mlen = m->m_len; - if (len < mlen) - mlen = len; - len -= mlen; + * Caller-supplied value; 'x' could be one of: + * + * htonl(proto + length), or + * htonl(proto + length + sum) + **/ + sum += x; + + /* fold in carry bits */ + ADDCARRY(sum); + + return (sum); +} + +/* + * m MUST contain at least an IPv6 header, if nxt is specified; + * nxt is the upper layer protocol number; + * off is an offset where TCP/UDP/ICMP6 header starts; + * len is a total length of a transport segment (e.g. TCP header + TCP payload) + */ +u_int16_t +inet6_cksum(struct mbuf *m, uint32_t nxt, uint32_t off, uint32_t len) +{ + uint32_t sum; + + sum = m_sum16(m, off, len); + + if (nxt != 0) { + struct ip6_hdr *ip6; + unsigned char buf[sizeof (*ip6)] __attribute__((aligned(8))); + uint32_t mlen; + /* - * Force to even boundary. + * Sanity check + * + * Use m_length2() instead of m_length(), as we cannot rely on + * the caller setting m_pkthdr.len correctly, if the mbuf is + * a M_PKTHDR one. */ - if ((1 & (long) w) && (mlen > 0)) { - REDUCE; - sum <<= 8; - s_util.c[0] = *(u_char *)w; - w = (u_int16_t *)((char *)w + 1); - mlen--; - byte_swapped = 1; + if ((mlen = m_length2(m, NULL)) < sizeof (*ip6)) { + panic("%s: mbuf %p pkt too short (%d) for IPv6 header", + __func__, m, mlen); + /* NOTREACHED */ } + /* - * Unroll the loop to make overhead from - * branches &c small. + * In case the IPv6 header is not contiguous, or not 32-bit + * aligned, copy it to a local buffer. Note here that we + * expect the data pointer to point to the IPv6 header. */ - while ((mlen -= 32) >= 0) { - sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; - sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; - sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; - sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; - w += 16; + if ((sizeof (*ip6) > m->m_len) || + !IP6_HDR_ALIGNED_P(mtod(m, caddr_t))) { + m_copydata(m, 0, sizeof (*ip6), (caddr_t)buf); + ip6 = (struct ip6_hdr *)(void *)buf; + } else { + ip6 = (struct ip6_hdr *)(void *)(m->m_data); } - mlen += 32; - while ((mlen -= 8) >= 0) { - sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; - w += 4; - } - mlen += 8; - if (mlen == 0 && byte_swapped == 0) - continue; - REDUCE; - while ((mlen -= 2) >= 0) { - sum += *w++; - } - if (byte_swapped) { - REDUCE; - sum <<= 8; - byte_swapped = 0; - if (mlen == -1) { - s_util.c[1] = *(char *)w; - sum += s_util.s; - mlen = 0; - } else - mlen = -1; - } else if (mlen == -1) - s_util.c[0] = *(char *)w; + + /* add pseudo header checksum */ + sum += in6_pseudo(&ip6->ip6_src, &ip6->ip6_dst, + htonl(nxt + len)); + + /* fold in carry bits */ + ADDCARRY(sum); } - if (len) - panic("in6_cksum: out of data\n"); - if (mlen == -1) { - /* The last mbuf has odd # of bytes. Follow the - standard (the odd byte may be shifted left by 8 bits - or not as determined by endian-ness of the machine) */ - s_util.c[1] = 0; - sum += s_util.s; + + return (~sum & 0xffff); +} + +/* + * buffer MUST contain at least an IPv6 header, if nxt is specified; + * nxt is the upper layer protocol number; + * off is an offset where TCP/UDP/ICMP6 header starts; + * len is a total length of a transport segment (e.g. TCP header + TCP payload) + */ +u_int16_t +inet6_cksum_buffer(const uint8_t *buffer, uint32_t nxt, uint32_t off, + uint32_t len) +{ + uint32_t sum; + + if (off >= len) + panic("%s: off (%d) >= len (%d)", __func__, off, len); + + sum = b_sum16(&((const uint8_t *)buffer)[off], len); + + if (nxt != 0) { + const struct ip6_hdr *ip6; + unsigned char buf[sizeof (*ip6)] __attribute__((aligned(8))); + + /* + * In case the IPv6 header is not contiguous, or not 32-bit + * aligned, copy it to a local buffer. Note here that we + * expect the data pointer to point to the IPv6 header. + */ + if (!IP6_HDR_ALIGNED_P(buffer)) { + memcpy(buf, buffer, sizeof (*ip6)); + ip6 = (const struct ip6_hdr *)(const void *)buf; + } else { + ip6 = (const struct ip6_hdr *)buffer; + } + + /* add pseudo header checksum */ + sum += in6_pseudo(&ip6->ip6_src, &ip6->ip6_dst, + htonl(nxt + len)); + + /* fold in carry bits */ + ADDCARRY(sum); } - REDUCE; + return (~sum & 0xffff); }