checksum.c 2.41 KB
Newer Older
1 2 3
/*
 *	BIRD Library -- IP One-Complement Checksum
 *
Martin Mareš's avatar
Martin Mareš committed
4
 *	(c) 1999--2000 Martin Mares <mj@ucw.cz>
5 6 7 8
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

Martin Mareš's avatar
Martin Mareš committed
9 10 11 12
/**
 * DOC: Miscellaneous functions.
 */

13 14 15 16 17
#include <stdarg.h>

#include "nest/bird.h"
#include "checksum.h"

18
static inline u32
19 20 21
add32(u32 sum, u32 x)
{
  u32 z = sum + x;
22 23 24 25 26 27
//  return z + (z < sum);

 /* add carry */
  if (z < x)
    z++;
  return z;
28 29 30
}

static u16
31
ipsum_calc_block(u32 *buf, unsigned len, u16 isum)
32 33 34 35 36 37 38 39 40 41 42 43 44
{
  /*
   *  A few simple facts about the IP checksum (see RFC 1071 for detailed
   *  discussion):
   *
   *	o  It's associative and commutative.
   *	o  It's byte order independent.
   *	o  It's word size independent.
   *
   *  This gives us a neat 32-bits-at-a-time algorithm which respects
   *  usual alignment requirements and is reasonably fast.
   */

45
  ASSERT(!(len % 4));
46
  if (!len)
47 48 49 50 51 52 53 54 55
    return isum;

  u32 *end = buf + (len >> 2);
  u32 sum = isum;
  while (buf < end)
    sum = add32(sum, *buf++);

  sum = (sum >> 16) + (sum & 0xffff);    /* add high-16 to low-16 */
  sum += (sum >> 16); /* add carry */
56 57 58
  return sum;
}

59 60 61 62 63 64 65
static u16
ipsum_calc(void *frag, unsigned len, va_list args)
{
  u16 sum = 0;

  for(;;)
    {
66
      sum = ipsum_calc_block(frag, len, sum);
67 68 69 70 71 72 73 74
      frag = va_arg(args, void *);
      if (!frag)
	break;
      len = va_arg(args, unsigned);
    }
  return sum;
}

Martin Mareš's avatar
Martin Mareš committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88
/**
 * ipsum_verify - verify an IP checksum
 * @frag: first packet fragment
 * @len: length in bytes
 *
 * This function verifies whether a given fragmented packet
 * has correct one's complement checksum as used by the IP
 * protocol.
 *
 * It uses all the clever tricks described in RFC 1071 to speed
 * up checksum calculation as much as possible.
 *
 * Result: 1 if the checksum is correct, 0 else.
 */
89 90 91 92 93 94 95 96 97 98 99 100
int
ipsum_verify(void *frag, unsigned len, ...)
{
  va_list args;
  u16 sum;

  va_start(args, len);
  sum = ipsum_calc(frag, len, args);
  va_end(args);
  return sum == 0xffff;
}

Martin Mareš's avatar
Martin Mareš committed
101 102 103 104 105
/**
 * ipsum_calculate - compute an IP checksum
 * @frag: first packet fragment
 * @len: length in bytes
 *
106
 * This function calculates a one's complement checksum of a given fragmented
Martin Mareš's avatar
Martin Mareš committed
107 108 109 110 111
 * packet.
 *
 * It uses all the clever tricks described in RFC 1071 to speed
 * up checksum calculation as much as possible.
 */
112 113 114 115 116 117 118 119 120 121 122
u16
ipsum_calculate(void *frag, unsigned len, ...)
{
  va_list args;
  u16 sum;

  va_start(args, len);
  sum = ipsum_calc(frag, len, args);
  va_end(args);
  return 0xffff - sum;
}