[lib] Add fletcher checksum implementation

2008-08-13 Jingjing Duan <Jingjing.Duan@sun.com>

	* lib/checksum.?: (fletcher_checksum) implementation of
	  Fletcher checksum, as per RFC1008.

Signed-off-by: Paul Jakma <paul@quagga.net>
diff --git a/lib/checksum.c b/lib/checksum.c
index 201da59..88ec72a 100644
--- a/lib/checksum.c
+++ b/lib/checksum.c
@@ -45,3 +45,82 @@
 	answer = ~sum;		/* ones-complement, then truncate to 16 bits */
 	return(answer);
 }
+
+/* Fletcher Checksum -- Refer to RFC1008. */
+#define MODX                 4102   /* 5802 should be fine */
+
+/* To be consistent, offset is 0-based index, rather than the 1-based 
+   index required in the specification ISO 8473, Annex C.1 */
+u_int16_t
+fletcher_checksum(u_char * buffer, int len, u_int16_t offset)
+{
+  u_int8_t *p;
+  int x;
+  int y;
+  u_int32_t mul;
+  u_int32_t c0;
+  u_int32_t c1;
+  u_int16_t checksum;
+  u_int16_t *csum;
+  int i, init_len, partial_len;
+
+  checksum = 0;
+
+  /*
+   * Zero the csum in the packet.
+   */
+  csum = (u_int16_t *) (buffer + offset);
+  *(csum) = checksum;
+
+  p = buffer;
+  c0 = 0;
+  c1 = 0;
+  init_len = len;
+
+  while (len != 0)
+    {
+      partial_len = MIN(len, MODX);
+
+      for (i = 0; i < partial_len; i++)
+	{
+	  c0 = c0 + *(p++);
+	  c1 += c0;
+	}
+
+      c0 = c0 % 255;
+      c1 = c1 % 255;
+
+      len -= partial_len;
+    }
+
+  mul = (init_len - offset)*(c0);
+
+  x = mul - c0 - c1;
+  y = c1 - mul - 1;
+
+  if (y > 0)
+    y++;
+  if (x < 0)
+    x--;
+
+  x %= 255;
+  y %= 255;
+
+  if (x == 0)
+    x = 255;
+  if (y == 0)
+    y = 1;
+
+  /*
+   * Now we write this to the packet.
+   * We could skip this step too, since the checksum returned would
+   * be stored into the checksum field by the caller.
+   */
+  buffer[offset] = x;
+  buffer[offset + 1] = y;
+
+  /* Take care of the endian issue */
+  checksum = htons((x << 8) | (y & 0xFF));
+
+  return checksum;
+}
diff --git a/lib/checksum.h b/lib/checksum.h
index ccc4da0..d3ce930 100644
--- a/lib/checksum.h
+++ b/lib/checksum.h
@@ -1 +1,2 @@
 extern int in_cksum(void *, int);
+extern u_int16_t fletcher_checksum(u_char * buffer, int len, u_int16_t offset);