xref: /csrg-svn/sys/netns/ns_cksum.c (revision 34496)
125949Ssam /*
233597Ssklower  * Copyright (c) 1982, 1988 Regents of the University of California.
333597Ssklower  * All rights reserved.
433597Ssklower  *
533597Ssklower  * Redistribution and use in source and binary forms are permitted
633597Ssklower  * provided that this notice is preserved and that due credit is given
733597Ssklower  * to the University of California at Berkeley. The name of the University
833597Ssklower  * may not be used to endorse or promote products derived from this
933597Ssklower  * software without specific prior written permission. This software
1033597Ssklower  * is provided ``as is'' without express or implied warranty.
1133597Ssklower  *
12*34496Skarels  *      @(#)ns_cksum.c	7.2 (Berkeley) 05/26/88
1325949Ssam  */
1425949Ssam #include "types.h"
1525949Ssam #include "mbuf.h"
1625949Ssam /*
1733634Ssklower  * Checksum routine for Network Systems Protocol Packets (Big-Endian).
1825949Ssam  *
1933597Ssklower  * This routine is very heavily used in the network
2033597Ssklower  * code and should be modified for each CPU to be as fast as possible.
2125949Ssam  */
2233597Ssklower 
23*34496Skarels #define ADDCARRY(x)  { if ((x) > 65535) (x) -= 65535; }
24*34496Skarels #define FOLD(x) {l_util.l = (x); (x) = l_util.s[0] + l_util.s[1]; ADDCARRY(x);}
2533597Ssklower 
2625949Ssam u_short
2725949Ssam ns_cksum(m, len)
2825949Ssam 	register struct mbuf *m;
2925949Ssam 	register int len;
3025949Ssam {
3133597Ssklower 	register u_short *w;
3233597Ssklower 	register int sum = 0;
3333597Ssklower 	register int mlen = 0;
3433597Ssklower 	register int sum2;
3525949Ssam 
3633597Ssklower 	union {
3733597Ssklower 		u_short s[2];
3833597Ssklower 		long	l;
3933597Ssklower 	} l_util;
4033597Ssklower 
4133597Ssklower 	for (;m && len; m = m->m_next) {
4233597Ssklower 		if (m->m_len == 0)
4333597Ssklower 			continue;
4433597Ssklower 		/*
4533597Ssklower 		 * Each trip around loop adds in
4633597Ssklower 		 * word from one mbuf segment.
4733597Ssklower 		 */
4833597Ssklower 		w = mtod(m, u_short *);
4933597Ssklower 		if (mlen == -1) {
5033597Ssklower 			/*
5133597Ssklower 			 * There is a byte left from the last segment;
5233597Ssklower 			 * ones-complement add it into the checksum.
5333597Ssklower 			 */
5433597Ssklower 			sum  += *(u_char *)w; /* Big-Endian, else << 8 */
5533597Ssklower 			sum += sum;
5633597Ssklower 			w = (u_short *)(1 + (char *)w);
5733597Ssklower 			mlen = m->m_len - 1;
5833597Ssklower 			len--;
5933597Ssklower 			FOLD(sum);
6033597Ssklower 		} else
6133597Ssklower 			mlen = m->m_len;
6233597Ssklower 		if (len < mlen)
6333597Ssklower 			mlen = len;
6433597Ssklower 		len -= mlen;
6533597Ssklower 		/*
6633597Ssklower 		 * We can do a 16 bit ones complement sum using
6733597Ssklower 		 * 32 bit arithmetic registers for adding,
6833597Ssklower 		 * with carries from the low added
6933597Ssklower 		 * into the high (by normal carry-chaining)
7033597Ssklower 		 * so long as we fold back before 16 carries have occured.
7133597Ssklower 		 */
7233597Ssklower 		if (1 & (int) w)
7333597Ssklower 			goto uuuuglyy;
7433597Ssklower #ifndef TINY
7533597Ssklower /* -DTINY reduces the size from 1250 to 550, but slows it down by 22% */
7633597Ssklower 		while ((mlen -= 32) >= 0) {
7733597Ssklower 			sum += w[0]; sum += sum; sum += w[1]; sum += sum;
7833597Ssklower 			sum += w[2]; sum += sum; sum += w[3]; sum += sum;
7933597Ssklower 			sum += w[4]; sum += sum; sum += w[5]; sum += sum;
8033597Ssklower 			sum += w[6]; sum += sum; sum += w[7]; sum += sum;
8133597Ssklower 			FOLD(sum);
8233597Ssklower 			sum += w[8]; sum += sum; sum += w[9]; sum += sum;
8333597Ssklower 			sum += w[10]; sum += sum; sum += w[11]; sum += sum;
8433597Ssklower 			sum += w[12]; sum += sum; sum += w[13]; sum += sum;
8533597Ssklower 			sum += w[14]; sum += sum; sum += w[15]; sum += sum;
8633597Ssklower 			FOLD(sum);
8733597Ssklower 			w += 16;
8825949Ssam 		}
8933597Ssklower 		mlen += 32;
9033597Ssklower #endif
9133597Ssklower 		while ((mlen -= 8) >= 0) {
9233597Ssklower 			sum += w[0]; sum += sum; sum += w[1]; sum += sum;
9333597Ssklower 			sum += w[2]; sum += sum; sum += w[3]; sum += sum;
9433597Ssklower 			FOLD(sum);
9533597Ssklower 			w += 4;
9633597Ssklower 		}
9733597Ssklower 		mlen += 8;
9833597Ssklower 		while ((mlen -= 2) >= 0) {
9933597Ssklower 			sum += *w++; sum += sum;
10033597Ssklower 		}
10133597Ssklower 		goto commoncase;
10233597Ssklower uuuuglyy:
10333597Ssklower /* Big-Endian; else reverse ww and vv */
10433634Ssklower #define ww(n) (((u_char *)w)[n + n + 1])
10533634Ssklower #define vv(n) (((u_char *)w)[n + n])
10633597Ssklower 		sum2 = 0;
10733597Ssklower #ifndef TINY
10833597Ssklower 		while ((mlen -= 32) >= 0) {
10933597Ssklower 		    sum += ww(0); sum += sum; sum += ww(1); sum += sum;
11033597Ssklower 		    sum += ww(2); sum += sum; sum += ww(3); sum += sum;
11133597Ssklower 		    sum += ww(4); sum += sum; sum += ww(5); sum += sum;
11233597Ssklower 		    sum += ww(6); sum += sum; sum += ww(7); sum += sum;
11333597Ssklower 		    FOLD(sum);
11433597Ssklower 		    sum += ww(8); sum += sum; sum += ww(9); sum += sum;
11533597Ssklower 		    sum += ww(10); sum += sum; sum += ww(11); sum += sum;
11633597Ssklower 		    sum += ww(12); sum += sum; sum += ww(13); sum += sum;
11733597Ssklower 		    sum += ww(14); sum += sum; sum += ww(15); sum += sum;
11833597Ssklower 		    FOLD(sum);
11933597Ssklower 		    sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2;
12033597Ssklower 		    sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2;
12133597Ssklower 		    sum2 += vv(4); sum2 += sum2; sum2 += vv(5); sum2 += sum2;
12233597Ssklower 		    sum2 += vv(6); sum2 += sum2; sum2 += vv(7); sum2 += sum2;
12333597Ssklower 		    FOLD(sum2);
12433597Ssklower 		    sum2 += vv(8); sum2 += sum2; sum2 += vv(9); sum2 += sum2;
12533597Ssklower 		    sum2 += vv(10); sum2 += sum2; sum2 += vv(11); sum2 += sum2;
12633597Ssklower 		    sum2 += vv(12); sum2 += sum2; sum2 += vv(13); sum2 += sum2;
12733597Ssklower 		    sum2 += vv(14); sum2 += sum2; sum2 += vv(15); sum2 += sum2;
12833597Ssklower 		    FOLD(sum2);
12933597Ssklower 		    w += 16;
13033597Ssklower 		}
13133597Ssklower 		mlen += 32;
13233597Ssklower #endif
13333597Ssklower 		while ((mlen -= 8) >= 0) {
13433597Ssklower 		    sum += ww(0); sum += sum; sum += ww(1); sum += sum;
13533597Ssklower 		    sum += ww(2); sum += sum; sum += ww(3); sum += sum;
13633597Ssklower 		    FOLD(sum);
13733597Ssklower 		    sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2;
13833597Ssklower 		    sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2;
13933597Ssklower 		    FOLD(sum2);
14033597Ssklower 		    w += 4;
14133597Ssklower 		}
14233597Ssklower 		mlen += 8;
14333597Ssklower 		while ((mlen -= 2) >= 0) {
14433597Ssklower 			sum += ww(0); sum += sum;
14533597Ssklower 			sum2 += vv(0); sum2 += sum2;
14633597Ssklower 			w++;
14733597Ssklower 		}
14833597Ssklower 		sum += (sum2 << 8);
14933597Ssklower commoncase:
15033597Ssklower 		if (mlen == -1) {
15133597Ssklower 			sum += *(u_char *)w << 8; /* Big-Endian, else no << 8 */
15233597Ssklower 		}
15333597Ssklower 		FOLD(sum);
15425949Ssam 	}
15533597Ssklower 	if (mlen == -1) {
15633597Ssklower 		/* We had an odd number of bytes to sum; assume a garbage
15733597Ssklower 		   byte of zero and clean up */
15833597Ssklower 		sum += sum;
15933597Ssklower 		FOLD(sum);
16033597Ssklower 	}
16133597Ssklower 	/*
16233597Ssklower 	 * sum has already been kept to low sixteen bits.
16333597Ssklower 	 * just examine result and exit.
16433597Ssklower 	 */
16533597Ssklower 	if(sum==0xffff) sum = 0;
16633597Ssklower 	return (sum);
16725949Ssam }
168