Ethereal-dev: Re: [ethereal-dev] Ethereal and UDP checksums..

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Guy Harris <gharris@xxxxxxxxxxxx>
Date: Wed, 10 May 2000 22:22:16 -0700
On Wed, May 10, 2000 at 02:21:38PM +0300, Markus Stenberg wrote:
> Apparently Ethereal did not check UDP checksums.. so I wrote quickie hack
> to do it.
> 
> There are some problems with the hack, mainly, the 12-byte global character
> array used for transferring UDP pseudoheader data from IP to UDP handler,
> as there doesn't seem any other way to do it (elegantly).

Well, here's a patch with TCP checksumming stuff I did a couple of weeks
ago (when we were having a problem at Network Appliance with a software
release under development - not yet released - that looked as if it
might be a checksumming problem, and did, in fact, turn out to be a
checksum bug), to which I've added UDP checksumming support.

It generates the pseudoheaders from the IP (v4 or v6) addresses handed
in via the "packet_info" structure and the IP protocol type value for
the protocol in question (TCP or UDP), and has a checksumming routine
that's the BSD checksum routine modified to take a scatter/gather list
of pointer/length values rather than an mbuf chain.

It doesn't checksum packets where we didn't capture all the data;
unfortunately, it *does* checksum fragmented IP datagrams, which it
shouldn't do (the IP layer needs to supply a "fragmented" indication to
TCP and UDP to do that) - in the long term, I'd like to be able to have
Ethereal reassemble fragmented IP datagrams, so that, for example, an
NFS READDIR or READDIRPLUS reply that fits in more than one link-layer
packet can be dissected in its entirety, rather than just having the
first fragment dissected (a capability I could've used several years
ago; that was one of the things that first got me interested in packet
analyzers), in which case, if we have all of the fragments, we *could*
checksum the datagram, although IP would still need to tell TCP and UDP
whether it found all of the fragments.

There's also a problem with the version of the BSD copyright/license in
that code - I have the impression that the "give credit" clause:

 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and its contributors.

conflicts with clause 6 of the GPL:

    6. Each time you redistribute the Program (or any work based on the
  Program), the recipient automatically receives a license from the
  original licensor to copy, distribute or modify the Program subject to
  these terms and conditions.  You may not impose any further
  restrictions on the recipients' exercise of the rights granted herein.
  You are not responsible for enforcing compliance by third parties to
  this License.

as it "imposes [a] further restriction" over and above "these terms and
conditions" (i.e., over and above what's in the GPL).

I don't know if the removal of that clause, which I think the folks at
UCB removed recently, retroactively affects code with the "give credit"
clause in it, nor do I know if there are any versions of the BSD
checksum code without that clause.

I didn't find any portable C checksumming code in the Linux kernel when
I last checked, although I may have missed it; I don't want to have only
assembler-language code, as I don't want to oblige Ethereal users to

	1) be running an GNU as-compatible assembler (and don't want to
	   have to cook up a way to arrange to pick the "right"
	   assembler-language code for the assembler the platform has)

or

	2) be running a processor currently supported by Linux.

It may be that a simple checksum routine is Fast Enough.

I've attached the patch, as well as the "in_cksum.h" and "in_cksum.c"
files for the checksumming code.
? in_cksum.h
? in_cksum.c
? errs
Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/ethereal/Makefile.am,v
retrieving revision 1.193
diff -c -r1.193 Makefile.am
*** Makefile.am	2000/05/05 09:32:00	1.193
--- Makefile.am	2000/05/11 04:45:12
***************
*** 248,253 ****
--- 248,255 ----
  	etypes.h       \
  	follow.c       \
  	follow.h       \
+ 	in_cksum.c     \
+ 	in_cksum.h     \
  	inet_v6defs.h  \
  	ipproto.c      \
  	ipv4.c         \
Index: packet-ip.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/packet-ip.c,v
retrieving revision 1.84
diff -c -r1.84 packet-ip.c
*** packet-ip.c	2000/05/10 21:36:55	1.84
--- packet-ip.c	2000/05/11 04:45:16
***************
*** 40,45 ****
--- 40,46 ----
  #include <glib.h>
  #include "packet.h"
  #include "resolv.h"
+ #include "in_cksum.h"
  
  #ifdef NEED_SNPRINTF_H
  # ifdef HAVE_STDARG_H
