Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 25 Aug 1999 17:20:00 +0700
From:      bp@butya.kz
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   kern/13374: PATCH: IPX checksuming code doesn't work at all
Message-ID:  <E11Ja9w-0007pf-00@lion.butya.kz>

next in thread | raw e-mail | index | archive | help

>Number:         13374
>Category:       kern
>Synopsis:       PATCH: IPX checksuming code doesn't work at all
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Aug 25 03:30:00 PDT 1999
>Closed-Date:
>Last-Modified:
>Originator:     Boris Popov
>Release:        FreeBSD 3.2-STABLE i386
>Organization:
none
>Environment:

	3.2-stable, 4.0-current with IPX support

>Description:

	IPX checksums aren't calculated correctly (they was copied from
XNS stack). This patch corrects this problem and enables using of chksums
on a per socket basis.

>How-To-Repeat:

	Try to use IPX checksums in NetWare environment.

>Fix:
	
diff -u org/ipx.h ./ipx.h
--- org/ipx.h	Mon Feb 16 00:00:00 1998
+++ ./ipx.h	Wed Aug 25 12:15:04 1999
@@ -95,6 +95,7 @@
 #define	SO_ALL_PACKETS		7
 #define SO_MTU			8
 #define SO_IPXTUN_ROUTE		9
+#define SO_IPX_CHECKSUM		10
 
 /*
  * IPX addressing
diff -u org/ipx_cksum.c ./ipx_cksum.c
--- org/ipx_cksum.c	Mon Feb 16 00:00:00 1998
+++ ./ipx_cksum.c	Wed Aug 25 16:58:24 1999
@@ -38,173 +38,76 @@
 
 #include <sys/param.h>
 #include <sys/mbuf.h>
+#include <sys/libkern.h>
 
+#include <netipx/ipx.h>
 #include <netipx/ipx_var.h>
 
-/*
- * Checksum routine for Network Systems Protocol Packets (Big-Endian).
- *
- * This routine is very heavily used in the network
- * code and should be modified for each CPU to be as fast as possible.
- */
 
-#define ADDCARRY(x)  { if ((x) > 65535) (x) -= 65535; }
-#define FOLD(x) {l_util.l = (x); (x) = l_util.s[0] + l_util.s[1]; ADDCARRY(x);}
+#define SUMADV	sum += *w++
 
 u_short
