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