***************
*** 805,825 ****
  
  static char *ip_checksum_state(e_ip *iph)
  {
!     unsigned long Sum;
!     unsigned char *Ptr, *PtrEnd;
!     unsigned short word;
! 
!     Sum    = 0;
!     PtrEnd = (lo_nibble(iph->ip_v_hl) * 4 + (char *)iph);
!     for (Ptr = (unsigned char *) iph; Ptr < PtrEnd; Ptr += 2) {
! 	memcpy(&word, Ptr, sizeof word);
!         Sum += word;
!     }
! 
!     Sum = (Sum & 0xFFFF) + (Sum >> 16);
!     Sum = (Sum & 0xFFFF) + (Sum >> 16);
  
!     if (Sum != 0xffff)
          return "incorrect";
  
      return "correct";
--- 806,816 ----
  
  static char *ip_checksum_state(e_ip *iph)
  {
!     vec_t cksum_vec[1];
  
!     cksum_vec[0].ptr = (const guint8 *)iph;
!     cksum_vec[0].len = lo_nibble(iph->ip_v_hl) * 4;
!     if (in_cksum(&cksum_vec[0], 1) != 0)
          return "incorrect";
  
      return "correct";
Index: packet-tcp.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/packet-tcp.c,v
retrieving revision 1.73
diff -c -r1.73 packet-tcp.c
*** packet-tcp.c	2000/05/05 09:32:05	1.73
--- packet-tcp.c	2000/05/11 04:45:18
***************
*** 40,45 ****
--- 40,46 ----
  #include "globals.h"
  #include "resolv.h"
  #include "follow.h"
+ #include "in_cksum.h"
  
  #ifdef NEED_SNPRINTF_H
  # ifdef HAVE_STDARG_H
***************
*** 422,427 ****
--- 423,430 ----
    guint      hlen;
    guint      optlen;
    guint      packet_max = pi.len;
+   vec_t      cksum_vec[4];
+   guint32    phdr[2];
  
    /* To do: Check for {cap len,pkt len} < struct len */
    /* Avoids alignment problems on many architectures. */
***************
*** 497,503 ****
      proto_tree_add_item(field_tree, hf_tcp_flags_syn, offset + 13, 1, th.th_flags);
      proto_tree_add_item(field_tree, hf_tcp_flags_fin, offset + 13, 1, th.th_flags);
      proto_tree_add_item(tcp_tree, hf_tcp_window_size, offset + 14, 2, th.th_win);
!     proto_tree_add_item(tcp_tree, hf_tcp_checksum, offset + 16, 2, th.th_sum);
      if (th.th_flags & TH_URG)
        proto_tree_add_item(tcp_tree, hf_tcp_urgent_pointer, offset + 18, 2, th.th_urp);
    }
--- 500,543 ----
      proto_tree_add_item(field_tree, hf_tcp_flags_syn, offset + 13, 1, th.th_flags);
      proto_tree_add_item(field_tree, hf_tcp_flags_fin, offset + 13, 1, th.th_flags);
      proto_tree_add_item(tcp_tree, hf_tcp_window_size, offset + 14, 2, th.th_win);
!     if (pi.captured_len >= pi.len) {
!       /* The packet isn't truncated, so we can checksum it.
!          XXX - we have to check whether this is part of a fragmented
! 	 IP datagram, too.... */
! 
!       /* Set up the fields of the pseudo-header. */
!       cksum_vec[0].ptr = pi.src.data;
!       cksum_vec[0].len = pi.src.len;
!       cksum_vec[1].ptr = pi.dst.data;
!       cksum_vec[1].len = pi.dst.len;
!       cksum_vec[2].ptr = (const guint8 *)&phdr;
!       switch (pi.src.type) {
! 
!       case AT_IPv4:
! 	phdr[0] = htonl((IP_PROTO_TCP<<16) + END_OF_FRAME);
! 	cksum_vec[2].len = 4;
! 	break;
! 
!       case AT_IPv6:
!         phdr[0] = htonl(END_OF_FRAME);
!         phdr[1] = htonl(IP_PROTO_TCP);
!         cksum_vec[2].len = 8;
!         break;
! 
!       default:
!         /* TCP runs only atop IPv4 and IPv6.... */
!         g_assert_not_reached();
!         break;
!       }
!       cksum_vec[3].ptr = &pd[offset];
!       cksum_vec[3].len = END_OF_FRAME;
!       proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, offset + 16, 2,
! 	th.th_sum, "Checksum: 0x%04x (%s)", th.th_sum,
! 	(in_cksum(&cksum_vec[0], 4) == 0) ? "correct" : "incorrect");
!     } else {
!       proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, offset + 16, 2,
! 	th.th_sum, "Checksum: 0x%04x", th.th_sum);
!     }
      if (th.th_flags & TH_URG)
        proto_tree_add_item(tcp_tree, hf_tcp_urgent_pointer, offset + 18, 2, th.th_urp);
    }
Index: packet-udp.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/packet-udp.c,v
retrieving revision 1.69
diff -c -r1.69 packet-udp.c
*** packet-udp.c	2000/04/20 07:05:57	1.69
--- packet-udp.c	2000/05/11 04:45:19
***************
*** 43,48 ****
--- 43,49 ----
  #include <glib.h>
  #include "globals.h"
  #include "resolv.h"
