Coverage Report

Created: 2026-07-16 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/checksum.c
Line
Count
Source
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.76M
  do {                                                                   \
14
7.76M
    typeof(dst) _add = (add);                                      \
15
7.76M
    dst += _add;                                                   \
16
7.76M
    if (dst < _add)                                                \
17
7.76M
      dst++;                                                 \
18
7.76M
  } while (0)
19
20
uint16_t in_cksumv(const struct iovec *iov, size_t iov_len)
21
8.37k
{
22
8.37k
  const struct iovec *iov_end;
23
8.37k
  uint32_t sum = 0;
24
8.37k
  register unsigned short answer; /* assumes unsigned short == 16 bits */
25
26
8.37k
  union {
27
8.37k
    uint8_t bytes[2];
28
8.37k
    uint16_t word;
29
8.37k
  } wordbuf;
30
8.37k
  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
16.7k
  for (iov_end = iov + iov_len; iov < iov_end; iov++) {
39
8.37k
    const uint8_t *ptr, *end;
40
41
8.37k
    ptr = (const uint8_t *)iov->iov_base;
42
8.37k
    end = ptr + iov->iov_len;
43
8.37k
    if (ptr == end)
44
0
      continue;
45
46
8.37k
    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.88M
    while (ptr + 8 <= end) {
54
3.87M
      add_carry(sum, *(const uint32_t *)(ptr + 0));
55
3.87M
      add_carry(sum, *(const uint32_t *)(ptr + 4));
56
3.87M
      ptr += 8;
57
3.87M
    }
58
59
19.1k
    while (ptr + 2 <= end) {
60
10.8k
      add_carry(sum, *(const uint16_t *)ptr);
61
10.8k
      ptr += 2;
62
10.8k
    }
63
64
8.37k
    if (ptr + 1 <= end) {
65
859
      wordbuf.bytes[0] = *ptr++;
66
859
      have_oddbyte = true;
67
859
    }
68
8.37k
  }
69
70
  /* mop up an odd byte, if necessary */
71
8.37k
  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
8.37k
  sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
81
8.37k
  sum += (sum >> 16);       /* add carry */
82
  /* ones-complement, then truncate to 16 bits */
83
8.37k
  answer = (unsigned short)~sum;
84
8.37k
  return (answer);
85
8.37k
}
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
45.0k
{
98
45.0k
  uint8_t *p;
99
45.0k
  int x, y, c0, c1;
100
45.0k
  uint16_t checksum = 0;
101
45.0k
  uint16_t *csum;
102
45.0k
  size_t partial_len, i, left = len;
103
104
45.0k
  if (offset != FLETCHER_CHECKSUM_VALIDATE)
105
  /* Zero the csum in the packet. */
106
305
  {
107
305
    assert(offset
108
305
           < (len - 1)); /* account for two bytes of checksum */
109
305
    csum = (uint16_t *)(buffer + offset);
110
305
    *(csum) = 0;
111
305
  }
112
113
45.0k
  p = buffer;
114
45.0k
  c0 = 0;
115
45.0k
  c1 = 0;
116
117
90.2k
  while (left != 0) {
118
45.1k
    partial_len = MIN(left, MODX);
119
120
1.67M
    for (i = 0; i < partial_len; i++) {
121
1.62M
      c0 = c0 + *(p++);
122
1.62M
      c1 += c0;
123
1.62M
    }
124
125
45.1k
    c0 = c0 % 255;
126
45.1k
    c1 = c1 % 255;
127
128
45.1k
    left -= partial_len;
129
45.1k
  }
130
131
  /* The cast is important, to ensure the mod is taken as a signed value.
132
   */
133
45.0k
  x = (int)((len - offset - 1) * c0 - c1) % 255;
134
135
45.0k
  if (x <= 0)
136
44.9k
    x += 255;
137
45.0k
  y = 510 - c0 - x;
138
45.0k
  if (y > 255)
139
14.2k
    y -= 255;
140
141
45.0k
  if (offset == FLETCHER_CHECKSUM_VALIDATE) {
142
44.7k
    checksum = (c1 << 8) + c0;
143
44.7k
  } 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
305
    buffer[offset] = x;
151
305
    buffer[offset + 1] = y;
152
153
    /* Take care of the endian issue */
154
305
    checksum = htons((x << 8) | (y & 0xFF));
155
305
  }
156
157
45.0k
  return checksum;
158
45.0k
}