1 /* 2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 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 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sys/netinet/in_cksum.c,v 1.5 2004/07/16 05:51:19 dillon Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/mbuf.h> 40 #include <sys/in_cksum.h> 41 42 #include <netinet/in.h> 43 #include <netinet/in_systm.h> 44 #include <netinet/ip.h> 45 46 #include <machine/endian.h> 47 48 /* 49 * Return the 16 bit 1's complement checksum in network byte order. Devolve 50 * the mbuf into 32 bit aligned segments that we can pass to assembly and 51 * do the rest manually. Even though we return a 16 bit unsigned value, 52 * we declare it as a 32 bit unsigned value to reduce unnecessary assembly 53 * conversions. 54 * 55 * Byte ordering issues. Note two things. First, no secondary carry occurs, 56 * and second, a one's complement checksum is endian-independant. If we are 57 * given a data buffer in network byte order, our checksum will be in network 58 * byte order. 59 * 60 * 0xffff + 0xffff = 0xfffe + C = 0xffff (so no second carry occurs). 61 * 62 * 0x8142 + 0x8243 = 0x0385 + C = 0x0386 (checksum is in same byte order 63 * 0x4281 + 0x4382 = 0x8603 as the data regardless of arch) 64 * 65 * This works with 16, 32, 64, etc... bits as long as we deal with the 66 * carry when collapsing it back down to 16 bits. 67 */ 68 __uint32_t 69 in_cksum_range(struct mbuf *m, int offset, int bytes) 70 { 71 __uint8_t *ptr; 72 __uint32_t sum0; 73 __uint32_t sum1; 74 int n; 75 int flip; 76 77 /* 78 * Skip fully engulfed mbufs. Branch predict optimal. 79 */ 80 while (m && offset >= m->m_len) { 81 offset -= m->m_len; 82 m = m->m_next; 83 } 84 85 /* 86 * Process the checksum for each segment. Note that the code below is 87 * branch-predict optimal, so it's faster then you might otherwise 88 * believe. When we are buffer-aligned but also odd-byte-aligned from 89 * the point of view of the IP packet, we accumulate to sum1 instead of 90 * sum0. 91 * 92 * Initial offsets do not pre-set flip (assert that offset is even?) 93 */ 94 sum0 = 0; 95 sum1 = 0; 96 flip = 0; 97 while (bytes > 0 && m) { 98 /* 99 * Calculate pointer base and number of bytes to snarf, account 100 * for snarfed bytes. 101 */ 102 ptr = mtod(m, __uint8_t *) + offset; 103 if ((n = m->m_len - offset) > bytes) 104 n = bytes; 105 bytes -= n; 106 107 /* 108 * First 16-bit-align our buffer by eating a byte if necessary, 109 * then 32-bit-align our buffer by eating a word if necessary. 110 * 111 * We are endian-sensitive when chomping a byte. WARNING! Be 112 * careful optimizing this! 16 ane 32 bit words must be aligned 113 * for this to be generic code. 114 */ 115 if (((intptr_t)ptr & 1) && n) { 116 #if BYTE_ORDER == LITTLE_ENDIAN 117 if (flip) 118 sum1 += ptr[0]; 119 else 120 sum0 += ptr[0]; 121 #else 122 if (flip) 123 sum0 += ptr[0]; 124 else 125 sum1 += ptr[0]; 126 #endif 127 ++ptr; 128 --n; 129 flip = 1 - flip; 130 } 131 if (((intptr_t)ptr & 2) && n > 1) { 132 if (flip) 133 sum1 += *(__uint16_t *)ptr; 134 else 135 sum0 += *(__uint16_t *)ptr; 136 ptr += 2; 137 n -= 2; 138 } 139 140 /* 141 * Process a 32-bit aligned data buffer and accumulate the result 142 * in sum0 or sum1. Allow only one 16 bit overflow carry. 143 */ 144 if (n >= 4) { 145 __uint32_t sum32; 146 147 sum32 = asm_ones32((void *)ptr, n >> 2); 148 sum32 = (sum32 >> 16) + (sum32 & 0xffff); 149 if (flip) 150 sum1 += sum32; 151 else 152 sum0 += sum32; 153 ptr += n & ~3; 154 /* n &= 3; dontcare */ 155 } 156 157 /* 158 * Handle oddly-sized buffers. Handle word issues first while 159 * ptr is still aligned. 160 */ 161 if (n & 2) { 162 if (flip) 163 sum1 += *(__uint16_t *)ptr; 164 else 165 sum0 += *(__uint16_t *)ptr; 166 ptr += 2; 167 /* n -= 2; dontcare */ 168 } 169 if (n & 1) { 170 #if BYTE_ORDER == LITTLE_ENDIAN 171 if (flip) 172 sum1 += ptr[0]; 173 else 174 sum0 += ptr[0]; 175 #else 176 if (flip) 177 sum0 += ptr[0]; 178 else 179 sum1 += ptr[0]; 180 #endif 181 /* ++ptr; dontcare */ 182 /* --n; dontcare */ 183 flip = 1 - flip; 184 } 185 m = m->m_next; 186 offset = 0; 187 } 188 189 /* 190 * Due to byte aligned or oddly-sized buffers we may have a checksum 191 * in sum1 which needs to be shifted and added to our main sum. There 192 * is a presumption here that no more then 255 overflows occured which 193 * is 255/3 byte aligned mbufs in the worst case. 194 */ 195 sum0 += sum1 << 8; 196 sum0 = (sum0 >> 16) + (sum0 & 0xffff); 197 if (sum0 > 0xffff) 198 ++sum0; 199 return(~sum0 & 0xffff); 200 } 201 202