-ipx_cksum(m, len)
-	struct mbuf *m;
-	int len;
-{
-	register u_short *w;
-	register int sum = 0;
-	register int mlen = 0;
-	register int sum2;
-
+ipx_cksum(struct mbuf *m, int len) {
+	u_int32_t sum = 0;
+	u_short *w;
+	u_char oldtc;
+	int mlen, words;
+	struct ipx *ipx;
 	union {
-		u_short s[2];
-		long	l;
-	} l_util;
-
-	for (;m != NULL && len; m = m->m_next) {
-		if (m->m_len == 0)
-			continue;
-		/*
-		 * Each trip around loop adds in
-		 * word from one mbuf segment.
-		 */
-		w = mtod(m, u_short *);
-		if (mlen == -1) {
-			/*
-			 * There is a byte left from the last segment;
-			 * ones-complement add it into the checksum.
-			 */
-#if BYTE_ORDER == BIG_ENDIAN
-			sum  += *(u_char *)w;
-#else
-			sum  += *(u_char *)w << 8;
-#endif
-			sum += sum;
-			w = (u_short *)(1 + (char *)w);
-			mlen = m->m_len - 1;
-			len--;
-			FOLD(sum);
-		} else
-			mlen = m->m_len;
-		if (len < mlen)
-			mlen = len;
-		len -= mlen;
-		/*
-		 * We can do a 16 bit ones complement sum using
-		 * 32 bit arithmetic registers for adding,
-		 * with carries from the low added
-		 * into the high (by normal carry-chaining)
-		 * so long as we fold back before 16 carries have occured.
-		 */
-		if (1 & (int) w)
-			goto uuuuglyy;
-#ifndef TINY
-/* -DTINY reduces the size from 1250 to 550, but slows it down by 22% */
-		while ((mlen -= 32) >= 0) {
-			sum += w[0]; sum += sum; sum += w[1]; sum += sum;
-			sum += w[2]; sum += sum; sum += w[3]; sum += sum;
-			sum += w[4]; sum += sum; sum += w[5]; sum += sum;
-			sum += w[6]; sum += sum; sum += w[7]; sum += sum;
-			FOLD(sum);
-			sum += w[8]; sum += sum; sum += w[9]; sum += sum;
-			sum += w[10]; sum += sum; sum += w[11]; sum += sum;
-			sum += w[12]; sum += sum; sum += w[13]; sum += sum;
-			sum += w[14]; sum += sum; sum += w[15]; sum += sum;
-			FOLD(sum);
-			w += 16;
-		}
-		mlen += 32;
-#endif
-		while ((mlen -= 8) >= 0) {
-			sum += w[0]; sum += sum; sum += w[1]; sum += sum;
-			sum += w[2]; sum += sum; sum += w[3]; sum += sum;
-			FOLD(sum);
-			w += 4;
-		}
-		mlen += 8;
-		while ((mlen -= 2) >= 0) {
-			sum += *w++; sum += sum;
-		}
-		goto commoncase;
-uuuuglyy:
-#if BYTE_ORDER == BIG_ENDIAN
-#define ww(n) (((u_char *)w)[n + n + 1])
-#define vv(n) (((u_char *)w)[n + n])
-#else
-#if BYTE_ORDER == LITTLE_ENDIAN
-#define vv(n) (((u_char *)w)[n + n + 1])
-#define ww(n) (((u_char *)w)[n + n])
-#endif
-#endif
-		sum2 = 0;
-#ifndef TINY
-		while ((mlen -= 32) >= 0) {
-		    sum += ww(0); sum += sum; sum += ww(1); sum += sum;
-		    sum += ww(2); sum += sum; sum += ww(3); sum += sum;
-		    sum += ww(4); sum += sum; sum += ww(5); sum += sum;
-		    sum += ww(6); sum += sum; sum += ww(7); sum += sum;
-		    FOLD(sum);
-		    sum += ww(8); sum += sum; sum += ww(9); sum += sum;
-		    sum += ww(10); sum += sum; sum += ww(11); sum += sum;
-		    sum += ww(12); sum += sum; sum += ww(13); sum += sum;
-		    sum += ww(14); sum += sum; sum += ww(15); sum += sum;
-		    FOLD(sum);
-		    sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2;
-		    sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2;
-		    sum2 += vv(4); sum2 += sum2; sum2 += vv(5); sum2 += sum2;
-		    sum2 += vv(6); sum2 += sum2; sum2 += vv(7); sum2 += sum2;
-		    FOLD(sum2);
-		    sum2 += vv(8); sum2 += sum2; sum2 += vv(9); sum2 += sum2;
-		    sum2 += vv(10); sum2 += sum2; sum2 += vv(11); sum2 += sum2;
-		    sum2 += vv(12); sum2 += sum2; sum2 += vv(13); sum2 += sum2;
-		    sum2 += vv(14); sum2 += sum2; sum2 += vv(15); sum2 += sum2;
-		    FOLD(sum2);
-		    w += 16;
-		}
-		mlen += 32;
-#endif
-		while ((mlen -= 8) >= 0) {
-		    sum += ww(0); sum += sum; sum += ww(1); sum += sum;
-		    sum += ww(2); sum += sum; sum += ww(3); sum += sum;
-		    FOLD(sum);
-		    sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2;
-		    sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2;
-		    FOLD(sum2);
-		    w += 4;
-		}
-		mlen += 8;
-		while ((mlen -= 2) >= 0) {
-			sum += ww(0); sum += sum;
-			sum2 += vv(0); sum2 += sum2;
-			w++;
-		}
-		sum += (sum2 << 8);
-commoncase:
-		if (mlen == -1) {
-#if BYTE_ORDER == BIG_ENDIAN
-			sum += *(u_char *)w << 8;
-#else
-			sum += *(u_char *)w;
-#endif
-		}
-		FOLD(sum);
-	}
-	if (mlen == -1) {
-		/* We had an odd number of bytes to sum; assume a garbage
-		   byte of zero and clean up */
-		sum += sum;
-		FOLD(sum);
+		u_char	b[2];
+		u_short	w;
+	} buf;
+
+	ipx = mtod(m, struct ipx*);
+	oldtc = ipx->ipx_tc;
+	ipx->ipx_tc = 0;
+	w = &ipx->ipx_len;
+	len -= 2;
+	mlen = 2;
+
+	for(;;) {
+		mlen = imin(m->m_len - mlen, len);
+		words = mlen / 2;
+		len -= mlen & ~1;
+		while (words >= 16) {
+			SUMADV;	SUMADV;	SUMADV;	SUMADV;
+			SUMADV;	SUMADV;	SUMADV;	SUMADV;
+			SUMADV;	SUMADV;	SUMADV;	SUMADV;
+			SUMADV;	SUMADV;	SUMADV;	SUMADV;
+			words -= 16;
+		}
+		while (words--)
+			SUMADV;
+		if (len == 0)
+			break;
+		mlen &= 1;
+		if (mlen) {
+			buf.b[0] = *(u_char*)w;
+			if (--len == 0) {
+				buf.b[1] = 0;
+				sum += buf.w;
+				break;
+			}
+		}
+		m = m->m_next;
+		if (m == NULL)
+			break;
+		w = mtod(m, u_short*);
+		if (mlen) {
+			buf.b[1] = *(u_char*)w;
+			sum += buf.w;
+			((u_char*)w)++;
+			if (--len == 0)
+				break;
+		} 
 	}
-	/*
-	 * sum has already been kept to low sixteen bits.
-	 * just examine result and exit.
-	 */
-	if(sum == 0xffff)
-		sum = 0;
+
+	ipx->ipx_tc = oldtc;
+
+	sum = (sum & 0xffff) + (sum >> 16);
+	if (sum >= 0x10000)
+		sum++;
+	if (sum)
+		sum = ~sum;
 	return (sum);
 }
diff -u org/ipx_input.c ./ipx_input.c
--- org/ipx_input.c	Mon Jun 22 01:00:00 1998
+++ ./ipx_input.c	Wed Aug 25 16:57:43 1999
@@ -130,9 +130,7 @@
 	register struct mbuf *m;
 	register struct ipxpcb *ipxp;
 	struct ipx_ifaddr *ia;
-	register int i;
 	int len, s;
-	char oddshortpacket = 0;
 
 next:
 	/*
@@ -171,15 +169,6 @@
 
 	ipx = mtod(m, struct ipx *);
 	len = ntohs(ipx->ipx_len);
-	if ((len < m->m_pkthdr.len) && (oddshortpacket = len & 1)) {
-		/*
-		 * If this packet is of odd length, and the length
-		 * inside the header is less than the received packet
-		 * length, preserve garbage byte for possible checksum.
-		 */
-		len++;
-	}
-
 	/*
 	 * Check that the amount of data in the buffers
 	 * is as at least much as the IPX header would have us expect.
@@ -197,9 +186,8 @@
 		} else
 			m_adj(m, len - m->m_pkthdr.len);
 	}
-	if (ipxcksum && ((i = ipx->ipx_sum) != 0xffff)) {
-		ipx->ipx_sum = 0;
-		if (i != (ipx->ipx_sum = ipx_cksum(m, len))) {
+	if (ipxcksum && ipx->ipx_sum != 0xffff) {
+		if (ipx->ipx_sum != ipx_cksum(m, len)) {
 			ipxstat.ipxs_badsum++;
 			goto bad;
 		}
@@ -274,9 +262,6 @@
 	 * Switch out to protocol's input routine.
 	 */
 	if (ipxp != NULL) {
-		if (oddshortpacket) {
-			m_adj(m, -1);
-		}
 		ipxstat.ipxs_delivered++;
 		if ((ipxp->ipxp_flags & IPXP_ALL_PACKETS) == 0)
 			switch (ipx->ipx_pt) {
@@ -396,32 +381,15 @@
 			goto cleanup;
 		}
 	}
