Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 30 May 2004 18:42:11 +0000 (UTC)
From:      naddy@mips.inka.de (Christian Weisgerber)
To:        freebsd-arch@freebsd.org
Subject:   ether_crc32_[bl]e()
Message-ID:  <c9d9u3$o6k$1@kemoauc.mips.inka.de>

next in thread | raw e-mail | index | archive | help
NetBSD and OpenBSD have two helper functions in if_ethersubr.c

ether_crc32_le(const u_int8_t *buf, size_t len)
ether_crc32_be(const u_int8_t *buf, size_t len)

that will calculate an ethernet CRC-32 in little endian/big endian
fashion.

These CRCs are used all over our network drivers, e.g. for setting
up multicast hash filters.  Functions to calculate them are duplicated
all over.  These could be factored out of some thirty drivers.
NetBSD has done so, btw.

The patch below adds the functions to if_ethersubr.c and, as an
example, switches re(4) to make use of this.

1. Do we want this?
2. If yes, there are probably stylistic issues I could use some
   help with.


Index: net/ethernet.h
===================================================================
RCS file: /home/ncvs/src/sys/net/ethernet.h,v
retrieving revision 1.22
diff -u -r1.22 ethernet.h
--- net/ethernet.h	14 Nov 2002 23:28:47 -0000	1.22
+++ net/ethernet.h	30 May 2004 16:06:22 -0000
@@ -358,6 +358,8 @@
 		   struct mbuf *, struct sockaddr *, struct rtentry *);
 extern	int  ether_output_frame(struct ifnet *, struct mbuf *);
 extern	char *ether_sprintf(const u_int8_t *);
+uint32_t ether_crc32_le(const uint8_t *, size_t);
+uint32_t ether_crc32_be(const uint8_t *, size_t);
 
 #else /* _KERNEL */
 
Index: net/if_ethersubr.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_ethersubr.c,v
retrieving revision 1.169
diff -u -r1.169 if_ethersubr.c
--- net/if_ethersubr.c	25 Apr 2004 09:24:51 -0000	1.169
+++ net/if_ethersubr.c	30 May 2004 16:05:49 -0000
@@ -878,6 +878,77 @@
 SYSCTL_INT(_net_link_ether, OID_AUTO, ipfw, CTLFLAG_RW,
 	    &ether_ipfw,0,"Pass ether pkts through firewall");
 
+#if 0
+/*
+ * This is for reference.  We have a table-driven version
+ * of the little-endian crc32 generator, which is faster
+ * than the double-loop.
+ */
+uint32_t
+ether_crc32_le(const uint8_t *buf, size_t len)
+{
+	uint32_t crc;
+	size_t i, bit;
+	uint8_t data;
+
+	crc = 0xffffffff;	/* initial value */
+
+	for (i = 0; i < len; i++) {
+		for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1)
+			carry = (crc ^ data) & 1;
+			crc >>= 1;
+			if (carry)
+				crc = (crc ^ ETHER_CRC_POLY_LE);
+	}
+
+	return (crc);
+}
+#else
+uint32_t
+ether_crc32_le(const uint8_t *buf, size_t len)
+{
+	static const uint32_t crctab[] = {
+		0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+		0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+		0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+		0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+	};
+	uint32_t crc;
+	size_t i;
+
+	crc = 0xffffffff;	/* initial value */
+
+	for (i = 0; i < len; i++) {
+		crc ^= buf[i];
+		crc = (crc >> 4) ^ crctab[crc & 0xf];
+		crc = (crc >> 4) ^ crctab[crc & 0xf];
+	}
+
+	return (crc);
+}
+#endif
+
+uint32_t
+ether_crc32_be(const u_int8_t *buf, size_t len)
+{
+	uint32_t crc, carry;
+	size_t i, bit;
+	uint8_t data;
+
+	crc = 0xffffffff;	/* initial value */
+
+	for (i = 0; i < len; i++) {
+		for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) {
+			carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01);
+			crc <<= 1;
+			if (carry)
+				crc = (crc ^ ETHER_CRC_POLY_BE) | carry;
+		}
+	}
+
+	return (crc);
+}
+
 int
 ether_ioctl(struct ifnet *ifp, int command, caddr_t data)
 {
Index: dev/re/if_re.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/re/if_re.c,v
retrieving revision 1.23
diff -u -r1.23 if_re.c
--- dev/re/if_re.c	24 May 2004 19:39:23 -0000	1.23
+++ dev/re/if_re.c	30 May 2004 16:42:17 -0000
@@ -225,7 +225,6 @@
 static int re_miibus_writereg	(device_t, int, int, int);
 static void re_miibus_statchg	(device_t);
 
-static uint32_t re_mchash	(const uint8_t *);
 static void re_setmulti		(struct rl_softc *);
 static void re_reset		(struct rl_softc *);
 
@@ -573,33 +572,6 @@
 }
 
 /*
- * Calculate CRC of a multicast group address, return the upper 6 bits.
- */
-static uint32_t
-re_mchash(addr)
-	const uint8_t *addr;
-{
-	uint32_t crc, carry;
-	int idx, bit;
-	uint8_t data;
-
-	/* Compute CRC for the address value. */
-	crc = 0xFFFFFFFF; /* initial value */
-
-	for (idx = 0; idx < 6; idx++) {
-		for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) {
-			carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01);
-			crc <<= 1;
-			if (carry)
-				crc = (crc ^ 0x04c11db6) | carry;
-		}
-	}
-
-	/* return the filter bit position */
-	return(crc >> 26);
-}
-
-/*
  * Program the 64-bit multicast hash filter.
  */
 static void
@@ -633,7 +605,8 @@
 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
 		if (ifma->ifma_addr->sa_family != AF_LINK)
 			continue;
-		h = re_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+		h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+		    ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
 		if (h < 32)
 			hashes[0] |= (1 << h);
 		else
-- 
Christian "naddy" Weisgerber                          naddy@mips.inka.de



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?c9d9u3$o6k$1>