summaryrefslogtreecommitdiff
path: root/lib/checksum.c
blob: 94cf71e8b4749c41ae940b46ad5e7ca88c9b3c43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/*
 *	BIRD Library -- IP One-Complement Checksum
 *
 *	(c) 1999 Martin Mares <mj@ucw.cz>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#include <stdarg.h>

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

static u16				/* One-complement addition */
add16(u16 sum, u16 x)
{
  u16 z = sum + x;
  return z + (z < sum);
}

static u32
add32(u32 sum, u32 x)
{
  u32 z = sum + x;
  return z + (z < sum);
}

static u16
ipsum_calc_block(u16 *x, unsigned len, u16 sum)
{
  int rest;
  u32 tmp, *xx;

  /*
   *  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.
   */

  ASSERT(!(len % 2));
  if (!len)
    return sum;
  len >>= 1;
  if ((unsigned long) x & 2)		/* Align to 32-bit boundary */
    {
      sum = add16(sum, *x++);
      len--;
    }
  rest = len & 1;
  len >>= 1;
  tmp = 0;
  xx = (u32 *) x;
  while (len)
    {
      tmp = add32(tmp, *xx++);
      len--;
    }
  sum = add16(sum, add16(tmp & 0xffff, tmp >> 16U));
  if (rest)
    sum = add16(sum, *(u16 *) xx);
  return sum;
}

static u16
ipsum_calc(void *frag, unsigned len, va_list args)
{
  u16 sum = 0;

  for(;;)
    {
      sum = ipsum_calc_block(frag, len, sum);
      frag = va_arg(args, void *);
      if (!frag)
	break;
      len = va_arg(args, unsigned);
    }
  return sum;
}

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;
}

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;
}