On Sat, Jul 20, 2002 at 11:46:02PM -0400, Michael Richardson wrote:
> When constructing packets that are broken in various ways (usually with a hex
> editor), I then run them through tcpdump to see if I got the checksum right.
> Rather than guess, I wanted to have tcpdump just print the right answer.
> 
> Is this the right formula:
> 
>                       sum = in_cksum((const u_short *)ip, hlen, 0);
>                       if (sum != 0) {
>                         unsigned int oldsum;
>                         oldsum = ip->ip_sum;
>                         (void)printf("%sbad cksum %x->%04x!", sep,
>                                      ntohs(oldsum),
>                                      ntohs(oldsum - ~(sum - 1)));

Well, here's the routine in Ethereal that computes what an Internet
checksum should have been, based on the value of the checksum field and
the checksum computed over the data (data including the checksum field
itself):

/*
 * Given the host-byte-order value of the checksum field in a packet
 * header, and the one's complement negation of the host-byte-order
 * checksum of the packet, compute what the checksum field *should*
 * have been.
 */
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.
         *
         * "computed_sum" is a host-byte-order value, so we must put
         * it in network byte order before subtracting it from the
         * network-byte-order value from the header; the adjusted
         * checksum will be in network byte order, which is what
         * we'll return.
         */
        shouldbe = sum;
        shouldbe += htons(computed_sum);
        shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
        shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
        return shouldbe;
}

In, for example, the IP dissector in Ethereal, "computed_sum" is
computed by the BSD "in_cksum()".

(Ethereal, as a whole, is GPLed, but the file containing that routine
includes the BSD "in_cksum()", and is covered by the
no-advertising-clause BSD license.  Besides, I wrote
"in_cksum_shouldbe()", so I guess that lets me say whether it can be
used in non-GPLed software; if so, I say "yes". :-))

"sum" in that routine corresponds to "ntohs(oldsum)" in your code, and
"computed_sum" in that routine corresponds to "sum".

So the Ethereal code takes "ntohs(oldsum)", adds to it "htons(sum)" in
one's complement (that's what the

        shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
        shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);

does, adding in the end-around carry, and then adding in any end-around
carry from that previous addition), and returns the result.

(Now that I look at it, and think about it, I'm not sure "computed_sum"
is in host byte order, especially given that the "sum" argument to
"in_cksum_shouldbe()" is in host byte order, and that it's checksumming
the raw data from the packet; I'll have to look at that and, if so,
update some comments in that code and replace "htons()" with "ntohs()". 
Of course, that doesn't make any difference to the code, as "ntohs()"
and "htons()" do the same thing....)

I threw in a hack to both Ethereal's and tcpdump's IP dissector to set
the IP ID of the packet to 0xF0F0, and checked the values that they
reported as the "should be" value for the checksum, and tested that on
one IP frame I had; the tcpdump value, both with the current
"print-ip.c" and with your change, was different from the Ethereal
value.  (The current "print-ip.c" was quite bogus; your version was
closer, but still off by 0x100, suggesting that the "- 1" shouldn't be
there.)

I then further hacked Ethereal's IP dissector to put the "should be" value
in as the checksum, and it reported that the checksum was correct, so
I'm assuming that means "in_cksum_shouldbe()" is correct.

A tcpdump equivalent to the stuff "in_cksum_shouldbe()" does is

        if (vflag) {
                u_int16_t computed_sum, sum;
                u_int32_t shouldbe;
                char *sep = "";

                if ((u_char *)ip + hlen <= snapend) {
                        computed_sum = in_cksum((const u_short *)ip, hlen, 0);
                        if (computed_sum != 0) {
                                sum = ntohs(ip->ip_sum);
                                shouldbe = sum;
                                shouldbe += htons(computed_sum);
                                shouldbe =
                                    (shouldbe & 0xFFFF) + (shouldbe >> 16);
                                shouldbe =
                                    (shouldbe & 0xFFFF) + (shouldbe >> 16);
                                (void)printf("%sbad cksum %x (->%x)!", sep,
                                             sum, shouldbe);
                                sep = ", ";
                        }
                }

I tried that, and it gave the same answer as Ethereal; I then stuffed
the "should be" checksum into the packet, as I'd done on Ethereal, and
it said the checksum was correct.

(All this was done on a PC, so "htons()" and "ntohs()" weren't no-ops.)
-
This is the TCPDUMP workers list. It is archived at
http://www.tcpdump.org/lists/workers/index.html
To unsubscribe use mailto:[EMAIL PROTECTED]?body=unsubscribe

Reply via email to