2000-12-13 02:24:23 +00:00
|
|
|
/* in_cksum.c
|
|
|
|
* 4.4-Lite-2 Internet checksum routine, modified to take a vector of
|
2011-01-19 22:57:19 +00:00
|
|
|
* pointers/lengths giving the pieces to be checksummed. Also using
|
|
|
|
* Tahoe/CGI version of ADDCARRY(x) macro instead of from portable version.
|
2000-12-13 02:24:23 +00:00
|
|
|
*
|
|
|
|
* Copyright (c) 1988, 1992, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
2018-02-09 09:12:31 +00:00
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2000-12-13 02:24:23 +00:00
|
|
|
*
|
|
|
|
* @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
|
|
|
|
*/
|
|
|
|
|
2012-09-20 01:48:30 +00:00
|
|
|
#include "config.h"
|
2000-12-13 02:24:23 +00:00
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
Clean up Internet checksum handling.
Add macros to set entries of a vec_t, one for use when you have a
pointer to private data, and one for use when you have data in a tvbuff.
The latter wraps the use of tvb_get_ptr(), so that you're not directly
calling it in a dissector.
Move ip_checksum() to epan/in_cksum.c, and add an ip_checksum_tvb() that
wraps the use of tvb_get_ptr().
In the CARP dissector, give the length variable an unsigned type -
there's no benefit to it being signed, and that requires some casts to
be thrown around.
In the DCCP dissector, check only against the coverage length to see if
we have enough data, combine the "should we check the checksum?" check
with the "*can* we check the checksum?" check in a single if, and throw
a dissector assertion if the source network address type isn't IPv4 or
IPv6.
Get rid of inclues of <epan/in_cksum.h> in dissectors that don't use any
of the Internet checksum routines.
In the HIP dissector, make sure we have the data to calculate the
checksum before doing so.
Change-Id: I2f9674775dbb54c533d33082632809f7d32ec8ae
Reviewed-on: https://code.wireshark.org/review/3517
Reviewed-by: Guy Harris <guy@alum.mit.edu>
2014-08-09 01:09:00 +00:00
|
|
|
#include <epan/tvbuff.h>
|
2004-09-28 00:06:32 +00:00
|
|
|
#include <epan/in_cksum.h>
|
2000-12-13 02:24:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Checksum routine for Internet Protocol family headers (Portable Version).
|
|
|
|
*
|
|
|
|
* This routine is very heavily used in the network
|
|
|
|
* code and should be modified for each CPU to be as fast as possible.
|
|
|
|
*/
|
|
|
|
|
2011-01-19 22:53:38 +00:00
|
|
|
#define ADDCARRY(x) {if ((x) > 65535) (x) -= 65535;}
|
2000-12-13 02:24:23 +00:00
|
|
|
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
|
|
|
|
|
TCP, UDP: Calculate partial (pseudo header) checksums for offload
Linux and Windows, at least, when performing Local Checksum Offload
during Generic Segmentation Offload and at other times, place the one's
complement sum of the pseudo header in the checksum field, which
provides the necessary correction when a device (or its driver,
if not supported in hardware) computes the one's complement checksum
of each inner layer buffer in the skbuff. (This is why GSO requires
equal length buffers - so that the pseudo header contribution to
the checksum is the same.)
When performing our Internet checksum calculation, we can output
the partial sum of all but the last vector, which is an intermediate
result we calculate in the process anyway. The last vector is
generally the payload, and the previous vectors are for the pseudo
header. We can then compare this partial sum to the value in the
UDP or TCP header if the overall computed checksum isn't 0.
If it matches appropriately, we can have a more informative and
less scary message.
Update the tests a bit because this causes checksums to no longer
fail and be reported malformed in the http2 reassembly example.
Fix #18864. Related to #19109
2023-11-09 16:51:43 +00:00
|
|
|
/*
|
|
|
|
* Linux and Windows, at least, when performing Local Checksum Offload
|
|
|
|
* store the one's complement sum (not inverted to its bitwise complement)
|
|
|
|
* of the pseudo header in the checksum field (instead of intializing
|
|
|
|
* to zero), allowing the device driver to calculate the real checksum
|
|
|
|
* later without needing knowledge of the pseudoheader itself.
|
|
|
|
* (This is presumably why GSO requires equal length buffers - so that the
|
|
|
|
* pseudo header contribution to the checksum, which includes the payload
|
|
|
|
* length, is the same.)
|
|
|
|
*
|
|
|
|
* We can output this partial checksum as an intermediate result,
|
|
|
|
* assuming that the pseudo header is all but the last chunk in the vector.
|
|
|
|
* Note that unlike the final output it is not inverted, and that it
|
|
|
|
* (like the final computed checksum) is is network byte order.
|
|
|
|
*/
|
2000-12-13 02:24:23 +00:00
|
|
|
int
|
TCP, UDP: Calculate partial (pseudo header) checksums for offload
Linux and Windows, at least, when performing Local Checksum Offload
during Generic Segmentation Offload and at other times, place the one's
complement sum of the pseudo header in the checksum field, which
provides the necessary correction when a device (or its driver,
if not supported in hardware) computes the one's complement checksum
of each inner layer buffer in the skbuff. (This is why GSO requires
equal length buffers - so that the pseudo header contribution to
the checksum is the same.)
When performing our Internet checksum calculation, we can output
the partial sum of all but the last vector, which is an intermediate
result we calculate in the process anyway. The last vector is
generally the payload, and the previous vectors are for the pseudo
header. We can then compare this partial sum to the value in the
UDP or TCP header if the overall computed checksum isn't 0.
If it matches appropriately, we can have a more informative and
less scary message.
Update the tests a bit because this causes checksums to no longer
fail and be reported malformed in the http2 reassembly example.
Fix #18864. Related to #19109
2023-11-09 16:51:43 +00:00
|
|
|
in_cksum_ret_partial(const vec_t *vec, int veclen, uint16_t *partial)
|
2000-12-13 02:24:23 +00:00
|
|
|
{
|
|
|
|
register const guint16 *w;
|
|
|
|
register int sum = 0;
|
|
|
|
register int mlen = 0;
|
|
|
|
int byte_swapped = 0;
|
|
|
|
|
|
|
|
union {
|
2001-06-09 01:02:29 +00:00
|
|
|
guint8 c[2];
|
2000-12-13 02:24:23 +00:00
|
|
|
guint16 s;
|
|
|
|
} s_util;
|
|
|
|
union {
|
|
|
|
guint16 s[2];
|
2001-06-09 01:02:29 +00:00
|
|
|
guint32 l;
|
2000-12-13 02:24:23 +00:00
|
|
|
} l_util;
|
|
|
|
|
|
|
|
for (; veclen != 0; vec++, veclen--) {
|
TCP, UDP: Calculate partial (pseudo header) checksums for offload
Linux and Windows, at least, when performing Local Checksum Offload
during Generic Segmentation Offload and at other times, place the one's
complement sum of the pseudo header in the checksum field, which
provides the necessary correction when a device (or its driver,
if not supported in hardware) computes the one's complement checksum
of each inner layer buffer in the skbuff. (This is why GSO requires
equal length buffers - so that the pseudo header contribution to
the checksum is the same.)
When performing our Internet checksum calculation, we can output
the partial sum of all but the last vector, which is an intermediate
result we calculate in the process anyway. The last vector is
generally the payload, and the previous vectors are for the pseudo
header. We can then compare this partial sum to the value in the
UDP or TCP header if the overall computed checksum isn't 0.
If it matches appropriately, we can have a more informative and
less scary message.
Update the tests a bit because this causes checksums to no longer
fail and be reported malformed in the http2 reassembly example.
Fix #18864. Related to #19109
2023-11-09 16:51:43 +00:00
|
|
|
if (veclen == 1 && partial) {
|
|
|
|
REDUCE;
|
|
|
|
*partial = sum;
|
|
|
|
}
|
2000-12-13 02:24:23 +00:00
|
|
|
if (vec->len == 0)
|
|
|
|
continue;
|
2011-06-16 03:56:08 +00:00
|
|
|
w = (const guint16 *)(const void *)vec->ptr;
|
2000-12-13 02:24:23 +00:00
|
|
|
if (mlen == -1) {
|
|
|
|
/*
|
|
|
|
* The first byte of this chunk is the continuation
|
|
|
|
* of a word spanning between this chunk and the
|
|
|
|
* last chunk.
|
|
|
|
*
|
2002-08-28 21:04:11 +00:00
|
|
|
* s_util.c[0] is already saved when scanning previous
|
2000-12-13 02:24:23 +00:00
|
|
|
* chunk.
|
|
|
|
*/
|
2001-06-09 01:02:29 +00:00
|
|
|
s_util.c[1] = *(const guint8 *)w;
|
2000-12-13 02:24:23 +00:00
|
|
|
sum += s_util.s;
|
2011-06-16 03:56:08 +00:00
|
|
|
w = (const guint16 *)(const void *)((const guint8 *)w + 1);
|
2000-12-13 02:24:23 +00:00
|
|
|
mlen = vec->len - 1;
|
|
|
|
} else
|
|
|
|
mlen = vec->len;
|
|
|
|
/*
|
|
|
|
* Force to even boundary.
|
|
|
|
*/
|
2015-06-08 07:57:35 +00:00
|
|
|
if ((1 & (gintptr)w) && (mlen > 0)) {
|
2000-12-13 02:24:23 +00:00
|
|
|
REDUCE;
|
|
|
|
sum <<= 8;
|
|
|
|
s_util.c[0] = *(const guint8 *)w;
|
2011-06-16 03:56:08 +00:00
|
|
|
w = (const guint16 *)(const void *)((const guint8 *)w + 1);
|
2000-12-13 02:24:23 +00:00
|
|
|
mlen--;
|
|
|
|
byte_swapped = 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Unroll the loop to make overhead from
|
|
|
|
* branches &c small.
|
|
|
|
*/
|
|
|
|
while ((mlen -= 32) >= 0) {
|
|
|
|
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
|
|
|
|
sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
|
|
|
|
sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
|
|
|
|
sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
|
|
|
|
w += 16;
|
|
|
|
}
|
|
|
|
mlen += 32;
|
|
|
|
while ((mlen -= 8) >= 0) {
|
|
|
|
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
|
|
|
|
w += 4;
|
|
|
|
}
|
|
|
|
mlen += 8;
|
|
|
|
if (mlen == 0 && byte_swapped == 0)
|
|
|
|
continue;
|
|
|
|
REDUCE;
|
|
|
|
while ((mlen -= 2) >= 0) {
|
|
|
|
sum += *w++;
|
|
|
|
}
|
|
|
|
if (byte_swapped) {
|
|
|
|
REDUCE;
|
|
|
|
sum <<= 8;
|
|
|
|
byte_swapped = 0;
|
|
|
|
if (mlen == -1) {
|
2001-06-09 01:02:29 +00:00
|
|
|
s_util.c[1] = *(const guint8 *)w;
|
2000-12-13 02:24:23 +00:00
|
|
|
sum += s_util.s;
|
|
|
|
mlen = 0;
|
|
|
|
} else
|
|
|
|
mlen = -1;
|
|
|
|
} else if (mlen == -1)
|
2001-06-09 01:02:29 +00:00
|
|
|
s_util.c[0] = *(const guint8 *)w;
|
2000-12-13 02:24:23 +00:00
|
|
|
}
|
|
|
|
if (mlen == -1) {
|
|
|
|
/* The last mbuf has odd # of bytes. Follow the
|
|
|
|
standard (the odd byte may be shifted left by 8 bits
|
|
|
|
or not as determined by endian-ness of the machine) */
|
|
|
|
s_util.c[1] = 0;
|
|
|
|
sum += s_util.s;
|
|
|
|
}
|
|
|
|
REDUCE;
|
|
|
|
return (~sum & 0xffff);
|
|
|
|
}
|
|
|
|
|
TCP, UDP: Calculate partial (pseudo header) checksums for offload
Linux and Windows, at least, when performing Local Checksum Offload
during Generic Segmentation Offload and at other times, place the one's
complement sum of the pseudo header in the checksum field, which
provides the necessary correction when a device (or its driver,
if not supported in hardware) computes the one's complement checksum
of each inner layer buffer in the skbuff. (This is why GSO requires
equal length buffers - so that the pseudo header contribution to
the checksum is the same.)
When performing our Internet checksum calculation, we can output
the partial sum of all but the last vector, which is an intermediate
result we calculate in the process anyway. The last vector is
generally the payload, and the previous vectors are for the pseudo
header. We can then compare this partial sum to the value in the
UDP or TCP header if the overall computed checksum isn't 0.
If it matches appropriately, we can have a more informative and
less scary message.
Update the tests a bit because this causes checksums to no longer
fail and be reported malformed in the http2 reassembly example.
Fix #18864. Related to #19109
2023-11-09 16:51:43 +00:00
|
|
|
int
|
|
|
|
in_cksum(const vec_t *vec, int veclen)
|
|
|
|
{
|
|
|
|
return in_cksum_ret_partial(vec, veclen, NULL);
|
|
|
|
}
|
|
|
|
|
Clean up Internet checksum handling.
Add macros to set entries of a vec_t, one for use when you have a
pointer to private data, and one for use when you have data in a tvbuff.
The latter wraps the use of tvb_get_ptr(), so that you're not directly
calling it in a dissector.
Move ip_checksum() to epan/in_cksum.c, and add an ip_checksum_tvb() that
wraps the use of tvb_get_ptr().
In the CARP dissector, give the length variable an unsigned type -
there's no benefit to it being signed, and that requires some casts to
be thrown around.
In the DCCP dissector, check only against the coverage length to see if
we have enough data, combine the "should we check the checksum?" check
with the "*can* we check the checksum?" check in a single if, and throw
a dissector assertion if the source network address type isn't IPv4 or
IPv6.
Get rid of inclues of <epan/in_cksum.h> in dissectors that don't use any
of the Internet checksum routines.
In the HIP dissector, make sure we have the data to calculate the
checksum before doing so.
Change-Id: I2f9674775dbb54c533d33082632809f7d32ec8ae
Reviewed-on: https://code.wireshark.org/review/3517
Reviewed-by: Guy Harris <guy@alum.mit.edu>
2014-08-09 01:09:00 +00:00
|
|
|
guint16
|
|
|
|
ip_checksum(const guint8 *ptr, int len)
|
|
|
|
{
|
|
|
|
vec_t cksum_vec[1];
|
|
|
|
|
|
|
|
SET_CKSUM_VEC_PTR(cksum_vec[0], ptr, len);
|
TCP, UDP: Calculate partial (pseudo header) checksums for offload
Linux and Windows, at least, when performing Local Checksum Offload
during Generic Segmentation Offload and at other times, place the one's
complement sum of the pseudo header in the checksum field, which
provides the necessary correction when a device (or its driver,
if not supported in hardware) computes the one's complement checksum
of each inner layer buffer in the skbuff. (This is why GSO requires
equal length buffers - so that the pseudo header contribution to
the checksum is the same.)
When performing our Internet checksum calculation, we can output
the partial sum of all but the last vector, which is an intermediate
result we calculate in the process anyway. The last vector is
generally the payload, and the previous vectors are for the pseudo
header. We can then compare this partial sum to the value in the
UDP or TCP header if the overall computed checksum isn't 0.
If it matches appropriately, we can have a more informative and
less scary message.
Update the tests a bit because this causes checksums to no longer
fail and be reported malformed in the http2 reassembly example.
Fix #18864. Related to #19109
2023-11-09 16:51:43 +00:00
|
|
|
return in_cksum_ret_partial(&cksum_vec[0], 1, NULL);
|
Clean up Internet checksum handling.
Add macros to set entries of a vec_t, one for use when you have a
pointer to private data, and one for use when you have data in a tvbuff.
The latter wraps the use of tvb_get_ptr(), so that you're not directly
calling it in a dissector.
Move ip_checksum() to epan/in_cksum.c, and add an ip_checksum_tvb() that
wraps the use of tvb_get_ptr().
In the CARP dissector, give the length variable an unsigned type -
there's no benefit to it being signed, and that requires some casts to
be thrown around.
In the DCCP dissector, check only against the coverage length to see if
we have enough data, combine the "should we check the checksum?" check
with the "*can* we check the checksum?" check in a single if, and throw
a dissector assertion if the source network address type isn't IPv4 or
IPv6.
Get rid of inclues of <epan/in_cksum.h> in dissectors that don't use any
of the Internet checksum routines.
In the HIP dissector, make sure we have the data to calculate the
checksum before doing so.
Change-Id: I2f9674775dbb54c533d33082632809f7d32ec8ae
Reviewed-on: https://code.wireshark.org/review/3517
Reviewed-by: Guy Harris <guy@alum.mit.edu>
2014-08-09 01:09:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
guint16
|
|
|
|
ip_checksum_tvb(tvbuff_t *tvb, int offset, int len)
|
|
|
|
{
|
|
|
|
vec_t cksum_vec[1];
|
|
|
|
|
|
|
|
SET_CKSUM_VEC_TVB(cksum_vec[0], tvb, offset, len);
|
TCP, UDP: Calculate partial (pseudo header) checksums for offload
Linux and Windows, at least, when performing Local Checksum Offload
during Generic Segmentation Offload and at other times, place the one's
complement sum of the pseudo header in the checksum field, which
provides the necessary correction when a device (or its driver,
if not supported in hardware) computes the one's complement checksum
of each inner layer buffer in the skbuff. (This is why GSO requires
equal length buffers - so that the pseudo header contribution to
the checksum is the same.)
When performing our Internet checksum calculation, we can output
the partial sum of all but the last vector, which is an intermediate
result we calculate in the process anyway. The last vector is
generally the payload, and the previous vectors are for the pseudo
header. We can then compare this partial sum to the value in the
UDP or TCP header if the overall computed checksum isn't 0.
If it matches appropriately, we can have a more informative and
less scary message.
Update the tests a bit because this causes checksums to no longer
fail and be reported malformed in the http2 reassembly example.
Fix #18864. Related to #19109
2023-11-09 16:51:43 +00:00
|
|
|
return in_cksum_ret_partial(&cksum_vec[0], 1, NULL);
|
Clean up Internet checksum handling.
Add macros to set entries of a vec_t, one for use when you have a
pointer to private data, and one for use when you have data in a tvbuff.
The latter wraps the use of tvb_get_ptr(), so that you're not directly
calling it in a dissector.
Move ip_checksum() to epan/in_cksum.c, and add an ip_checksum_tvb() that
wraps the use of tvb_get_ptr().
In the CARP dissector, give the length variable an unsigned type -
there's no benefit to it being signed, and that requires some casts to
be thrown around.
In the DCCP dissector, check only against the coverage length to see if
we have enough data, combine the "should we check the checksum?" check
with the "*can* we check the checksum?" check in a single if, and throw
a dissector assertion if the source network address type isn't IPv4 or
IPv6.
Get rid of inclues of <epan/in_cksum.h> in dissectors that don't use any
of the Internet checksum routines.
In the HIP dissector, make sure we have the data to calculate the
checksum before doing so.
Change-Id: I2f9674775dbb54c533d33082632809f7d32ec8ae
Reviewed-on: https://code.wireshark.org/review/3517
Reviewed-by: Guy Harris <guy@alum.mit.edu>
2014-08-09 01:09:00 +00:00
|
|
|
}
|
|
|
|
|
2000-12-13 02:24:23 +00:00
|
|
|
/*
|
|
|
|
* Given the host-byte-order value of the checksum field in a packet
|
2002-07-21 20:31:21 +00:00
|
|
|
* header, and the network-byte-order computed checksum of the data
|
|
|
|
* that the checksum covers (including the checksum itself), compute
|
|
|
|
* what the checksum field *should* have been.
|
2000-12-13 02:24:23 +00:00
|
|
|
*/
|
|
|
|
guint16
|
|
|
|
in_cksum_shouldbe(guint16 sum, guint16 computed_sum)
|
|
|
|
{
|
|
|
|
guint32 shouldbe;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The value that should have gone into the checksum field
|
|
|
|
* is the negative of the value gotten by summing up everything
|
|
|
|
* *but* the checksum field.
|
|
|
|
*
|
|
|
|
* We can compute that by subtracting the value of the checksum
|
|
|
|
* field from the sum of all the data in the packet, and then
|
|
|
|
* computing the negative of that value.
|
|
|
|
*
|
|
|
|
* "sum" is the value of the checksum field, and "computed_sum"
|
|
|
|
* is the negative of the sum of all the data in the packets,
|
|
|
|
* so that's -(-computed_sum - sum), or (sum + computed_sum).
|
|
|
|
*
|
|
|
|
* All the arithmetic in question is one's complement, so the
|
|
|
|
* addition must include an end-around carry; we do this by
|
|
|
|
* doing the arithmetic in 32 bits (with no sign-extension),
|
|
|
|
* and then adding the upper 16 bits of the sum, which contain
|
|
|
|
* the carry, to the lower 16 bits of the sum, and then do it
|
|
|
|
* again in case *that* sum produced a carry.
|
|
|
|
*
|
|
|
|
* As RFC 1071 notes, the checksum can be computed without
|
|
|
|
* byte-swapping the 16-bit words; summing 16-bit words
|
|
|
|
* on a big-endian machine gives a big-endian checksum, which
|
|
|
|
* can be directly stuffed into the big-endian checksum fields
|
|
|
|
* in protocol headers, and summing words on a little-endian
|
|
|
|
* machine gives a little-endian checksum, which must be
|
|
|
|
* byte-swapped before being stuffed into a big-endian checksum
|
|
|
|
* field.
|
|
|
|
*
|
2002-07-21 20:31:21 +00:00
|
|
|
* "computed_sum" is a network-byte-order value, so we must put
|
|
|
|
* it in host byte order before subtracting it from the
|
|
|
|
* host-byte-order value from the header; the adjusted checksum
|
|
|
|
* will be in host byte order, which is what we'll return.
|
2000-12-13 02:24:23 +00:00
|
|
|
*/
|
|
|
|
shouldbe = sum;
|
2002-08-02 23:36:07 +00:00
|
|
|
shouldbe += g_ntohs(computed_sum);
|
2000-12-13 02:24:23 +00:00
|
|
|
shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
|
|
|
|
shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
|
|
|
|
return shouldbe;
|
|
|
|
}
|
2014-10-10 14:41:10 +00:00
|
|
|
|
|
|
|
/*
|
2019-07-26 18:43:17 +00:00
|
|
|
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
2014-10-10 14:41:10 +00:00
|
|
|
*
|
|
|
|
* Local variables:
|
|
|
|
* c-basic-offset: 8
|
|
|
|
* tab-width: 8
|
|
|
|
* indent-tabs-mode: t
|
|
|
|
* End:
|
|
|
|
*
|
|
|
|
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
|
|
|
|
* :indentSize=8:tabSize=8:noTabs=false:
|
|
|
|
*/
|