Ethereal-dev: Re: [ethereal-dev] tcp header checksum validation
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: Fri, 9 Jun 2000 21:51:20 -0700
On Fri, Jun 09, 2000 at 04:03:36PM -0700, Skip Hansen wrote: > I recently added code to validate the TCP header checksums Presumably you mean "TCP checksums"; the checksum includes the TCP payload as well as the header. > The attached patch adds TCP header checksum validation. The text " (correct)" > is appended to the TCP header checksum field if the checksum is correct, > otherwise the text " (incorrect, should be 0x????) is appended. If the network > address is not type IPv4 then nothing is appended. > > The changes have been tested under FreeBSD version 3.2 with an x86 processor. I > believe the code should also function correctly on a big endian machine, ...except that most big-endian machines require strict alignment of integral data types bigger than one byte, so it's probably more likely to fail there - although it'll probably fail on Alpha-based systems as well, even though most OSes run Alphas in little-endian mode. There is no guarantee that "&pd[offset]", pointing to the first byte of the TCP header, is aligned on a 2-byte boundary, but "tcp_checksum_state()" casts it to an "unsigned short *" and dereferences it - on processors that require strict alignment, this will probably cause a trap unless the compiler generates "conservative" (and more expensive) code (some compilers have, I think, options to do that; I don't know whether GCC does) that doesn't assume pointers are properly aligned (or doesn't assume it if it sees that the pointer comes from a cast, if its dataflow analysis is sophisticated enough to detect that, or if it just punts entirely if the function contains such a cast). The trap might be handled by the OS, which might do the fetch for you, but the performance penalty for that might be severe. x86 processors allow unaligned references (with some performance penalty on many processors), but Alpha, SPARC, MIPS, and, I think, PA-RISC and some POWER-family (POWER, POWER2, PowerPC) processors do not allow them. I've done a similar patch, that checksums the header+body of both TCP segments and UDP datagrams; however: 1) it doesn't check whether the IP datagram containing the TCP segment or UDP datagram is fragmented - currently, if it is, the checksum couldn't be computed, as we currently don't reassemble fragmented IP datagrams, so we'd have to punt on those; 2) it uses the BSD kernel C-language checksum routine, which (at least in the current version of FreeBSD) still has the old BSD copyright notice, including 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. which apparently conflicts with this clause 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 ... further restrictions on the recipients' exercise of the rights granted herein", so adding "BSD Classic" code to a GPLed program is apparently not permitted by the GPL. (I think it might be possible to code with the new BSD license - Berkeley have, apparently, removed the "give credit" clause from their copyright notice, and in some fashion applied that change to code such as 4.4-Lite that they've already published, although I don't know how one goes about effacing that clause from 4.4-Lite or 4.4-Lite-derived code.) My patch also handles IPv6 as well as IPv4, and the BSD checksum code handles unaligned data; I've attached it, along with a modified version of the BSD checksum code, and a header that declares the "in_cksum()" that it exports. (The patch is one attachment; the BSD-licensed routines are another, so they're not being aggregated, so the GPL clause doesn't apply; put *that* in your pipe and smoke it, GPL lawyers. :-) I guess that if they're lawyers, though, they could find some way to liken it to supplying, separately, the two components of a binary nerve gas....) (It's a patch against the current version in the CVS tree; see http://ethereal.zing.org/development.html#anoncvs for instructions on how to access that tree via anonymous CVS. It should also apply to one of the nightly source snapshots; see http://ethereal.zing.org/distribution/nightly-builds/ for those if you are, for example, stuck behind a firewall that doesn't let you do anonymous CVS.)
Index: Makefile.am =================================================================== RCS file: /usr/local/cvsroot/ethereal/Makefile.am,v retrieving revision 1.203 diff -c -r1.203 Makefile.am *** Makefile.am 2000/06/07 22:57:43 1.203 --- Makefile.am 2000/06/10 04:45:42 *************** *** 255,260 **** --- 255,262 ---- exceptions.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.92 diff -c -r1.92 packet-ip.c *** packet-ip.c 2000/06/05 03:21:01 1.92 --- packet-ip.c 2000/06/10 04:45:46 *************** *** 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 *************** *** 777,797 **** 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"; --- 778,788 ---- 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.75 diff -c -r1.75 packet-tcp.c *** packet-tcp.c 2000/05/31 05:07:49 1.75 --- packet-tcp.c 2000/06/10 04:45:48 *************** *** 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_boolean(field_tree, hf_tcp_flags_syn, NullTVB, offset + 13, 1, th.th_flags); proto_tree_add_boolean(field_tree, hf_tcp_flags_fin, NullTVB, offset + 13, 1, th.th_flags); proto_tree_add_uint(tcp_tree, hf_tcp_window_size, NullTVB, offset + 14, 2, th.th_win); ! proto_tree_add_uint(tcp_tree, hf_tcp_checksum, NullTVB, offset + 16, 2, th.th_sum); if (th.th_flags & TH_URG) proto_tree_add_uint(tcp_tree, hf_tcp_urgent_pointer, NullTVB, offset + 18, 2, th.th_urp); } --- 500,543 ---- proto_tree_add_boolean(field_tree, hf_tcp_flags_syn, NullTVB, offset + 13, 1, th.th_flags); proto_tree_add_boolean(field_tree, hf_tcp_flags_fin, NullTVB, offset + 13, 1, th.th_flags); proto_tree_add_uint(tcp_tree, hf_tcp_window_size, NullTVB, 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, NullTVB, ! 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, NullTVB, ! offset + 16, 2, th.th_sum, "Checksum: 0x%04x", th.th_sum); ! } if (th.th_flags & TH_URG) proto_tree_add_uint(tcp_tree, hf_tcp_urgent_pointer, NullTVB, offset + 18, 2, th.th_urp); } Index: packet-udp.c =================================================================== RCS file: /usr/local/cvsroot/ethereal/packet-udp.c,v retrieving revision 1.72 diff -c -r1.72 packet-udp.c *** packet-udp.c 2000/05/31 05:07:52 1.72 --- packet-udp.c 2000/06/10 04:45:49 *************** *** 43,48 **** --- 43,49 ---- #include <glib.h> #include "globals.h" #include "resolv.h" + #include "in_cksum.h" #include "plugins.h" #include "packet-udp.h" *************** *** 145,150 **** --- 146,153 ---- 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); *************** *** 177,184 **** proto_tree_add_uint_hidden(udp_tree, hf_udp_port, NullTVB, offset+2, 2, uh_dport); proto_tree_add_uint(udp_tree, hf_udp_length, NullTVB, offset + 4, 2, uh_ulen); ! proto_tree_add_uint_format(udp_tree, hf_udp_checksum, NullTVB, offset + 6, 2, uh_sum, ! "Checksum: 0x%04x", uh_sum); } /* Skip over header */ --- 180,223 ---- proto_tree_add_uint_hidden(udp_tree, hf_udp_port, NullTVB, offset+2, 2, uh_dport); proto_tree_add_uint(udp_tree, hf_udp_length, NullTVB, 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, NullTVB, ! 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, NullTVB, ! 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); }
- Follow-Ups:
- Re: [ethereal-dev] tcp header checksum validation
- From: Skip Hansen
- Re: [ethereal-dev] tcp header checksum validation
- From: Guy Harris
- Re: [ethereal-dev] tcp header checksum validation
- References:
- [ethereal-dev] tcp header checksum validation
- From: Skip Hansen
- [ethereal-dev] tcp header checksum validation
- Prev by Date: [ethereal-dev] tcp header checksum validation
- Next by Date: Re: [ethereal-dev] tcp header checksum validation
- Previous by thread: [ethereal-dev] tcp header checksum validation
- Next by thread: Re: [ethereal-dev] tcp header checksum validation
- Index(es):