X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/316670eb35587141e969394ae8537d66b9211e80..f427ee49d309d8fc33ebf3042c3a775f2f530ded:/bsd/netinet6/in6_cksum.c diff --git a/bsd/netinet6/in6_cksum.c b/bsd/netinet6/in6_cksum.c index bf8fe680d..41350d560 100644 --- a/bsd/netinet6/in6_cksum.c +++ b/bsd/netinet6/in6_cksum.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2009-2011 Apple Inc. All rights reserved. + * 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 @@ -11,10 +11,10 @@ * 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, @@ -22,7 +22,7 @@ * 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@ */ @@ -120,17 +120,13 @@ */ #include +#include #include #include #include #include #include - -#include - -#include - - +#include /* * Checksum routine for Internet Protocol family headers (Portable Version). @@ -139,224 +135,147 @@ * code and should be modified for each CPU to be as fast as possible. */ -#define ADDCARRY(x) do { if (x > 65535) { x -= 65535; } } while (0) -#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. */ - -u_int16_t -inet6_cksum(struct mbuf *m, unsigned int nxt, unsigned int off, - unsigned int 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; - 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_flags & M_PKTHDR) && m->m_pkthdr.len < off + len) { - panic("inet6_cksum: mbuf len (%d) < off+len (%d+%d)\n", - m->m_pkthdr.len, off, len); - } - - if (nxt != 0) { - bzero(&uph, sizeof (uph)); + uint32_t sum = 0; + const uint16_t *w; - /* - * First create IP6 pseudo header and calculate a summary. - */ - ip6 = mtod(m, struct ip6_hdr *); - w = (u_int16_t *)&ip6->ip6_src; - uph.ph.ph_len = htonl(len); - uph.ph.ph_nxt = nxt; - - /* IPv6 source address */ - sum += w[0]; - if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_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]; - } - - /* - * 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 *)(void *)(mtod(m, u_char *) + off); - mlen = m->m_len - off; - if (len < mlen) - mlen = len; - len -= mlen; /* - * Force to even boundary. + * IPv6 source address */ - if ((1 & (intptr_t) w) && (mlen > 0)) { - REDUCE; - sum <<= 8; - s_util.c[0] = *(u_char *)w; - w = (u_int16_t *)(void *)((char *)w + 1); - mlen--; - byte_swapped = 1; + w = (const uint16_t *)src; + sum += w[0]; + 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]; + /* - * Unroll the loop to make overhead from - * branches &c small. + * IPv6 destination address */ - 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; + w = (const uint16_t *)dst; + sum += w[0]; + if (!IN6_IS_SCOPE_EMBED(dst)) { + sum += w[1]; } - 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; + 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. - */ + * 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 (uint16_t)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; - 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 *)(void *)((char *)w + 1); - mlen = m->m_len - 1; - len--; - } else - mlen = m->m_len; - if (len < mlen) - mlen = len; - len -= 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 & (intptr_t) w) && (mlen > 0)) { - REDUCE; - sum <<= 8; - s_util.c[0] = *(u_char *)w; - w = (u_int16_t *)(void *)((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; - } - if (len) - printf("inet6_cksum: out of data by %d\n", len); - 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; + + /* 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); + + 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); + } + + return ~sum & 0xffff; +}