-	/* XXX
-	 * I think the checksum generation is bogus. According to the NLSP
-	 * spec the ipx_tc (hop count) field and the ipx_sum should be
-	 * zero'ed before generating the checksum, ie. it should not be
-	 * necesary to recompute it in the forwarding function.
+	/*
+	 * We don't need to recompute checksum because ipx_tc field
+	 * is ignored by checksum calculation routine, however
+	 * it may be desirable to reset checksum if ipxcksum == 0
 	 */
-	/* need to adjust checksum */
-	if (ipxcksum && ipx->ipx_sum != 0xffff) {
-		union bytes {
-			u_char c[4];
-			u_short s[2];
-			long l;
-		} x;
-		register int shift;
-		x.l = 0;
-		x.c[0] = agedelta;
-		shift = (((((int)ntohs(ipx->ipx_len)) + 1) >> 1) - 2) & 0xf;
-		x.l = ipx->ipx_sum + (x.s[0] << shift);
-		x.l = x.s[0] + x.s[1];
-		x.l = x.s[0] + x.s[1];
-		if (x.l == 0xffff)
-			ipx->ipx_sum = 0;
-		else
-			ipx->ipx_sum = x.l;
-	} else 
+#if 0
+	if (!ipxcksum)
 		ipx->ipx_sum = 0xffff;
