Ethereal-users: Re: [ethereal-users] can-t read tcpdump dump on RH6

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

From: Guy Harris <guy@xxxxxxxxxx>
Date: Sat, 6 Nov 1999 02:43:04 -0800 (PST)
> Sigh.  Perhaps there's some heuristic we can use to detect, when reading
> a "libpcap" file, whether it's a real "libpcap" file or a Kuznetsified
> "libpcap" file, allowing Ethereal, on *ANY* platform, to read *both*
> real "libpcap" files and Kuznetsified "libpcap" files.

Well, I grabbed both the latest version of Alexey's patches (that
version changes the magic number), and the bad version (the one that
doesn't), and built "libpcap" and "tcpdump" with both sets of patches on
a RH 6.0 system here, and got capture files with both versions.  I
checked into the Ethereal CVS tree some changes that

	1) handle the new magic number

and

	2) if the file has the old magic number, does some
	   hacks^H^H^H^H^Hheuristics to try to see if it's a file of the
	   type the bad version of the patch generates.

It seems to work, at least on those test files, although there's no
*guarantee* that it'll detect a file of the type the bad version of the
patch generates.

I've filed bug 6773 on Red Hat's Bugzilla site, complaining about this,
and saying they should fix "libpcap" to use the new magic number, and
provide updates for "libpcap" and "tcpdump".  If any of you have Red Hat
6.1 and have support for it, please plead with Red Hat to fix this....

Here's a patch to Ethereal (or, rather, to Wiretap) that has the change
I checked in; hopefully, it'll let Ethereal read files from the Red Hat
6.1 version of "tcpdump":

Index: wiretap/libpcap.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/libpcap.c,v
retrieving revision 1.21
retrieving revision 1.23
diff -c -r1.21 -r1.23
*** libpcap.c	1999/10/05 07:06:06	1.21
--- libpcap.c	1999/11/06 10:31:47	1.23
***************
*** 39,48 ****
     writes them, and the reader is expected to fix this up.
  
     PCAP_MAGIC is the magic number, in host byte order; PCAP_SWAPPED_MAGIC
!   a byte-swapped version of that.  */
! #define	PCAP_MAGIC		0xa1b2c3d4
! #define	PCAP_SWAPPED_MAGIC	0xd4c3b2a1
  
  /* Macros to byte-swap 32-bit and 16-bit quantities. */
  #define	BSWAP32(x) \
  	((((x)&0xFF000000)>>24) | \
--- 39,58 ----
     writes them, and the reader is expected to fix this up.
  
     PCAP_MAGIC is the magic number, in host byte order; PCAP_SWAPPED_MAGIC
!    is a byte-swapped version of that.
  
+    PCAP_MODIFIED_MAGIC is for Alexey Kuznetsov's modified "libpcap"
+    format, as generated on Linux systems that have a "libpcap" with
+    his patches, at
+    
+ 	http://ftp.sunet.se/pub/os/Linux/ip-routing/lbl-tools/
+ 
+    applied; PCAP_SWAPPED_MODIFIED_MAGIC is the byte-swapped version. */
+ #define	PCAP_MAGIC			0xa1b2c3d4
+ #define	PCAP_SWAPPED_MAGIC		0xd4c3b2a1
+ #define	PCAP_MODIFIED_MAGIC		0xa1b2cd34
+ #define	PCAP_SWAPPED_MODIFIED_MAGIC	0x34cdb2a1
+ 
  /* Macros to byte-swap 32-bit and 16-bit quantities. */
  #define	BSWAP32(x) \
  	((((x)&0xFF000000)>>24) | \
***************
*** 76,82 ****
--- 86,103 ----
  	guint32	orig_len;	/* actual length of packet */
  };
  
+ /* "libpcap" record header for Alexey's patched version. */
+ struct pcaprec_modified_hdr {
+ 	struct pcaprec_hdr hdr;	/* the regular header */
+ 	guint32 ifindex;	/* index, in *capturing* machine's list of
+ 				   interfaces, of the interface on which this
+ 				   packet came in. */
+ 	guint16 protocol;	/* Ethernet packet type */
+ 	guint8 pkt_type;	/* broadcast/multicast/etc. indication */
+ };
+ 
  static int libpcap_read(wtap *wth, int *err);
+ static void adjust_header(wtap *wth, struct pcaprec_hdr *hdr);
  static int libpcap_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
      const u_char *pd, int *err);
  static int libpcap_dump_close(wtap_dumper *wdh, int *err);
