Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/in_cksum.c
Line
Count
Source
1
/* in_cksum.c
2
 * 4.4-Lite-2 Internet checksum routine, modified to take a vector of
3
 * pointers/lengths giving the pieces to be checksummed.  Also using
4
 * Tahoe/CGI version of ADDCARRY(x) macro instead of from portable version.
5
 *
6
 * Copyright (c) 1988, 1992, 1993
7
 *  The Regents of the University of California.  All rights reserved.
8
 *
9
 * SPDX-License-Identifier: BSD-3-Clause
10
 *
11
 *  @(#)in_cksum.c  8.1 (Berkeley) 6/10/93
12
 */
13
14
#include "config.h"
15
16
#include <glib.h>
17
18
#include <epan/tvbuff.h>
19
#include <epan/in_cksum.h>
20
21
/*
22
 * Checksum routine for Internet Protocol family headers (Portable Version).
23
 *
24
 * This routine is very heavily used in the network
25
 * code and should be modified for each CPU to be as fast as possible.
26
 */
27
28
56.3k
#define ADDCARRY(x)  {if ((x) > 65535) (x) -= 65535;}
29
56.3k
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
30
31
/*
32
 * Linux and Windows, at least, when performing Local Checksum Offload
33
 * store the one's complement sum (not inverted to its bitwise complement)
34
 * of the pseudo header in the checksum field (instead of initializing
35
 * to zero), allowing the device driver to calculate the real checksum
36
 * later without needing knowledge of the pseudoheader itself.
37
 * (This is presumably why GSO requires equal length buffers - so that the
38
 * pseudo header contribution to the checksum, which includes the payload
39
 * length, is the same.)
40
 *
41
 * We can output this partial checksum as an intermediate result,
42
 * assuming that the pseudo header is all but the last chunk in the vector.
43
 * Note that unlike the final output it is not inverted, and that it
44
 * (like the final computed checksum) is is network byte order.
45
 */
46
int
47
in_cksum_ret_partial(const vec_t *vec, int veclen, uint16_t *partial)
48
21.8k
{
49
21.8k
  register const uint16_t *w;
50
21.8k
  register int sum = 0;
51
21.8k
  register int mlen = 0;
52
21.8k
  int byte_swapped = 0;
53
54
21.8k
  union {
55
21.8k
    uint8_t c[2];
56
21.8k
    uint16_t  s;
57
21.8k
  } s_util;
58
21.8k
  union {
59
21.8k
    uint16_t s[2];
60
21.8k
    uint32_t  l;
61
21.8k
  } l_util;
62
63
71.3k
  for (; veclen != 0; vec++, veclen--) {
64
49.5k
    if (veclen == 1 && partial) {
65
0
      REDUCE;
66
0
      *partial = sum;
67
0
    }
68
49.5k
    if (vec->len == 0)
69
3.15k
      continue;
70
46.3k
    w = (const uint16_t *)(const void *)vec->ptr;
71
46.3k
    if (mlen == -1) {
72
      /*
73
       * The first byte of this chunk is the continuation
74
       * of a word spanning between this chunk and the
75
       * last chunk.
76
       *
77
       * s_util.c[0] is already saved when scanning previous
78
       * chunk.
79
       */
80
0
      s_util.c[1] = *(const uint8_t *)w;
81
0
      sum += s_util.s;
82
0
      w = (const uint16_t *)(const void *)((const uint8_t *)w + 1);
83
0
      mlen = vec->len - 1;
84
0
    } else
85
46.3k
      mlen = vec->len;
86
    /*
87
     * Force to even boundary.
88
     */
89
46.3k
    if ((1 & (intptr_t)w) && (mlen > 0)) {
90
2.11k
      REDUCE;
91
2.11k
      sum <<= 8;
92
2.11k
      s_util.c[0] = *(const uint8_t *)w;
93
2.11k
      w = (const uint16_t *)(const void *)((const uint8_t *)w + 1);
94
2.11k
      mlen--;
95
2.11k
      byte_swapped = 1;
96
2.11k
    }
97
    /*
98
     * Unroll the loop to make overhead from
99
     * branches &c small.
100
     */
101
260k
    while ((mlen -= 32) >= 0) {
102
213k
      sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
103
213k
      sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
104
213k
      sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
105
213k
      sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
106
213k
      w += 16;
107
213k
    }
108
46.3k
    mlen += 32;
109
98.8k
    while ((mlen -= 8) >= 0) {
110
52.5k
      sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
111
52.5k
      w += 4;
112
52.5k
    }
113
46.3k
    mlen += 8;
114
46.3k
    if (mlen == 0 && byte_swapped == 0)
115
16.0k
      continue;
116
30.2k
    REDUCE;
117
75.4k
    while ((mlen -= 2) >= 0) {
118
45.1k
      sum += *w++;
119
45.1k
    }
120
30.2k
    if (byte_swapped) {
121
2.11k
      REDUCE;
122
2.11k
      sum <<= 8;
123
2.11k
      byte_swapped = 0;
124
2.11k
      if (mlen == -1) {
125
1.56k
        s_util.c[1] = *(const uint8_t *)w;
126
1.56k
        sum += s_util.s;
127
1.56k
        mlen = 0;
128
1.56k
      } else
129
551
        mlen = -1;
130
28.1k
    } else if (mlen == -1)
131
8.15k
      s_util.c[0] = *(const uint8_t *)w;
132
30.2k
  }
133
21.8k
  if (mlen == -1) {
134
    /* The last mbuf has odd # of bytes. Follow the
135
       standard (the odd byte may be shifted left by 8 bits
136
       or not as determined by endian-ness of the machine) */
137
8.70k
    s_util.c[1] = 0;
138
8.70k
    sum += s_util.s;
139
8.70k
  }
140
21.8k
  REDUCE;
141
21.8k
  return (~sum & 0xffff);
142
21.8k
}
143
144
int
145
in_cksum(const vec_t *vec, int veclen)
146
20.1k
{
147
20.1k
  return in_cksum_ret_partial(vec, veclen, NULL);
148
20.1k
}
149
150
uint16_t
151
ip_checksum(const uint8_t *ptr, int len)
152
309
{
153
309
  vec_t cksum_vec[1];
154
155
309
  SET_CKSUM_VEC_PTR(cksum_vec[0], ptr, len);
156
309
  return in_cksum_ret_partial(&cksum_vec[0], 1, NULL);
157
309
}
158
159
uint16_t
160
ip_checksum_tvb(tvbuff_t *tvb, int offset, int len)
161
1.40k
{
162
1.40k
  vec_t cksum_vec[1];
163
164
1.40k
  SET_CKSUM_VEC_TVB(cksum_vec[0], tvb, offset, len);
165
1.40k
  return in_cksum_ret_partial(&cksum_vec[0], 1, NULL);
166
1.40k
}
167
168
/*
169
 * Given the host-byte-order value of the checksum field in a packet
170
 * header, and the network-byte-order computed checksum of the data
171
 * that the checksum covers (including the checksum itself), compute
172
 * what the checksum field *should* have been.
173
 *
174
 * This always returns +0 (0x0000) not -0 (0xffff). The few protocols,
175
 * like ICMP, that can have an all zero packet (aside from the checksum
176
 * field) such that 0xffff is the correct result should test for that
177
 * case before, after, or instead of calling this method.
178
 */