+#endif
 
 	error = ipx_outputfl(m, &ipx_droute, flags);
 	if (error == 0) {
diff -u org/ipx_outputfl.c ./ipx_outputfl.c
--- org/ipx_outputfl.c	Sun Apr 11 14:46:00 1999
+++ ./ipx_outputfl.c	Wed Aug 25 12:17:07 1999
@@ -247,12 +247,8 @@
 			ipx->ipx_dna.x_net = dst.sipx_addr.x_net;
 			m1 = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
 
-			if(ipx->ipx_sum != 0xffff) {
-				int len = ntohs(ipx->ipx_len);
-				ipx->ipx_sum = 0;
-				len = ((len - 1) | 1) + 1;
-				ipx->ipx_sum = ipx_cksum(m, len);
-			}
+			if(ipx->ipx_sum != 0xffff)
+				ipx->ipx_sum = ipx_cksum(m, ntohs(ipx->ipx_len));
 			if(m1) {
 				error = (*ifp->if_output)(ifp, m1,
 					(struct sockaddr *)&dst, NULL);
diff -u org/ipx_pcb.c ./ipx_pcb.c
--- org/ipx_pcb.c	Sat May 22 00:22:00 1999
+++ ./ipx_pcb.c	Wed Aug 25 12:15:04 1999
@@ -66,6 +66,8 @@
 		return (ENOBUFS);
 	bzero(ipxp, sizeof *ipxp);
 	ipxp->ipxp_socket = so;
+	if (ipxcksum)
+		ipxp->ipxp_flags |= IPXP_CHECKSUM;
 	insque(ipxp, head);
 	so->so_pcb = (caddr_t)ipxp;
 	return (0);
diff -u org/ipx_pcb.h ./ipx_pcb.h
--- org/ipx_pcb.h	Mon Feb 16 00:00:00 1998
+++ ./ipx_pcb.h	Wed Aug 25 12:15:05 1999
@@ -64,6 +64,7 @@
 #define IPXP_RAWIN		0x2	/* show headers on input */
 #define IPXP_RAWOUT		0x4	/* show header on output */
 #define IPXP_ALL_PACKETS	0x8	/* Turn off higher proto processing */
+#define	IPXP_CHECKSUM		0x10	/* use checksum on this socket */
 
 #define	IPX_WILDCARD		1
 
diff -u org/ipx_usrreq.c ./ipx_usrreq.c
--- org/ipx_usrreq.c	Sat May 22 00:22:00 1999
+++ ./ipx_usrreq.c	Wed Aug 25 12:17:27 1999
@@ -214,7 +214,7 @@
 		m = mprev;
 		if ((m->m_flags & M_EXT) == 0 &&
 			(m->m_len + m->m_data < &m->m_dat[MLEN])) {
-			m->m_len++;
+			mtod(m, char*)[m->m_len++] = 0;
 		} else {
 			struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
 
@@ -250,9 +250,7 @@
 
 	ipx->ipx_len = htons((u_short)len);
 
-	if (ipxcksum) {
-		ipx->ipx_sum = 0;
-		len = ((len - 1) | 1) + 1;
+	if (ipxp->ipxp_flags & IPXP_CHECKSUM) {
 		ipx->ipx_sum = ipx_cksum(m, len);
 	} else
 		ipx->ipx_sum = 0xffff;
@@ -333,6 +331,10 @@
 		case SO_HEADERS_ON_INPUT:
 			mask = IPXP_RAWIN;
 			goto get_flags;
+
+		case SO_IPX_CHECKSUM:
+			mask = IPXP_CHECKSUM;
+			goto get_flags;
 			
 		case SO_HEADERS_ON_OUTPUT:
 			mask = IPXP_RAWOUT;
@@ -371,6 +373,9 @@
 		case SO_HEADERS_ON_INPUT:
 			mask = IPXP_RAWIN;
 			goto set_head;
+
+		case SO_IPX_CHECKSUM:
+			mask = IPXP_CHECKSUM;
 
 		case SO_HEADERS_ON_OUTPUT:
 			mask = IPXP_RAWOUT;
Only in .: org
diff -u org/spx_usrreq.c ./spx_usrreq.c
--- org/spx_usrreq.c	Sun Feb  7 11:15:00 1999
+++ ./spx_usrreq.c	Wed Aug 25 12:17:54 1999
@@ -1081,11 +1081,7 @@
 		si->si_ack = htons(cb->s_ack);
 
 		if (ipxcksum) {
-			si->si_sum = 0;
-			len = ntohs(si->si_len);
-			if (len & 1)
-				len++;
-			si->si_sum = ipx_cksum(m, len);
+			si->si_sum = ipx_cksum(m, ntohs(si->si_len));
 		} else
 			si->si_sum = 0xffff;
 

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E11Ja9w-0007pf-00>