***************
*** 144,150 ****
  	int bytes_read;
  	guint32 magic;
  	struct pcap_hdr hdr;
! 	int byte_swapped = 0;
  
  	/* Read in the number that should be at the start of a "libpcap" file */
  	file_seek(wth->fh, 0, SEEK_SET);
--- 165,174 ----
  	int bytes_read;
  	guint32 magic;
  	struct pcap_hdr hdr;
! 	gboolean byte_swapped;
! 	gboolean modified;
! 	struct pcaprec_hdr first_rec_hdr;
! 	struct pcaprec_hdr second_rec_hdr;
  
  	/* Read in the number that should be at the start of a "libpcap" file */
  	file_seek(wth->fh, 0, SEEK_SET);
***************
*** 158,170 ****
  		return 0;
  	}
  	wth->data_offset += sizeof magic;
  
! 	if (magic == PCAP_SWAPPED_MAGIC) {
  		/* Host that wrote it has a byte order opposite to ours. */
! 		magic = PCAP_MAGIC;
! 		byte_swapped = 1;
! 	}
! 	if (magic != PCAP_MAGIC) {
  		return 0;
  	}
  
--- 182,219 ----
  		return 0;
  	}
  	wth->data_offset += sizeof magic;
+ 
+ 	switch (magic) {
+ 
+ 	case PCAP_MAGIC:
+ 		/* Host that wrote it has our byte order. */
+ 		byte_swapped = FALSE;
+ 		modified = FALSE;
+ 		break;
+ 
+ 	case PCAP_MODIFIED_MAGIC:
+ 		/* Host that wrote it has our byte order, but was running
+ 		   a program using the patched "libpcap". */
+ 		byte_swapped = FALSE;
+ 		modified = TRUE;
+ 		break;
  
! 	case PCAP_SWAPPED_MAGIC:
  		/* Host that wrote it has a byte order opposite to ours. */
! 		byte_swapped = TRUE;
! 		modified = FALSE;
! 		break;
! 
! 	case PCAP_SWAPPED_MODIFIED_MAGIC:
! 		/* Host that wrote it out has a byte order opposite to
! 		   ours, and was running a program using the patched
! 		   "libpcap". */
! 		byte_swapped = TRUE;
! 		modified = TRUE;
! 		break;
! 
! 	default:
! 		/* Not a "libpcap" type we know about. */
  		return 0;
  	}
  
***************
*** 202,215 ****
  	}
  
  	/* This is a libpcap file */
! 	wth->file_type = WTAP_FILE_PCAP;
  	wth->capture.pcap = g_malloc(sizeof(libpcap_t));
  	wth->capture.pcap->byte_swapped = byte_swapped;
  	wth->capture.pcap->version_major = hdr.version_major;
  	wth->capture.pcap->version_minor = hdr.version_minor;
  	wth->subtype_read = libpcap_read;
  	wth->file_encap = pcap_encap[hdr.network];
  	wth->snapshot_length = hdr.snaplen;
  	return 1;
  }
  
--- 251,362 ----
  	}
  
  	/* This is a libpcap file */
! 	wth->file_type = modified ? WTAP_FILE_PCAP_MODIFIED : WTAP_FILE_PCAP;
  	wth->capture.pcap = g_malloc(sizeof(libpcap_t));
  	wth->capture.pcap->byte_swapped = byte_swapped;
+ 	wth->capture.pcap->modified = modified;
  	wth->capture.pcap->version_major = hdr.version_major;
  	wth->capture.pcap->version_minor = hdr.version_minor;
  	wth->subtype_read = libpcap_read;
  	wth->file_encap = pcap_encap[hdr.network];
  	wth->snapshot_length = hdr.snaplen;