179
uint16_t
180
in_cksum_shouldbe(uint16_t sum, uint16_t computed_sum)
181
11.6k
{
182
11.6k
  uint32_t shouldbe;
183
184
  /*
185
   * The value that should have gone into the checksum field
186
   * is the negative of the value gotten by summing up everything
187
   * *but* the checksum field.
188
   *
189
   * We can compute that by subtracting the value of the checksum
190
   * field from the sum of all the data in the packet, and then
191
   * computing the negative of that value.
192
   *
193
   * "sum" is the value of the checksum field, and "computed_sum"
194
   * is the negative of the sum of all the data in the packets,
195
   * so that's -(-computed_sum - sum), or (sum + computed_sum).
196
   *
197
   * All the arithmetic in question is one's complement, so the
198
   * addition must include an end-around carry; we do this by
199
   * doing the arithmetic in 32 bits (with no sign-extension),
200
   * and then adding the upper 16 bits of the sum, which contain
201
   * the carry, to the lower 16 bits of the sum, and then do it
202
   * again in case *that* sum produced a carry. (XXX - It won't.
203
   * It can't be any larger than 0xFFFF + 0xFFFF = 0x1FFFE,
204
   * which only carries once back to 0xFFFF.)
205
   *
206
   * Also, since all the arithmetic is one's complement, +0 (0x0000)
207
   * and -0 (0xFFFF) are indistinguishable. (Different ways of
208
   * performing the calculation can yield one zero or the other for
209
   * different inputs.) Between the two, +0 is the representation we
210
   * want unless all the bits of the packet except for the checksum
211
   * field are zero, but we can't know that from our inputs here.
212
   * Most protocols which use this checksum require at least some
213
   * nonzero bits (a version, a length field, something either directly
214
   * or in a pseudoheader), and so +0 is correct. Despite this, some
215
   * networking stacks put 0xFFFF when 0x0000 is appropriate, see RFC
216
   * 1624.
217
   *
218
   * As RFC 1071 notes, the checksum can be computed without
219
   * byte-swapping the 16-bit words; summing 16-bit words
220
   * on a big-endian machine gives a big-endian checksum, which
221
   * can be directly stuffed into the big-endian checksum fields
222
   * in protocol headers, and summing words on a little-endian
223
   * machine gives a little-endian checksum, which must be
224
   * byte-swapped before being stuffed into a big-endian checksum
225
   * field.
226
   *
227
   * "computed_sum" is a network-byte-order value, so we must put
228
   * it in host byte order before subtracting it from the
229
   * host-byte-order value from the header; the adjusted checksum
230
   * will be in host byte order, which is what we'll return.
231
   */
232
11.6k
  shouldbe = sum;
233
11.6k
  shouldbe += g_ntohs(computed_sum);
234
11.6k
  shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
235
11.6k
  shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16); // XXX - Unneeded.
236
  /* Always return +0, not -0.
237
   * There are some other ways to always return +0, such as
238
   * calculating -(-computed_sum + -sum) instead; the fastest
239
   * approach is likely CPU and compiler dependent.
240
   */
241
11.6k
  return shouldbe == 0xFFFF ? 0 : shouldbe;
242
11.6k
}
243
244
/*
245
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
246
 *
247
 * Local variables:
248
 * c-basic-offset: 8
249
 * tab-width: 8
250
 * indent-tabs-mode: t
251
 * End:
252
 *
253
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
254
 * :indentSize=8:tabSize=8:noTabs=false:
255
 */