+ #include "in_cksum.h"
  
  #include "plugins.h"
  #include "packet-udp.h"
***************
*** 146,151 ****
--- 147,154 ----
    guint16    uh_sport, uh_dport, uh_ulen, uh_sum;
    proto_tree *udp_tree;
    proto_item *ti;
+   vec_t      cksum_vec[4];
+   guint32    phdr[2];
  
    if (!BYTES_ARE_IN_FRAME(offset, sizeof(e_udphdr))) {
      dissect_data(pd, offset, fd, tree);
***************
*** 178,185 ****
      proto_tree_add_item_hidden(udp_tree, hf_udp_port, offset+2, 2, uh_dport);
  
      proto_tree_add_item(udp_tree, hf_udp_length, offset + 4, 2,  uh_ulen);
!     proto_tree_add_uint_format(udp_tree, hf_udp_checksum, offset + 6, 2, uh_sum,
! 	"Checksum: 0x%04x", uh_sum);
    }
  
    /* Skip over header */
--- 181,224 ----
      proto_tree_add_item_hidden(udp_tree, hf_udp_port, offset+2, 2, uh_dport);
  
      proto_tree_add_item(udp_tree, hf_udp_length, offset + 4, 2,  uh_ulen);
!     if (pi.captured_len >= pi.len) {
!       /* The packet isn't truncated, so we can checksum it.
!          XXX - we have to check whether this is part of a fragmented
! 	 IP datagram, too.... */
! 
!       /* Set up the fields of the pseudo-header. */
!       cksum_vec[0].ptr = pi.src.data;
!       cksum_vec[0].len = pi.src.len;
!       cksum_vec[1].ptr = pi.dst.data;
!       cksum_vec[1].len = pi.dst.len;
!       cksum_vec[2].ptr = (const guint8 *)&phdr;
!       switch (pi.src.type) {
! 
!       case AT_IPv4:
! 	phdr[0] = htonl((IP_PROTO_UDP<<16) + END_OF_FRAME);
! 	cksum_vec[2].len = 4;
! 	break;
! 
!       case AT_IPv6:
!         phdr[0] = htonl(END_OF_FRAME);
!         phdr[1] = htonl(IP_PROTO_UDP);
!         cksum_vec[2].len = 8;
!         break;
! 
!       default:
!         /* TCP runs only atop IPv4 and IPv6.... */
!         g_assert_not_reached();
!         break;
!       }
!       cksum_vec[3].ptr = &pd[offset];
!       cksum_vec[3].len = END_OF_FRAME;
!       proto_tree_add_uint_format(udp_tree, hf_udp_checksum, offset + 6, 2,
!         uh_sum,	"Checksum: 0x%04x (%s)", uh_sum,
! 	(in_cksum(&cksum_vec[0], 4) == 0) ? "correct" : "incorrect");
!     } else {
!       proto_tree_add_uint_format(udp_tree, hf_udp_checksum, offset + 6, 2,
!         uh_sum,	"Checksum: 0x%04x", uh_sum);
!     }
    }
  
    /* Skip over header */
typedef struct {
	const guint8 *ptr;
	int	len;
} vec_t;

extern int in_cksum(const vec_t *vec, int veclen);

/*
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. 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
 * $FreeBSD: src/sys/netinet/in_cksum.c,v 1.5.4.1 1999/08/29 16:29:34 peter Exp $
 */

#include <glib.h>

#include "in_cksum.h"

/*
 * 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.
 */

#define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}

int
in_cksum(const vec_t *vec, int veclen)
{
	register const guint16 *w;
	register int sum = 0;
	register int mlen = 0;
	int byte_swapped = 0;

	union {
		char	c[2];
		guint16	s;
	} s_util;
	union {
		guint16 s[2];
		long	l;
	} l_util;

	for (; veclen != 0; vec++, veclen--) {
		if (vec->len == 0)
			continue;
		w = (const guint16 *)vec->ptr;
		if (mlen == -1) {
			/*
			 * The first byte of this chunk is the continuation
			 * of a word spanning between this chunk and the
			 * last chunk.
			 *
			 * s_util.c[0] is already saved when scanning previous
			 * chunk.
			 */
			s_util.c[1] = *(char *)w;
			sum += s_util.s;
			w = (const guint16 *)((const guint8 *)w + 1);
			mlen = vec->len - 1;
		} else
			mlen = vec->len;
		/*
		 * Force to even boundary.
		 */
		if ((1 & (int) w) && (mlen > 0)) {
			REDUCE;
			sum <<= 8;
			s_util.c[0] = *(const guint8 *)w;
			w = (const guint16 *)((const guint8 *)w + 1);
			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) {
				s_util.c[1] = *(char *)w;
				sum += s_util.s;
				mlen = 0;
			} else
				mlen = -1;
		} else if (mlen == -1)
			s_util.c[0] = *(char *)w;
	}
	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);
}