+ 
+ 	/*
+ 	 * Is this a capture file with the non-modified magic number?
+ 	 */
+ 	if (!wth->capture.pcap->modified) {
+ 		/*
+ 		 * Yes.  Let's look at the header for the first record,
+ 		 * and see if, interpreting it as a non-modified header,
+ 		 * the position where it says the header for the
+ 		 * *second* record is contains a corrupted header.
+ 		 *
+ 		 * If so, this may be a modified capture file with a
+ 		 * non-modified magic number - in some versions of
+ 		 * Alexey's patches, the packet header format was
+ 		 * changed but the magic number wasn't, and, alas,
+ 		 * Red Hat appear to have picked up one of those
+ 		 * patches for RH 6.1, meaning RH 6.1 has a "tcpdump"
+ 		 * that writes out files that can't be read by any software
+ 		 * that expects non-modified headers if the magic number isn't
+ 		 * the modified magic number (e.g., any normal version of
+ 		 * "tcpdump", and Ethereal if we don't do this gross
+ 		 * heuristic).
+ 		 */
+ 		bytes_read = file_read(&first_rec_hdr, 1,
+ 		    sizeof first_rec_hdr, wth->fh);
+ 		if (bytes_read != sizeof first_rec_hdr) {
+ 			*err = file_error(wth->fh);
+ 			if (*err != 0)
+ 				return -1;	/* failed to read it */
+ 
+ 			/*
+ 			 * Short read - assume the file isn't modified,
+ 			 * and put the seek pointer back.  The attempt
+ 			 * to read the first packet will presumably get
+ 			 * the same short read.
+ 			 */
+ 			goto give_up;
+ 		}
+ 
+ 		adjust_header(wth, &first_rec_hdr);
+ 
+ 		if (first_rec_hdr.incl_len > WTAP_MAX_PACKET_SIZE) {
+ 			/*
+ 			 * The first record is bogus, so this is probably
+ 			 * a corrupt file.  Assume the file isn't modified,
+ 			 * and put the seek pointer back.  The attempt
+ 			 * to read the first packet will probably get
+ 			 * the same bogus length.
+ 			 */
+ 			goto give_up;
+ 		}
+ 
+ 		file_seek(wth->fh,
+ 		    wth->data_offset + sizeof first_rec_hdr + first_rec_hdr.incl_len,
+ 		    SEEK_SET);
+ 		bytes_read = file_read(&second_rec_hdr, 1,
+ 		    sizeof second_rec_hdr, wth->fh);
+ 
+ 		/*
+ 		 * OK, does the next packet's header look sane?
+ 		 */
+ 		if (bytes_read != sizeof second_rec_hdr) {
+ 			*err = file_error(wth->fh);
+ 			if (*err != 0)
+ 				return -1;	/* failed to read it */
+ 
+ 			/*
+ 			 * Short read - assume the file isn't modified,
+ 			 * and put the seek pointer back.  The attempt
+ 			 * to read the second packet will presumably get
+ 			 * the same short read error.
+ 			 */
+ 			goto give_up;
+ 		}
+ 
+ 		adjust_header(wth, &second_rec_hdr);
+ 		if (second_rec_hdr.incl_len > WTAP_MAX_PACKET_SIZE) {
+ 			/*
+ 			 * Oh, dear.  Maybe it's a Capture File
+ 			 * From Hell, and what looks like the
+ 			 * "header" of the next packet is actually
+ 			 * random junk from the middle of a packet.
+ 			 * Try treating it as a modified file;
+ 			 * if that doesn't work, it probably *is*
+ 			 * a corrupt file.
+ 			 */
+ 			wth->file_type = WTAP_FILE_PCAP_MODIFIED;
+ 			wth->capture.pcap->modified = TRUE;
+ 		}
+ 
+ 	give_up:
+ 		/*
+ 		 * Restore the seek pointer.
+ 		 */
+ 		file_seek(wth->fh, wth->data_offset, SEEK_SET);
+ 	}
+ 
  	return 1;
  }
  
