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 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/mbuf.h> 38 #include <sys/in_cksum.h> 39 40 #include <netinet/in.h> 41 #include <netinet/in_systm.h> 42 #include <netinet/ip.h> 43 #include <netinet/ip_var.h> 44 45 #include <machine/endian.h> 46 47 /* 48 * Return the 16 bit 1's complement checksum in network byte order. Devolve 49 * the mbuf into 32 bit aligned segments that we can pass to assembly and 50 * do the rest manually. Even though we return a 16 bit unsigned value, 51 * we declare it as a 32 bit unsigned value to reduce unnecessary assembly 52 * conversions. 53 * 54 * Byte ordering issues. Note two things. First, no secondary carry occurs, 55 * and second, a one's complement checksum is endian-independant. If we are 56 * given a data buffer in network byte order, our checksum will be in network 57 * byte order. 58 * 59 * 0xffff + 0xffff = 0xfffe + C = 0xffff (so no second carry occurs). 60 * 61 * 0x8142 + 0x8243 = 0x0385 + C = 0x0386 (checksum is in same byte order 62 * 0x4281 + 0x4382 = 0x8603 as the data regardless of arch) 63 * 64 * This works with 16, 32, 64, etc... bits as long as we deal with the 65 * carry when collapsing it back down to 16 bits. 66 */ 67 68 uint32_t 69 in_cksum_range(const struct mbuf *m, int nxt, int offset, int bytes) 70 { 71 const uint8_t *ptr; 72 uint32_t sum0; 73 uint32_t sum1; 74 int n; 75 int flip; 76 77 sum0 = 0; 78 sum1 = 0; 79 flip = 0; 80 81 if (nxt != 0) { 82 uint32_t sum32; 83 struct ipovly ipov; 84 85 /* pseudo header */ 86 if (offset < sizeof(struct ipovly)) 87 panic("in_cksum_range: offset too short"); 88 if (m->m_len < sizeof(struct ip)) 89 panic("in_cksum_range: bad mbuf chain"); 90 bzero(&ipov, sizeof ipov); 91 ipov.ih_len = htons(bytes); 92 ipov.ih_pr = nxt; 93 ipov.ih_src = mtod(m, const struct ip *)->ip_src; 94 ipov.ih_dst = mtod(m, const struct ip *)->ip_dst; 95 ptr = (const uint8_t *)&ipov; 96 97 sum32 = asm_ones32(ptr, sizeof(ipov) / 4); 98 sum32 = (sum32 >> 16) + (sum32 & 0xffff); 99 if (flip) 100 sum1 += sum32; 101 else 102 sum0 += sum32; 103 } 104 105 /* 106 * Skip fully engulfed mbufs. Branch predict optimal. 107 */ 108 while (m && offset >= m->m_len) { 109 offset -= m->m_len; 110 m = m->m_next; 111 } 112 113 /* 114 * Process the checksum for each segment. Note that the code below is 115 * branch-predict optimal, so it's faster then you might otherwise 116 * believe. When we are buffer-aligned but also odd-byte-aligned from 117 * the point of view of the IP packet, we accumulate to sum1 instead of 118 * sum0. 119 * 120 * Initial offsets do not pre-set flip (assert that offset is even?) 121 */ 122 while (bytes > 0 && m) { 123 /* 124 * Calculate pointer base and number of bytes to snarf, account 125 * for snarfed bytes. 126 */ 127 ptr = mtod(m, const uint8_t *) + offset; 128 if ((n = m->m_len - offset) > bytes) 129 n = bytes; 130 bytes -= n; 131 132 /* 133 * First 16-bit-align our buffer by eating a byte if necessary, 134 * then 32-bit-align our buffer by eating a word if necessary. 135 * 136 * We are endian-sensitive when chomping a byte. WARNING! Be 137 * careful optimizing this! 16 ane 32 bit words must be aligned 138 * for this to be generic code. 139 */ 140 if (((intptr_t)ptr & 1) && n) { 141 #if BYTE_ORDER == LITTLE_ENDIAN 142 if (flip) 143 sum1 += ptr[0]; 144 else 145 sum0 += ptr[0]; 146 #else 147 if (flip) 148 sum0 += ptr[0]; 149 else 150 sum1 += ptr[0]; 151 #endif 152 ++ptr; 153 --n; 154 flip = 1 - flip; 155 } 156 if (((intptr_t)ptr & 2) && n > 1) { 157 if (flip) 158 sum1 += *(const uint16_t *)ptr; 159 else 160 sum0 += *(const uint16_t *)ptr; 161 ptr += 2; 162 n -= 2; 163 } 164 165 /* 166 * Process a 32-bit aligned data buffer and accumulate the result 167 * in sum0 or sum1. Allow only one 16 bit overflow carry. 168 */ 169 if (n >= 4) { 170 uint32_t sum32; 171 172 sum32 = asm_ones32((const void *)ptr, n >> 2); 173 sum32 = (sum32 >> 16) + (sum32 & 0xffff); 174 if (flip) 175 sum1 += sum32; 176 else 177 sum0 += sum32; 178 ptr += n & ~3; 179 /* n &= 3; dontcare */ 180 } 181 182 /* 183 * Handle oddly-sized buffers. Handle word issues first while 184 * ptr is still aligned. 185 */ 186 if (n & 2) { 187 if (flip) 188 sum1 += *(const uint16_t *)ptr; 189 else 190 sum0 += *(const uint16_t *)ptr; 191 ptr += 2; 192 /* n -= 2; dontcare */ 193 } 194 if (n & 1) { 195 #if BYTE_ORDER == LITTLE_ENDIAN 196 if (flip) 197 sum1 += ptr[0]; 198 else 199 sum0 += ptr[0]; 200 #else 201 if (flip) 202 sum0 += ptr[0]; 203 else 204 sum1 += ptr[0]; 205 #endif 206 /* ++ptr; dontcare */ 207 /* --n; dontcare */ 208 flip = 1 - flip; 209 } 210 m = m->m_next; 211 offset = 0; 212 } 213 214 /* 215 * Due to byte aligned or oddly-sized buffers we may have a checksum 216 * in sum1 which needs to be shifted and added to our main sum. There 217 * is a presumption here that no more then 255 overflows occured which 218 * is 255/3 byte aligned mbufs in the worst case. 219 */ 220 sum0 += sum1 << 8; 221 sum0 = (sum0 >> 16) + (sum0 & 0xffff); 222 if (sum0 > 0xffff) 223 ++sum0; 224 return(~sum0 & 0xffff); 225 } 226 227