Coverage Report

Created: 2025-08-26 06:20

/src/frr/lib/checksum.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Checksum routine for Internet Protocol family headers (C Version).
3
 *
4
 * Refer to "Computing the Internet Checksum" by R. Braden, D. Borman and
5
 * C. Partridge, Computer Communication Review, Vol. 19, No. 2, April 1989,
6
 * pp. 86-101, for additional details on computing this checksum.
7
 */
8
9
#include <zebra.h>
10
#include "checksum.h"
11
12
#define add_carry(dst, add)                                                    \
13
7.25M
  do {                                                                   \
14
7.25M
    typeof(dst) _add = (add);                                      \
15
7.25M
    dst += _add;                                                   \
16
7.25M
    if (dst < _add)                                                \
17
7.25M
      dst++;                                                 \
18
7.25M
  } while (0)
19
20
uint16_t in_cksumv(const struct iovec *iov, size_t iov_len)
21
7.44k
{
22
7.44k
  const struct iovec *iov_end;
23
7.44k
  uint32_t sum = 0;
24
7.44k
  register unsigned short answer; /* assumes unsigned short == 16 bits */
25
26
7.44k
  union {
27
7.44k
    uint8_t bytes[2];
28
7.44k
    uint16_t word;
29
7.44k
  } wordbuf;
30
7.44k
  bool have_oddbyte = false;
31
32
  /*
33
   * Our algorithm is simple, using a 32-bit accumulator (sum),
34
   * we add sequential 16-bit words to it, and at the end, fold back
35
   * all the carry bits from the top 16 bits into the lower 16 bits.
36
   */
37
38
14.8k
  for (iov_end = iov + iov_len; iov < iov_end; iov++) {
39
7.44k
    const uint8_t *ptr, *end;
40
41
7.44k
    ptr = (const uint8_t *)iov->iov_base;
42
7.44k
    end = ptr + iov->iov_len;
43
7.44k
    if (ptr == end)
44
0
      continue;
45
46
7.44k
    if (have_oddbyte) {
47
0
      have_oddbyte = false;
48
0
      wordbuf.bytes[1] = *ptr++;
49
50
0
      add_carry(sum, wordbuf.word);
51
0
    }
52
53
3.63M
    while (ptr + 8 <= end) {
54
3.62M
      add_carry(sum, *(const uint32_t *)(ptr + 0));
55
3.62M
      add_carry(sum, *(const uint32_t *)(ptr + 4));
56
3.62M
      ptr += 8;
57
3.62M
    }
58
59
16.2k
    while (ptr + 2 <= end) {
60
8.84k
      add_carry(sum, *(const uint16_t *)ptr);
61
8.84k
      ptr += 2;
62
8.84k
    }
63
64
7.44k
    if (ptr + 1 <= end) {
65
859
      wordbuf.bytes[0] = *ptr++;
66
859
      have_oddbyte = true;
67
859
    }
68
7.44k
  }
69
70
  /* mop up an odd byte, if necessary */
71
7.44k
  if (have_oddbyte) {
72
859
    wordbuf.bytes[1] = 0;
73
859
    add_carry(sum, wordbuf.word);
74
859
  }
75
76
  /*
77
   * Add back carry outs from top 16 bits to low 16 bits.
78
   */
79
80
7.44k
  sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
81
7.44k
  sum += (sum >> 16);       /* add carry */
82
  /* ones-complement, then truncate to 16 bits */
83
7.44k
  answer = (unsigned short)~sum;
84
7.44k
  return (answer);
85
7.44k
}
86
87
/* Fletcher Checksum -- Refer to RFC1008. */
88
#define MODX                 4102U   /* 5802 should be fine */
89
90
/* To be consistent, offset is 0-based index, rather than the 1-based
91
   index required in the specification ISO 8473, Annex C.1 */
92
/* calling with offset == FLETCHER_CHECKSUM_VALIDATE will validate the checksum
93
   without modifying the buffer; a valid checksum returns 0 */
94
uint16_t fletcher_checksum(uint8_t *buffer, const size_t len,
95
         const uint16_t offset)
96
  __attribute__((no_sanitize("unsigned-integer-overflow")))
97
47.4k
{
98
47.4k
  uint8_t *p;
99
47.4k
  int x, y, c0, c1;
100
47.4k
  uint16_t checksum = 0;
101
47.4k
  uint16_t *csum;
102
47.4k
  size_t partial_len, i, left = len;
103
104
47.4k
  if (offset != FLETCHER_CHECKSUM_VALIDATE)
105
  /* Zero the csum in the packet. */
106
367
  {
107
367
    assert(offset
108
367
           < (len - 1)); /* account for two bytes of checksum */
109
367
    csum = (uint16_t *)(buffer + offset);
110
367
    *(csum) = 0;
111
367
  }
112
113
47.4k
  p = buffer;
114
47.4k
  c0 = 0;
115
47.4k
  c1 = 0;
116
117
94.8k
  while (left != 0) {
118
47.4k
    partial_len = MIN(left, MODX);
119
120
1.71M
    for (i = 0; i < partial_len; i++) {
121
1.66M
      c0 = c0 + *(p++);
122
1.66M
      c1 += c0;
123
1.66M
    }
124
125
47.4k
    c0 = c0 % 255;
126
47.4k
    c1 = c1 % 255;
127
128
47.4k
    left -= partial_len;
129
47.4k
  }
130
131
  /* The cast is important, to ensure the mod is taken as a signed value.
132
   */
133
47.4k
  x = (int)((len - offset - 1) * c0 - c1) % 255;
134
135
47.4k
  if (x <= 0)
136
47.2k
    x += 255;
137
47.4k
  y = 510 - c0 - x;
138
47.4k
  if (y > 255)
139
14.7k
    y -= 255;
140
141
47.4k
  if (offset == FLETCHER_CHECKSUM_VALIDATE) {
142
47.0k
    checksum = (c1 << 8) + c0;
143
47.0k
  } else {
144
    /*
145
     * Now we write this to the packet.
146
     * We could skip this step too, since the checksum returned
147
     * would
148
     * be stored into the checksum field by the caller.
149
     */
150
367
    buffer[offset] = x;
151
367
    buffer[offset + 1] = y;
152
153
    /* Take care of the endian issue */
154
367
    checksum = htons((x << 8) | (y & 0xFF));
155
367
  }
156
157
47.4k
  return checksum;
158
47.4k
}