aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-cdp.c
diff options
context:
space:
mode:
authorJaap Keuter <jaap.keuter@xs4all.nl>2009-01-06 19:58:42 +0000
committerJaap Keuter <jaap.keuter@xs4all.nl>2009-01-06 19:58:42 +0000
commit767617bff4cfb159035fac6fc4870ade6d7656c1 (patch)
tree8c441062aa3685d28d6438fa02a27d49cdc35174 /epan/dissectors/packet-cdp.c
parentb16a64c80c4c43d0e4c82c8c5c8257905abc7e24 (diff)
Fix for bug 3112:
CDP checksum calculation has some deviations from the RFC 1071 Internet checksum algorithm, in particular when faced with odd length packets. svn path=/trunk/; revision=27171
Diffstat (limited to 'epan/dissectors/packet-cdp.c')
-rw-r--r--epan/dissectors/packet-cdp.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/epan/dissectors/packet-cdp.c b/epan/dissectors/packet-cdp.c
index ffb761d49d..a857b4ea04 100644
--- a/epan/dissectors/packet-cdp.c
+++ b/epan/dissectors/packet-cdp.c
@@ -191,10 +191,41 @@ dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
packet_checksum = tvb_get_ntohs(tvb, offset);
data_length = tvb_reported_length(tvb);
-
- cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, data_length);
- cksum_vec[0].len = data_length;
-
+
+ /* CDP doesn't adhere to RFC 1071 section 2. (B). It incorrectly assumes
+ * checksums are calculated on a big endian platform, therefore i.s.o.
+ * padding odd sized data with a zero byte _at the end_ it sets the last
+ * big endian _word_ to contain the last network _octet_. This byteswap
+ * has to be done on the last octet of network data before feeding it to
+ * the Internet checksum routine.
+ * CDP checksumming code has a bug in the addition of this last _word_
+ * as a signed number into the long word intermediate checksum. When
+ * reducing this long to word size checksum an off-by-one error can be
+ * made. This off-by-one error is compensated for in the last _word_ of
+ * the network data.
+ */
+ if (data_length & 1) {
+ guint8 *padded_buffer;
+ /* Allocate new buffer */
+ padded_buffer = ep_alloc(data_length+1);
+ tvb_memcpy(tvb, padded_buffer, 0, data_length);
+ /* Swap bytes in last word */
+ padded_buffer[data_length] = padded_buffer[data_length-1];
+ padded_buffer[data_length-1] = 0;
+ /* Compensate off-by-one error */
+ if (padded_buffer[data_length] & 0x80) {
+ padded_buffer[data_length]--;
+ padded_buffer[data_length-1]--;
+ }
+ /* Setup checksum routine data buffer */
+ cksum_vec[0].ptr = padded_buffer;
+ cksum_vec[0].len = data_length+1;
+ } else {
+ /* Setup checksum routine data buffer */
+ cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, data_length);
+ cksum_vec[0].len = data_length;
+ }
+
computed_checksum = in_cksum(cksum_vec, 1);
checksum_good = (computed_checksum == 0);
checksum_bad = !checksum_good;