The way tcpdump displays bad checksums is annoyingly inconsistent.

Here's an example where tcpdump is showing two packets: one with a
bad IP checksum, and another with a bad TCP checksum (emphasis mine):

Bad IP checksum
14:38:42.489639 192.168.30.1.20 > 192.168.30.70.80: S [tcp sum ok]
0:0(0) win 8192 (ttl 64, id 1, len 40, bad cksum abcd! differs by 116a)
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Bad TCP checksum
14:38:44.900411 192.168.30.1.20 > 192.168.30.70.80: S [bad tcp cksum 1926!]
0:0(0) win 8192 (ttl 64, id 1, len 40)                 ^^^^^^^^^^^^^^^^^^^

Here are the inconsistencies:

1. "differs by" is shown for the bad IP checksum but not for the bad TCP
   checksum.

2. The "bad cksum" value 0xabcd in the first packet is the bad IP checksum
   within the IP header itself (my Scapy-crafted packet intentionally used
   a bad IP checksum of 0xabcd); based on this, one would think that the
   "bad tcp cksum" of 0x1926 in the second packet is the bad TCP checksum
   within the TCP header too. But it's not!

   Despite being called "bad tcp cksum", 0x1926 is actually the
   difference between the bad checksum in the TCP header and what the
   good checksum should be (in other words, it's the "differs by" value).

3. The actual bad TCP checksum in the TCP header is not shown at all.

To fix these inconsistencies, the following diff forces tcpdump to
always show the actual bad checksum within the header.  As a bonus, it
also shows what the correct checksum should be (concept and code
borrowed from mainline tcpdump).

For example, here's what the fixed tcpdump shows for a variety of bad
checksums (all of these packets use a crafted bad checksum of 0xabcd):

Bad IP checksum
14:38:42.489639 192.168.30.1.20 > 192.168.30.70.80: S [tcp sum ok]
0:0(0) win 8192 (ttl 64, id 1, len 40, bad ip cksum abcd! -> bd37)

Bad TCP checksum (over IPv4)
14:38:44.900411 192.168.30.1.20 > 192.168.30.70.80: S
[bad tcp cksum abcd! -> d1e6] 0:0(0) win 8192 (ttl 64, id 1, len 40)

Bad UDP checksum (over IPv4)
14:38:47.286166 192.168.30.1.53 > 192.168.30.70.53:
[bad udp cksum abcd! -> 41dc] 0 [0q] (0) (ttl 64, id 1, len 28)

Bad ICMP checksum
14:38:49.686247 192.168.30.1 > 192.168.30.70: icmp: echo request (id:0000
seq:0) [bad icmp cksum abcd! -> f7ff] (ttl 64, id 1, len 28)

Bad TCP checksum (over IPv6)
14:38:52.127616 2001:470:xxxx:1::1.20 > 2001:470:xxxx:1::70.80: S
[bad tcp cksum abcd! -> 7ecc] 0:0(0) win 8192 (len 20, hlim 64)

Bad UDP checksum (over IPv6)
14:38:54.531458 2001:470:xxxx:1::1.53 > 2001:470:xxxx:1::70.53:
[bad udp cksum abcd! -> eec1][|domain] (len 8, hlim 64)

Bad ICMPv6 checksum
14:38:56.937431 2001:470:xxxx:1::1 > 2001:470:xxxx:1::70: icmp6: echo request
(id:0000 seq:0) [bad icmp6 cksum abcd! -> 6f0a] (len 8, hlim 64)

Tested on amd64, i386, and macppc.

ok?


Index: Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/Makefile,v
retrieving revision 1.54
diff -u -p -r1.54 Makefile
--- Makefile    19 Jun 2013 03:51:30 -0000      1.54
+++ Makefile    16 Jun 2014 04:22:35 -0000
@@ -50,7 +50,7 @@ SRCS= tcpdump.c addrtoname.c privsep.c p
        print-pfsync.c pf_print_state.c \
        print-udpencap.c print-carp.c \
        print-802_11.c print-iapp.c print-mpls.c print-slow.c \
-       gmt2local.c savestr.c setsignal.c
+       gmt2local.c savestr.c setsignal.c in_cksum.c
 
 # TCP OS Fingerprinting
 .PATH: ${.CURDIR}/../../sys/net
Index: in_cksum.c
===================================================================
RCS file: in_cksum.c
diff -N in_cksum.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ in_cksum.c  16 Jun 2014 04:22:35 -0000
@@ -0,0 +1,88 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 1988, 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)in_cksum.c  8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/types.h>
+
+#include "interface.h"
+
+/*
+ * Given the host-byte-order value of the checksum field in a packet
+ * 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.
+ */
+u_int16_t
+in_cksum_shouldbe(u_int16_t sum, u_int16_t computed_sum)
+{
+       u_int32_t 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.
+        *
+        * "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.
+        */
+       shouldbe = sum;
+       shouldbe += ntohs(computed_sum);
+       shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
+       shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
+       return shouldbe;
+}
Index: interface.h
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/interface.h,v
retrieving revision 1.62
diff -u -p -r1.62 interface.h
--- interface.h 11 Jan 2014 04:35:52 -0000      1.62
+++ interface.h 16 Jun 2014 04:22:35 -0000
@@ -289,3 +289,4 @@ extern void dhcp6_print(const u_char *, 
 #endif /*INET6*/
 
 extern u_short in_cksum(const u_short *addr, register int len, int csum);
+extern u_int16_t in_cksum_shouldbe(u_int16_t, u_int16_t);
Index: print-icmp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-icmp.c,v
retrieving revision 1.22
diff -u -p -r1.22 print-icmp.c
--- print-icmp.c        11 Jan 2014 04:40:45 -0000      1.22
+++ print-icmp.c        16 Jun 2014 04:22:35 -0000
@@ -375,15 +375,18 @@ icmp_print(const u_char *bp, u_int lengt
        }
        (void)printf("icmp: %s", str);
        if (vflag) {
-               u_int16_t sum;
                if (TTEST2(dp->icmp_type, length)) {
+                       u_int16_t sum, icmp_sum;
                        sum = in_cksum((const u_short *)dp, length, 0);
-                       if (sum != 0)
-                               (void)printf(" [bad icmp cksum %x!]", sum);
+                       if (sum != 0) {
+                               icmp_sum = EXTRACT_16BITS(&dp->icmp_cksum);
+                               (void)printf(" [bad icmp cksum %x! -> %x]", 
icmp_sum,
+                                   in_cksum_shouldbe(icmp_sum, sum));
+                       }
                        else
                                (void)printf(" [icmp cksum ok]");
                }
-    }
+       }
        if (vflag > 1 && !ICMP_INFOTYPE(dp->icmp_type) &&
            TTEST(dp->icmp_ip)) {
                (void)printf(" for ");
Index: print-icmp6.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-icmp6.c,v
retrieving revision 1.12
diff -u -p -r1.12 print-icmp6.c
--- print-icmp6.c       11 Jan 2014 04:41:08 -0000      1.12
+++ print-icmp6.c       16 Jun 2014 04:22:35 -0000
@@ -53,6 +53,7 @@
 
 #include "interface.h"
 #include "addrtoname.h"
+#include "extract.h"
 
 void icmp6_opt_print(const u_char *, int);
 void mld6_print(const u_char *);
@@ -485,12 +486,14 @@ icmp6_print(const u_char *bp, u_int leng
                break;
        }
        if (vflag) {
-               u_int16_t sum;
                if (TTEST2(dp->icmp6_type, length)) {
+                       u_int16_t sum, icmp6_sum;
                        sum = icmp6_cksum(ip, dp, length);
-                       if (sum != 0)
-                               printf(" [bad icmp6 cksum %x!]", sum);
-                       else
+                       if (sum != 0) {
+                               icmp6_sum = EXTRACT_16BITS(&dp->icmp6_cksum);
+                               printf(" [bad icmp6 cksum %x! -> %x]", 
icmp6_sum,
+                                   in_cksum_shouldbe(icmp6_sum, sum));
+                       } else
                                printf(" [icmp6 cksum ok]");
                }
        }
Index: print-ip.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-ip.c,v
retrieving revision 1.37
diff -u -p -r1.37 print-ip.c
--- print-ip.c  11 Jan 2014 04:35:52 -0000      1.37
+++ print-ip.c  16 Jun 2014 04:22:35 -0000
@@ -627,7 +627,6 @@ ip_print(register const u_char *bp, regi
                (void)printf(" [ttl %d]", (int)ip->ip_ttl);
 
        if (vflag) {
-               int sum;
                char *sep = "";
 
                printf(" (");
@@ -642,12 +641,12 @@ ip_print(register const u_char *bp, regi
                (void)printf("%slen %u", sep, ntohs(ip->ip_len));
                sep = ", ";
                if ((u_char *)ip + hlen <= snapend) {
+                       u_int16_t sum, ip_sum;
                        sum = in_cksum((const u_short *)ip, hlen, 0);
                        if (sum != 0) {
-                               (void)printf("%sbad cksum %x!", sep,
-                                            ntohs(ip->ip_sum));
-                               if (vflag > 1)
-                                       (void)printf(" differs by %x", 
htons(sum));
+                               ip_sum = EXTRACT_16BITS(&ip->ip_sum);
+                               (void)printf("%sbad ip cksum %x! -> %x", sep, 
ip_sum,
+                                            in_cksum_shouldbe(ip_sum, sum));
                                sep = ", ";
                        }
                }
Index: print-tcp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-tcp.c,v
retrieving revision 1.29
diff -u -p -r1.29 print-tcp.c
--- print-tcp.c 5 Feb 2014 21:12:19 -0000       1.29
+++ print-tcp.c 16 Jun 2014 04:22:35 -0000
@@ -419,23 +419,27 @@ tcp_print(register const u_char *bp, reg
        }
 
        if (ip && ip->ip_v == 4 && vflag) {
-               int sum;
                if (TTEST2(tp->th_sport, length)) {
+                       u_int16_t sum, tcp_sum;
                        sum = tcp_cksum(ip, tp, length);
-                       if (sum != 0)
-                               (void)printf(" [bad tcp cksum %x!]", sum);
-                       else
+                       if (sum != 0) {
+                               tcp_sum = EXTRACT_16BITS(&tp->th_sum);
+                               (void)printf(" [bad tcp cksum %x! -> %x]", 
tcp_sum,
+                                   in_cksum_shouldbe(tcp_sum, sum));
+                       } else
                                (void)printf(" [tcp sum ok]");
                }
        }
 #ifdef INET6
        if (ip6 && ip6->ip6_plen && vflag) {
-               int sum;
                if (TTEST2(tp->th_sport, length)) {
+                       u_int16_t sum, tcp_sum;
                        sum = tcp6_cksum(ip6, tp, length);
-                       if (sum != 0)
-                               (void)printf(" [bad tcp cksum %x!]", sum);
-                       else
+                       if (sum != 0) {
+                               tcp_sum = EXTRACT_16BITS(&tp->th_sum);
+                               (void)printf(" [bad tcp cksum %x! -> %x]", 
tcp_sum,
+                                   in_cksum_shouldbe(tcp_sum, sum));
+                       } else
                                (void)printf(" [tcp sum ok]");
                }
        }
Index: print-udp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-udp.c,v
retrieving revision 1.35
diff -u -p -r1.35 print-udp.c
--- print-udp.c 26 Jan 2014 18:03:27 -0000      1.35
+++ print-udp.c 16 Jun 2014 04:22:35 -0000
@@ -57,6 +57,7 @@
 
 #include "interface.h"
 #include "addrtoname.h"
+#include "extract.h"
 #include "appletalk.h"
 
 #include "nfsv2.h"
@@ -566,27 +567,29 @@ udp_print(register const u_char *bp, u_i
 #endif
 
        if (ip->ip_v == 4 && vflag) {
-               int sum = up->uh_sum;
-               if (sum == 0) {
-                       (void)printf(" [no cksum]");
+               u_int16_t sum, udp_sum = EXTRACT_16BITS(&up->uh_sum);
+               if (udp_sum == 0) {
+                       (void)printf(" [no udp cksum]");
                } else if (TTEST2(cp[0], length)) {
                        sum = udp_cksum(ip, up, length + sizeof(struct udphdr));
                        if (sum != 0)
-                               (void)printf(" [bad udp cksum %x!]", sum);
+                               (void)printf(" [bad udp cksum %x! -> %x]", 
udp_sum,
+                                   in_cksum_shouldbe(udp_sum, sum));
                        else
                                (void)printf(" [udp sum ok]");
                }
        }
 #ifdef INET6
        if (ip->ip_v == 6 && ip6->ip6_plen && vflag) {
-               int sum = up->uh_sum;
+               u_int16_t sum, udp_sum = EXTRACT_16BITS(&up->uh_sum);
                /* for IPv6, UDP checksum is mandatory */
-               if (sum == 0) {
-                       (void)printf(" [invalid cksum 0]");
+               if (udp_sum == 0) {
+                       (void)printf(" [invalid udp cksum 0]");
                } else if (TTEST2(cp[0], length)) {
                        sum = udp6_cksum(ip6, up, length + sizeof(struct 
udphdr));
                        if (sum != 0)
-                               (void)printf(" [bad udp cksum %x!]", sum);
+                               (void)printf(" [bad udp cksum %x! -> %x]", 
udp_sum,
+                                   in_cksum_shouldbe(udp_sum, sum));
                        else
                                (void)printf(" [udp sum ok]");
                }

Reply via email to