aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--epan/dissectors/packet-tcp.c30
-rw-r--r--epan/dissectors/packet-udp.c55
-rw-r--r--epan/in_cksum.c31
-rw-r--r--epan/in_cksum.h2
-rw-r--r--packaging/debian/libwireshark0.symbols1
-rw-r--r--test/captures/http-ooo-fuzzed.pcapngbin0 -> 1584 bytes
-rw-r--r--test/suite_clopts.py25
7 files changed, 110 insertions, 34 deletions
diff --git a/epan/dissectors/packet-tcp.c b/epan/dissectors/packet-tcp.c
index 0a876853a8..18e00f7b80 100644
--- a/epan/dissectors/packet-tcp.c
+++ b/epan/dissectors/packet-tcp.c
@@ -472,6 +472,7 @@ static expert_field ei_tcp_connection_rst = EI_INIT;
static expert_field ei_tcp_connection_fin_active = EI_INIT;
static expert_field ei_tcp_connection_fin_passive = EI_INIT;
static expert_field ei_tcp_checksum_ffff = EI_INIT;
+static expert_field ei_tcp_checksum_partial = EI_INIT;
static expert_field ei_tcp_checksum_bad = EI_INIT;
static expert_field ei_tcp_urgent_pointer_non_zero = EI_INIT;
static expert_field ei_tcp_suboption_malformed = EI_INIT;
@@ -8392,8 +8393,12 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
DISSECTOR_ASSERT_NOT_REACHED();
break;
}
+ /* See discussion in packet-udp.c of partial checksums used in
+ * checksum offloading in Linux and Windows (and possibly others.)
+ */
+ uint16_t partial_cksum;
SET_CKSUM_VEC_TVB(cksum_vec[3], tvb, offset, reported_len);
- computed_cksum = in_cksum(cksum_vec, 4);
+ computed_cksum = in_cksum_ret_partial(cksum_vec, 4, &partial_cksum);
if (computed_cksum == 0 && th_sum == 0xffff) {
item = proto_tree_add_uint_format_value(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th_sum,
@@ -8415,11 +8420,23 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
desegment_ok = TRUE;
} else {
proto_item* calc_item;
- item = proto_tree_add_checksum(tcp_tree, tvb, offset+16, hf_tcp_checksum, hf_tcp_checksum_status, &ei_tcp_checksum_bad, pinfo, computed_cksum,
- ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM);
-
- calc_item = proto_tree_add_uint(tcp_tree, hf_tcp_checksum_calculated, tvb,
- offset + 16, 2, in_cksum_shouldbe(th_sum, computed_cksum));
+ uint16_t shouldbe_cksum = in_cksum_shouldbe(th_sum, computed_cksum);
+ if (th_sum == g_htons(partial_cksum)) {
+ /* Don't use PROTO_CHECKSUM_IN_CKSUM because we expect the value
+ * to match what we pass in. */
+ item = proto_tree_add_checksum(tcp_tree, tvb, offset+16, hf_tcp_checksum, hf_tcp_checksum_status, &ei_tcp_checksum_bad, pinfo, g_htons(partial_cksum),
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
+ proto_item_append_text(item, " (matches partial checksum, not 0x%4x, likely caused by \"TCP checksum offload\")", shouldbe_cksum);
+ expert_add_info(pinfo, item, &ei_tcp_checksum_partial);
+ computed_cksum = 0;
+ /* XXX Add a new status, e.g. PROTO_CHECKSUM_E_PARTIAL? */
+ } else {
+ item = proto_tree_add_checksum(tcp_tree, tvb, offset+16, hf_tcp_checksum, hf_tcp_checksum_status, &ei_tcp_checksum_bad, pinfo, computed_cksum,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM);
+ }
+ checksum_tree = proto_item_add_subtree(item, ett_tcp_checksum);
+ calc_item = proto_tree_add_uint(checksum_tree, hf_tcp_checksum_calculated, tvb,
+ offset + 16, 2, shouldbe_cksum);
proto_item_set_generated(calc_item);
/* Checksum is valid, so we're willing to desegment it. */
@@ -9718,6 +9735,7 @@ proto_register_tcp(void)
*/
{ &ei_tcp_connection_rst, { "tcp.connection.rst", PI_SEQUENCE, PI_WARN, "Connection reset (RST)", EXPFILL }},
{ &ei_tcp_checksum_ffff, { "tcp.checksum.ffff", PI_CHECKSUM, PI_WARN, "TCP Checksum 0xffff instead of 0x0000 (see RFC 1624)", EXPFILL }},
+ { &ei_tcp_checksum_partial, { "tcp.checksum.partial", PI_CHECKSUM, PI_NOTE, "Partial (pseudo header) checksum (likely caused by \"TCP checksum offload\")", EXPFILL }},
{ &ei_tcp_checksum_bad, { "tcp.checksum_bad.expert", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }},
{ &ei_tcp_urgent_pointer_non_zero, { "tcp.urgent_pointer.non_zero", PI_PROTOCOL, PI_NOTE, "The urgent pointer field is nonzero while the URG flag is not set", EXPFILL }},
{ &ei_tcp_suboption_malformed, { "tcp.suboption_malformed", PI_MALFORMED, PI_ERROR, "suboption would go past end of option", EXPFILL }},
diff --git a/epan/dissectors/packet-udp.c b/epan/dissectors/packet-udp.c
index 180c479899..ba5b5d2170 100644
--- a/epan/dissectors/packet-udp.c
+++ b/epan/dissectors/packet-udp.c
@@ -81,6 +81,7 @@ static expert_field ei_udp_possible_traceroute = EI_INIT;
static expert_field ei_udp_length_bad = EI_INIT;
static expert_field ei_udplite_checksum_coverage_bad = EI_INIT;
static expert_field ei_udp_checksum_zero = EI_INIT;
+static expert_field ei_udp_checksum_partial = EI_INIT;
static expert_field ei_udp_checksum_bad = EI_INIT;
static expert_field ei_udp_length_bad_zero = EI_INIT;
@@ -1128,13 +1129,6 @@ dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 ip_proto)
DISSECTOR_ASSERT_NOT_REACHED();
break;
}
- SET_CKSUM_VEC_TVB(cksum_vec[3], tvb, offset, udph->uh_sum_cov);
- computed_cksum = in_cksum(&cksum_vec[0], 4);
-
- item = proto_tree_add_checksum(udp_tree, tvb, offset + 6, hf_udp_checksum, hf_udp_checksum_status, &ei_udp_checksum_bad,
- pinfo, computed_cksum, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM);
- checksum_tree = proto_item_add_subtree(item, ett_udp_checksum);
-
/*
* in_cksum() should never return 0xFFFF here, because, to quote
* RFC 1624 section 3 "Discussion":
@@ -1162,15 +1156,49 @@ dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 ip_proto)
* all zero, so the sum won't be 0 (+0), and thus the negation
* won't be -0, i.e. won't be 0xFFFF.
*/
+ /*
+ * Linux and Windows, at least, when performing Local Checksum
+ * Offload (during Generic Segmentation Offload or at other
+ * times), place the one's complement sum of the pseudo header
+ * in the checksum fields initially, which provides the necessary
+ * correction when a device (or driver) computes the one's
+ * complement checksum of each buffer in the skbuff. Since it is
+ * not inverted, the partial_cksum will never be 0x0000 by the
+ * same argument as above.
+ */
+ uint16_t partial_cksum;
+ SET_CKSUM_VEC_TVB(cksum_vec[3], tvb, offset, udph->uh_sum_cov);
+ computed_cksum = in_cksum_ret_partial(&cksum_vec[0], 4, &partial_cksum);
+ uint16_t shouldbe_cksum = in_cksum_shouldbe(udph->uh_sum, computed_cksum);
+ if (computed_cksum != 0 && udph->uh_sum == g_htons(partial_cksum)) {
+ /* Don't use PROTO_CHECKSUM_IN_CKSUM because we expect the value
+ * to match what we pass in. */
+ item = proto_tree_add_checksum(udp_tree, tvb, offset + 6, hf_udp_checksum, hf_udp_checksum_status, &ei_udp_checksum_bad,
+ pinfo, g_htons(partial_cksum), ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
+ proto_item_append_text(item, " (matches partial checksum, not 0x%4x, likely caused by \"UDP checksum offload\")", shouldbe_cksum);
+ expert_add_info(pinfo, item, &ei_udp_checksum_partial);
+ computed_cksum = 0;
+ /* XXX: Find some way hint to QUIC (or other dissectors) that
+ * GSO is a possibility so that extra effort can be made to
+ * recover coalesced PDUs? E.g., by searching heuristically
+ * through the payload for the DCID bytes if they're non-zero?
+ * Add a new status, e.g. PROTO_CHECKSUM_E_PARTIAL?
+ */
+ } else {
+ item = proto_tree_add_checksum(udp_tree, tvb, offset + 6, hf_udp_checksum, hf_udp_checksum_status, &ei_udp_checksum_bad,
+ pinfo, computed_cksum, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM);
+ }
+ checksum_tree = proto_item_add_subtree(item, ett_udp_checksum);
+
if (computed_cksum != 0) {
- proto_item_append_text(item, " (maybe caused by \"UDP checksum offload\"?)");
- col_append_str(pinfo->cinfo, COL_INFO, " [UDP CHECKSUM INCORRECT]");
- calc_item = proto_tree_add_uint(checksum_tree, hf_udp_checksum_calculated,
- tvb, offset + 6, 2, in_cksum_shouldbe(udph->uh_sum, computed_cksum));
+ proto_item_append_text(item, " (maybe caused by \"UDP checksum offload\"?)");
+ col_append_str(pinfo->cinfo, COL_INFO, " [UDP CHECKSUM INCORRECT]");
+ calc_item = proto_tree_add_uint(checksum_tree, hf_udp_checksum_calculated,
+ tvb, offset + 6, 2, in_cksum_shouldbe(udph->uh_sum, computed_cksum));
}
else {
- calc_item = proto_tree_add_uint(checksum_tree, hf_udp_checksum_calculated,
- tvb, offset + 6, 2, udph->uh_sum);
+ calc_item = proto_tree_add_uint(checksum_tree, hf_udp_checksum_calculated,
+ tvb, offset + 6, 2, shouldbe_cksum);
}
proto_item_set_generated(calc_item);
@@ -1399,6 +1427,7 @@ proto_register_udp(void)
{ &ei_udp_length_bad, { "udp.length.bad", PI_MALFORMED, PI_ERROR, "Bad length value", EXPFILL }},
{ &ei_udplite_checksum_coverage_bad, { "udplite.checksum_coverage.bad", PI_MALFORMED, PI_ERROR, "Bad checksum coverage length value", EXPFILL }},
{ &ei_udp_checksum_zero, { "udp.checksum.zero", PI_CHECKSUM, PI_ERROR, "Illegal checksum value (0)", EXPFILL }},
+ { &ei_udp_checksum_partial, { "udp.checksum.partial", PI_CHECKSUM, PI_NOTE, "Partial (pseudo header) checksum (likely caused by \"UDP checksum offload\")", EXPFILL }},
{ &ei_udp_checksum_bad, { "udp.checksum.bad", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }},
{ &ei_udp_length_bad_zero, { "udp.length.bad_zero", PI_PROTOCOL, PI_WARN, "Length is zero but payload < 65536", EXPFILL }},
};
diff --git a/epan/in_cksum.c b/epan/in_cksum.c
index 3dfc757839..5bb154bb95 100644
--- a/epan/in_cksum.c
+++ b/epan/in_cksum.c
@@ -28,8 +28,23 @@
#define ADDCARRY(x) {if ((x) > 65535) (x) -= 65535;}
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+/*
+ * 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.
+ */
int
-in_cksum(const vec_t *vec, int veclen)
+in_cksum_ret_partial(const vec_t *vec, int veclen, uint16_t *partial)
{
register const guint16 *w;
register int sum = 0;
@@ -46,6 +61,10 @@ in_cksum(const vec_t *vec, int veclen)
} l_util;
for (; veclen != 0; vec++, veclen--) {
+ if (veclen == 1 && partial) {
+ REDUCE;
+ *partial = sum;
+ }
if (vec->len == 0)
continue;
w = (const guint16 *)(const void *)vec->ptr;
@@ -122,13 +141,19 @@ in_cksum(const vec_t *vec, int veclen)
return (~sum & 0xffff);
}
+int
+in_cksum(const vec_t *vec, int veclen)
+{
+ return in_cksum_ret_partial(vec, veclen, NULL);
+}
+
guint16
ip_checksum(const guint8 *ptr, int len)
{
vec_t cksum_vec[1];
SET_CKSUM_VEC_PTR(cksum_vec[0], ptr, len);
- return in_cksum(&cksum_vec[0], 1);
+ return in_cksum_ret_partial(&cksum_vec[0], 1, NULL);
}
guint16
@@ -137,7 +162,7 @@ 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);
- return in_cksum(&cksum_vec[0], 1);
+ return in_cksum_ret_partial(&cksum_vec[0], 1, NULL);
}
/*
diff --git a/epan/in_cksum.h b/epan/in_cksum.h
index 671546f6df..d0f6fdd68d 100644
--- a/epan/in_cksum.h
+++ b/epan/in_cksum.h
@@ -33,6 +33,8 @@ WS_DLL_PUBLIC guint16 ip_checksum(const guint8 *ptr, int len);
WS_DLL_PUBLIC guint16 ip_checksum_tvb(tvbuff_t *tvb, int offset, int len);
+WS_DLL_PUBLIC int in_cksum_ret_partial(const vec_t *vec, int veclen, uint16_t *partial);
+
WS_DLL_PUBLIC int in_cksum(const vec_t *vec, int veclen);
guint16 in_cksum_shouldbe(guint16 sum, guint16 computed_sum);
diff --git a/packaging/debian/libwireshark0.symbols b/packaging/debian/libwireshark0.symbols
index 88644041cf..a3c3f32fe1 100644
--- a/packaging/debian/libwireshark0.symbols
+++ b/packaging/debian/libwireshark0.symbols
@@ -1071,6 +1071,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
ieee80211_supported_rates_vals_ext@Base 1.99.1
ieee802a_add_oui@Base 1.9.1
in_cksum@Base 1.9.1
+ in_cksum_ret_partial@Base 4.3.0
init_srt_table@Base 1.99.8
init_srt_table_row@Base 1.99.8
ip_checksum@Base 1.99.0
diff --git a/test/captures/http-ooo-fuzzed.pcapng b/test/captures/http-ooo-fuzzed.pcapng
new file mode 100644
index 0000000000..7cb871c3bc
--- /dev/null
+++ b/test/captures/http-ooo-fuzzed.pcapng
Binary files differ
diff --git a/test/suite_clopts.py b/test/suite_clopts.py
index d13f96cc86..86aca99eac 100644
--- a/test/suite_clopts.py
+++ b/test/suite_clopts.py
@@ -226,7 +226,7 @@ class TestTsharkZExpert:
def test_tshark_z_expert_all(self, cmd_tshark, capture_file, test_env):
proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert',
'-o', 'tcp.check_checksum:TRUE',
- '-r', capture_file('http2-data-reassembly.pcap')), capture_output=True, env=test_env)
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
# http2-data-reassembly.pcap has Errors, Warnings, Notes, and Chats
# when TCP checksum are verified.
assert grep_output(proc.stdout, 'Errors')
@@ -237,7 +237,7 @@ class TestTsharkZExpert:
def test_tshark_z_expert_error(self, cmd_tshark, capture_file, test_env):
proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,error',
'-o', 'tcp.check_checksum:TRUE',
- '-r', capture_file('http2-data-reassembly.pcap')), capture_output=True, env=test_env)
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
assert grep_output(proc.stdout, 'Errors')
assert not grep_output(proc.stdout, 'Warns')
assert not grep_output(proc.stdout, 'Notes')
@@ -246,7 +246,7 @@ class TestTsharkZExpert:
def test_tshark_z_expert_warn(self, cmd_tshark, capture_file, test_env):
proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,warn',
'-o', 'tcp.check_checksum:TRUE',
- '-r', capture_file('http2-data-reassembly.pcap')), capture_output=True, env=test_env)
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
assert grep_output(proc.stdout, 'Errors')
assert grep_output(proc.stdout, 'Warns')
assert not grep_output(proc.stdout, 'Notes')
@@ -255,7 +255,7 @@ class TestTsharkZExpert:
def test_tshark_z_expert_note(self, cmd_tshark, capture_file, test_env):
proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,note',
'-o', 'tcp.check_checksum:TRUE',
- '-r', capture_file('http2-data-reassembly.pcap')), capture_output=True, env=test_env)
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
assert grep_output(proc.stdout, 'Errors')
assert grep_output(proc.stdout, 'Warns')
assert grep_output(proc.stdout, 'Notes')
@@ -264,7 +264,7 @@ class TestTsharkZExpert:
def test_tshark_z_expert_chat(self, cmd_tshark, capture_file, test_env):
proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,chat',
'-o', 'tcp.check_checksum:TRUE',
- '-r', capture_file('http2-data-reassembly.pcap')), capture_output=True, env=test_env)
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
assert grep_output(proc.stdout, 'Errors')
assert grep_output(proc.stdout, 'Warns')
assert grep_output(proc.stdout, 'Notes')
@@ -293,7 +293,7 @@ class TestTsharkZExpert:
def test_tshark_z_expert_filter(self, cmd_tshark, capture_file, test_env):
proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,udp',
'-o', 'tcp.check_checksum:TRUE',
- '-r', capture_file('http2-data-reassembly.pcap')), capture_output=True, env=test_env)
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
# Filtering for UDP should produce no expert infos.
assert not grep_output(proc.stdout, 'Errors')
assert not grep_output(proc.stdout, 'Warns')
@@ -301,14 +301,15 @@ class TestTsharkZExpert:
assert not grep_output(proc.stdout, 'Chats')
def test_tshark_z_expert_error_filter(self, cmd_tshark, capture_file, test_env):
- proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,warn,tls', # tls is a filter
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,note,http', # tls is a filter
'-o', 'tcp.check_checksum:TRUE',
- '-r', capture_file('http2-data-reassembly.pcap')), capture_output=True, env=test_env)
- # Filtering for TLS should produce only Error level expert infos
- # with checksumming turned on, because the lower level expert infos
- # are on the packets with TCP but not TLS.
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
+ # Filtering for HTTP and Note level expert info should produce only
+ # Error and Warning level expert infos with checksumming turned on.
+ # The Note warnings on are packets with TCP but not HTTP, and we're
+ # filtering out the Chat level.
assert grep_output(proc.stdout, 'Errors')
- assert not grep_output(proc.stdout, 'Warns')
+ assert grep_output(proc.stdout, 'Warns')
assert not grep_output(proc.stdout, 'Notes')
assert not grep_output(proc.stdout, 'Chats')