***************
*** 217,230 ****
  static int libpcap_read(wtap *wth, int *err)
  {
  	guint	packet_size;
! 	int	bytes_read;
! 	struct pcaprec_hdr hdr;
  	int	data_offset;
  
  	/* Read record header. */
  	errno = WTAP_ERR_CANT_READ;
! 	bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
! 	if (bytes_read != sizeof hdr) {
  		*err = file_error(wth->fh);
  		if (*err != 0)
  			return -1;
--- 364,379 ----
  static int libpcap_read(wtap *wth, int *err)
  {
  	guint	packet_size;
! 	int	bytes_to_read, bytes_read;
! 	struct pcaprec_modified_hdr hdr;
  	int	data_offset;
  
  	/* Read record header. */
  	errno = WTAP_ERR_CANT_READ;
! 	bytes_to_read = wth->capture.pcap->modified ?
! 	    sizeof hdr : sizeof hdr.hdr;
! 	bytes_read = file_read(&hdr, 1, bytes_to_read, wth->fh);
! 	if (bytes_read != bytes_to_read) {
  		*err = file_error(wth->fh);
  		if (*err != 0)
  			return -1;
***************
*** 233,269 ****
  			return -1;
  		}
  		return 0;
- 	}
- 	wth->data_offset += sizeof hdr;
- 
- 	if (wth->capture.pcap->byte_swapped) {
- 		/* Byte-swap the record header fields. */
- 		hdr.ts_sec = BSWAP32(hdr.ts_sec);
- 		hdr.ts_usec = BSWAP32(hdr.ts_usec);
- 		hdr.incl_len = BSWAP32(hdr.incl_len);
- 		hdr.orig_len = BSWAP32(hdr.orig_len);
  	}
! 
! 	/* In file format version 2.3, the "incl_len" and "orig_len" fields
! 	   were swapped, in order to match the BPF header layout.
  
! 	   Unfortunately, some files were, according to a comment in the
! 	   "libpcap" source, written with version 2.3 in their headers
! 	   but without the interchanged fields, so if "incl_len" is
! 	   greater than "orig_len" - which would make no sense - we
! 	   assume that we need to swap them.  */
! 	if (wth->capture.pcap->version_major == 2 &&
! 	    (wth->capture.pcap->version_minor < 3 ||
! 	     (wth->capture.pcap->version_minor == 3 &&
! 	      hdr.incl_len > hdr.orig_len))) {
! 		guint32 temp;
! 
! 		temp = hdr.orig_len;
! 		hdr.orig_len = hdr.incl_len;
! 		hdr.incl_len = temp;
! 	}
  
! 	packet_size = hdr.incl_len;
  	if (packet_size > WTAP_MAX_PACKET_SIZE) {
  		/*
  		 * Probably a corrupt capture file; don't blow up trying
--- 382,393 ----
  			return -1;
  		}
  		return 0;
  	}
! 	wth->data_offset += bytes_read;
  
! 	adjust_header(wth, &hdr.hdr);
  
! 	packet_size = hdr.hdr.incl_len;
  	if (packet_size > WTAP_MAX_PACKET_SIZE) {
  		/*
  		 * Probably a corrupt capture file; don't blow up trying
***************
*** 289,301 ****
  	}
  	wth->data_offset += packet_size;
  
! 	wth->phdr.ts.tv_sec = hdr.ts_sec;
! 	wth->phdr.ts.tv_usec = hdr.ts_usec;
  	wth->phdr.caplen = packet_size;
! 	wth->phdr.len = hdr.orig_len;
  	wth->phdr.pkt_encap = wth->file_encap;
  
  	return data_offset;
  }
  
  int wtap_pcap_encap_to_wtap_encap(int encap)
--- 413,456 ----
  	}
  	wth->data_offset += packet_size;
  
! 	wth->phdr.ts.tv_sec = hdr.hdr.ts_sec;
! 	wth->phdr.ts.tv_usec = hdr.hdr.ts_usec;
  	wth->phdr.caplen = packet_size;
! 	wth->phdr.len = hdr.hdr.orig_len;
  	wth->phdr.pkt_encap = wth->file_encap;
  
  	return data_offset;
+ }
+ 
+ static void
+ adjust_header(wtap *wth, struct pcaprec_hdr *hdr)
+ {
+ 	if (wth->capture.pcap->byte_swapped) {
+ 		/* Byte-swap the record header fields. */
+ 		hdr->ts_sec = BSWAP32(hdr->ts_sec);
+ 		hdr->ts_usec = BSWAP32(hdr->ts_usec);
+ 		hdr->incl_len = BSWAP32(hdr->incl_len);
+ 		hdr->orig_len = BSWAP32(hdr->orig_len);
+ 	}
+ 
+ 	/* In file format version 2.3, the "incl_len" and "orig_len" fields
+ 	   were swapped, in order to match the BPF header layout.
+ 
+ 	   Unfortunately, some files were, according to a comment in the
+ 	   "libpcap" source, written with version 2.3 in their headers
+ 	   but without the interchanged fields, so if "incl_len" is
+ 	   greater than "orig_len" - which would make no sense - we
+ 	   assume that we need to swap them.  */
+ 	if (wth->capture.pcap->version_major == 2 &&
+ 	    (wth->capture.pcap->version_minor < 3 ||
+ 	     (wth->capture.pcap->version_minor == 3 &&
+ 	      hdr->incl_len > hdr->orig_len))) {
+ 		guint32 temp;
+ 
+ 		temp = hdr->orig_len;
+ 		hdr->orig_len = hdr->incl_len;
+ 		hdr->incl_len = temp;
+ 	}
  }
  
  int wtap_pcap_encap_to_wtap_encap(int encap)

Index: wiretap/wtap.h
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/wtap.h,v
retrieving revision 1.46
retrieving revision 1.48
diff -c -r1.46 -r1.48
*** wtap.h	1999/10/31 17:46:11	1.46
--- wtap.h	1999/11/06 10:31:46	1.48
***************
*** 101,108 ****
  #define WTAP_FILE_UNKNOWN			0
  #define WTAP_FILE_WTAP				1
  #define WTAP_FILE_PCAP				2
! #define WTAP_FILE_LANALYZER			3
! #define WTAP_FILE_NGSNIFFER			4
  #define WTAP_FILE_SNOOP				6
  #define WTAP_FILE_IPTRACE			7
  #define WTAP_FILE_NETMON_1_x			8
--- 101,109 ----
  #define WTAP_FILE_UNKNOWN			0
  #define WTAP_FILE_WTAP				1
  #define WTAP_FILE_PCAP				2
! #define WTAP_FILE_PCAP_MODIFIED			3
! #define WTAP_FILE_LANALYZER			4
! #define WTAP_FILE_NGSNIFFER			5
  #define WTAP_FILE_SNOOP				6
  #define WTAP_FILE_IPTRACE			7
  #define WTAP_FILE_NETMON_1_x			8
***************
*** 159,165 ****
  } lanalyzer_t;
  
  typedef struct {
! 	int	byte_swapped;
  	guint16	version_major;
  	guint16	version_minor;
  } libpcap_t;
--- 160,167 ----
  } lanalyzer_t;
  
  typedef struct {
! 	gboolean byte_swapped;
! 	gboolean modified;
  	guint16	version_major;
  	guint16	version_minor;
  } libpcap_t;


Index: wiretap/wtap.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/wtap.c,v
retrieving revision 1.27
retrieving revision 1.28
diff -c -r1.27 -r1.28
*** wtap.c	1999/10/31 17:46:10	1.27
--- wtap.c	1999/11/06 10:31:45	1.28
***************
*** 66,71 ****
--- 66,74 ----
  		case WTAP_FILE_PCAP:
  			return "pcap";
  
+ 		case WTAP_FILE_PCAP_MODIFIED:
+ 			return "pcap-modified";
+ 
  		case WTAP_FILE_LANALYZER:
  			return "Novell LANalyzer";
  
***************
*** 161,166 ****
--- 164,170 ----
  	 * But for now this will work. */
  	switch(wth->file_type) {
  		case WTAP_FILE_PCAP:
+ 		case WTAP_FILE_PCAP_MODIFIED:
  			g_free(wth->capture.pcap);
  			break;