Ethereal-dev: [ethereal-dev] [patch] pipe capture
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: Olivier Abad <oabad@xxxxxxxxxxxxx>
Date: Tue, 25 Jul 2000 00:37:07 +0200
Here is a first patch to read captures from a pipe. I didn't change anything in the capture window : if you enter a pipe name (e.g. /tmp/fifo) instead of an interface name, this name is passed to the capture() function, which first tries a pcap_open_live() on it, and if it fails, performs a pipe_open_live() I have created two functions : pipe_open_live() to initialize the capture from the pipe, and pipe_dispatch() to read one record from the pipe and write it in the capture file. Reading from stdin seems to work also. You can do something like this : $ wget -O - http://ethereal.zing.org/sample/v6.pcap | ./ethereal -i - -k Please tell me if you find problems. I'll check the code in CVS later. Olivier P.S. : I guess it will work only on Unix platforms. I'll have to put #ifdefs for win32. -- When you have 200 programmers trying to write code for one product, like Win95 or NT, what you get is a multipule personality program. By definition, the real problem is that these programs are psychotic by nature and make people crazy when they use them. -- Joan Brewer on alt.destroy.microsoft
diff -uNr ethereal/capture.c ethereal.pipe/capture.c
--- ethereal/capture.c Fri Jul 21 23:19:05 2000
+++ ethereal.pipe/capture.c Mon Jul 24 23:29:54 2000
@@ -99,6 +99,8 @@
#include "simple_dialog.h"
#include "prefs.h"
#include "globals.h"
+#include "wiretap/libpcap.h"
+#include "wiretap/wtap-int.h"
#include "packet-clip.h"
#include "packet-eth.h"
@@ -134,6 +136,9 @@
gint max;
gint linktype;
gint sync_packets;
+ gboolean from_pipe; /* TRUE if we are capturing data from a pipe */
+ gboolean modified; /* TRUE if data in the pipe uses modified pcap headers */
+ gboolean byte_swapped; /* TRUE if data in the pipe is byte swapped */
packet_counts counts;
wtap_dumper *pdh;
} loop_data;
@@ -707,6 +712,204 @@
*/
#define CAP_READ_TIMEOUT 250
+static void
+adjust_header(loop_data *ld, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr)
+{
+ if (ld->byte_swapped) {
+ /* Byte-swap the record header fields. */
+ rechdr->ts_sec = BSWAP32(rechdr->ts_sec);
+ rechdr->ts_usec = BSWAP32(rechdr->ts_usec);
+ rechdr->incl_len = BSWAP32(rechdr->incl_len);
+ rechdr->orig_len = BSWAP32(rechdr->orig_len);
+ }
+ if (hdr->version_major == 2 &&
+ (hdr->version_minor < 3 ||
+ (hdr->version_minor == 3 && rechdr->incl_len > rechdr->orig_len))) {
+ guint32 temp;
+
+ temp = rechdr->orig_len;
+ rechdr->orig_len = rechdr->incl_len;
+ rechdr->incl_len = temp;
+ }
+}
+
+/* mimic pcap_open_live() for pipe captures */
+static int
+pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, char *ebuf)
+{
+ struct stat pipe_stat;
+ int fd;
+ guint32 magic;
+ int bytes_read, b;
+
+ if (strcmp(pipename, "-") == 0) fd = 0; /* read from stdin */
+ else if (stat(pipename, &pipe_stat) == 0 && S_ISFIFO(pipe_stat.st_mode)) {
+ if ((fd = open(pipename, O_RDONLY)) == -1) return -1;
+ } else return -1;
+
+ ld->from_pipe = TRUE;
+ /* read the pcap header */
+ if (read(fd, &magic, sizeof magic) != sizeof magic) {
+ close(fd);
+ return -1;
+ }
+
+ switch (magic) {
+ case PCAP_MAGIC:
+ /* Host that wrote it has our byte order. */
+ ld->byte_swapped = FALSE;
+ ld->modified = FALSE;
+ break;
+ case PCAP_MODIFIED_MAGIC:
+ /* Host that wrote it has our byte order, but was running
+ a program using the patched "libpcap". */
+ ld->byte_swapped = FALSE;
+ ld->modified = TRUE;
+ break;
+ case PCAP_SWAPPED_MAGIC:
+ /* Host that wrote it has a byte order opposite to ours. */
+ ld->byte_swapped = TRUE;
+ ld->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". */
+ ld->byte_swapped = TRUE;
+ ld->modified = TRUE;
+ break;
+ default:
+ /* Not a "libpcap" type we know about. */
+ close(fd);
+ return -1;
+ }
+
+ bytes_read = read(fd, hdr, sizeof(struct pcap_hdr));
+ if (bytes_read <= 0) {
+ close(fd);
+ return -1;
+ }
+ while (bytes_read < sizeof(struct pcap_hdr))
+ {
+ b = read(fd, ((char *)&hdr)+bytes_read, sizeof(struct pcap_hdr) - bytes_read);
+ if (b <= 0) {
+ close(fd);
+ return -1;
+ }
+ bytes_read += b;
+ }
+ if (ld->byte_swapped) {
+ /* Byte-swap the header fields about which we care. */
+ hdr->version_major = BSWAP16(hdr->version_major);
+ hdr->version_minor = BSWAP16(hdr->version_minor);
+ hdr->snaplen = BSWAP32(hdr->snaplen);
+ hdr->network = BSWAP32(hdr->network);
+ }
+ if (hdr->version_major < 2) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr)
+{
+ struct wtap_pkthdr whdr;
+ struct pcaprec_modified_hdr rechdr;
+ int bytes_to_read, bytes_read, b;
+ u_char pd[WTAP_MAX_PACKET_SIZE];
+ int err;
+
+ bytes_to_read = ld->modified ? sizeof rechdr : sizeof rechdr.hdr;
+ bytes_read = read(fd, &rechdr, bytes_to_read);
+ if (bytes_read <= 0) {
+ close(fd);
+ ld->go = FALSE;
+ fprintf(stderr, "record header read error : %d, errno = %d\n", bytes_read, errno);
+ return 0;
+ }
+ while (bytes_read < bytes_to_read)
+ {
+ b = read(fd, ((char *)&rechdr)+bytes_read, bytes_to_read - bytes_read);
+ if (b <= 0) {
+ close(fd);
+ ld->go = FALSE;
+ fprintf(stderr, "record header read error : %d, errno = %d\n", bytes_read, errno);
+ return 0;
+ }
+ bytes_read += b;
+ }
+ adjust_header(ld, hdr, &rechdr.hdr);
+ if (rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) {
+ close(fd);
+ ld->go = FALSE;
+ fprintf(stderr, "invalid record length : %d\n", rechdr.hdr.incl_len);
+ return 0;
+ }
+ bytes_read = read(fd, pd, rechdr.hdr.incl_len);
+ if (bytes_read <= 0) {
+ close(fd);
+ ld->go = FALSE;
+ fprintf(stderr, "record read error : %d, errno = %d\n", bytes_read, errno);
+ return 0;
+ }
+ while (bytes_read < rechdr.hdr.incl_len)
+ {
+ b = read(fd, pd+bytes_read, rechdr.hdr.incl_len - bytes_read);
+ if (b <= 0) {
+ close(fd);
+ ld->go = FALSE;
+ fprintf(stderr, "record read error : %d, errno = %d\n", bytes_read, errno);
+ return 0;
+ }
+ bytes_read += b;
+ }
+ whdr.ts.tv_sec = rechdr.hdr.ts_sec;
+ whdr.ts.tv_usec = rechdr.hdr.ts_usec;
+ whdr.caplen = rechdr.hdr.incl_len;
+ whdr.len = rechdr.hdr.orig_len;
+ whdr.pkt_encap = ld->linktype;
+ wtap_dump(ld->pdh, &whdr, NULL, pd, &err);
+
+ /* Set the initial payload to the packet length, and the initial
+ captured payload to the capture length (other protocols may
+ reduce them if their headers say they're less). */
+ pi.len = whdr.len;
+ pi.captured_len = whdr.caplen;
+
+ switch (ld->linktype) {
+ case WTAP_ENCAP_ETHERNET:
+ capture_eth(pd, 0, &ld->counts);
+ break;
+ case WTAP_ENCAP_FDDI:
+ case WTAP_ENCAP_FDDI_BITSWAPPED:
+ capture_fddi(pd, &ld->counts);
+ break;
+ case WTAP_ENCAP_TR:
+ capture_tr(pd, 0, &ld->counts);
+ break;
+ case WTAP_ENCAP_NULL:
+ capture_null(pd, &ld->counts);
+ break;
+ case WTAP_ENCAP_PPP:
+ capture_ppp(pd, 0, &ld->counts);
+ break;
+ case WTAP_ENCAP_RAW_IP:
+ capture_raw(pd, &ld->counts);
+ break;
+ case WTAP_ENCAP_LINUX_ATM_CLIP:
+ capture_clip(pd, &ld->counts);
+ break;
+ /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483,
+ with LLC header following; we should implement it at some
+ point. */
+ }
+
+ return 1;
+}
+
/* Do the low-level work of a capture.
Returns TRUE if it succeeds, FALSE otherwise. */
int
@@ -724,12 +927,14 @@
#ifdef linux
fd_set set1;
struct timeval timeout;
- int pcap_fd;
+ int pcap_fd = 0;
#endif
#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
#endif
+ int pipe_fd = -1;
+ struct pcap_hdr hdr;
/* Initialize Windows Socket if we are in a WIN32 OS
This needs to be done before querying the interface for network/netmask */
@@ -748,6 +953,7 @@
ld.counts.total = 0;
ld.max = cfile.count;
ld.linktype = WTAP_ENCAP_UNKNOWN;
+ ld.from_pipe = FALSE;
ld.sync_packets = 0;
ld.counts.sctp = 0;
ld.counts.tcp = 0;
@@ -765,22 +971,28 @@
pch = pcap_open_live(cfile.iface, cfile.snap, 1, CAP_READ_TIMEOUT, err_str);
if (pch == NULL) {
- /* Well, we couldn't start the capture.
- If this is a child process that does the capturing in sync
- mode or fork mode, it shouldn't do any UI stuff until we pop up the
- capture-progress window, and, since we couldn't start the
- capture, we haven't popped it up. */
- if (!capture_child) {
- while (gtk_events_pending()) gtk_main_iteration();
- }
- snprintf(errmsg, sizeof errmsg,
- "The capture session could not be initiated (%s).\n"
- "Please check to make sure you have sufficient permissions, and that\n"
- "you have the proper interface specified.", err_str);
- goto error;
+ /* try to open cfile.iface as a pipe */
+ pipe_fd = pipe_open_live(cfile.iface, &hdr, &ld, err_str);
+
+ if (pipe_fd == -1) {
+ /* Well, we couldn't start the capture.
+ If this is a child process that does the capturing in sync
+ mode or fork mode, it shouldn't do any UI stuff until we pop up the
+ capture-progress window, and, since we couldn't start the
+ capture, we haven't popped it up. */
+ if (!capture_child) {
+ while (gtk_events_pending()) gtk_main_iteration();
+ }
+ snprintf(errmsg, sizeof errmsg,
+ "The capture session could not be initiated (%s).\n"
+ "Please check to make sure you have sufficient permissions, and that\n"
+ "you have the proper interface or pipe specified.", err_str);
+ goto error;
+ }
}
- if (cfile.cfilter) {
+ /* capture filters only work on real interfaces */
+ if (cfile.cfilter && !ld.from_pipe) {
/* A capture filter was specified; set it up. */
if (pcap_lookupnet (cfile.iface, &netnum, &netmask, err_str) < 0) {
snprintf(errmsg, sizeof errmsg,
@@ -800,14 +1012,15 @@
}
/* Set up to write to the capture file. */
- ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_datalink(pch));
+ ld.linktype = wtap_pcap_encap_to_wtap_encap(ld.from_pipe ? hdr.network
+ : pcap_datalink(pch));
if (ld.linktype == WTAP_ENCAP_UNKNOWN) {
strcpy(errmsg, "The network you're capturing from is of a type"
" that Ethereal doesn't support.");
goto error;
}
ld.pdh = wtap_dump_fdopen(cfile.save_file_fd, WTAP_FILE_PCAP,
- ld.linktype, pcap_snapshot(pch), &err);
+ ld.linktype, ld.from_pipe ? hdr.snaplen : pcap_snapshot(pch), &err);
if (ld.pdh == NULL) {
/* We couldn't set up to write to the capture file. */
@@ -929,41 +1142,60 @@
upd_time = time(NULL);
#ifdef linux
- pcap_fd = pcap_fileno(pch);
+ if (!ld.from_pipe) pcap_fd = pcap_fileno(pch);
#endif
while (ld.go) {
while (gtk_events_pending()) gtk_main_iteration();
+
+ if (ld.from_pipe) {
+ FD_ZERO(&set1);
+ FD_SET(pipe_fd, &set1);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+ if (select(pipe_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+ /*
+ * "select()" says we can read from the pipe without blocking; go for
+ * it. We are not sure we can read a whole record, but at least the
+ * begninning of one. pipe_dispatch() will block reading the whole
+ * record.
+ */
+ inpkts = pipe_dispatch(pipe_fd, &ld, &hdr);
+ } else
+ inpkts = 0;
+ }
+ else {
#ifdef linux
- /*
- * Sigh. The semantics of the read timeout argument to
- * "pcap_open_live()" aren't particularly well specified by
- * the "pcap" man page - at least with the BSD BPF code, the
- * intent appears to be, at least in part, a way of cutting
- * down the number of reads done on a capture, by blocking
- * until the buffer fills or a timer expires - and the Linux
- * libpcap doesn't actually support it, so we can't use it
- * to break out of the "pcap_dispatch()" every 1/4 of a second
- * or so.
- *
- * Thus, on Linux, we do a "select()" on the file descriptor for the
- * capture, with a timeout of CAP_READ_TIMEOUT milliseconds, or
- * CAP_READ_TIMEOUT*1000 microseconds.
- */
- FD_ZERO(&set1);
- FD_SET(pcap_fd, &set1);
- timeout.tv_sec = 0;
- timeout.tv_usec = CAP_READ_TIMEOUT*1000;
- if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) {
/*
- * "select()" says we can read from it without blocking; go for
- * it.
+ * Sigh. The semantics of the read timeout argument to
+ * "pcap_open_live()" aren't particularly well specified by
+ * the "pcap" man page - at least with the BSD BPF code, the
+ * intent appears to be, at least in part, a way of cutting
+ * down the number of reads done on a capture, by blocking
+ * until the buffer fills or a timer expires - and the Linux
+ * libpcap doesn't actually support it, so we can't use it
+ * to break out of the "pcap_dispatch()" every 1/4 of a second
+ * or so.
+ *
+ * Thus, on Linux, we do a "select()" on the file descriptor for the
+ * capture, with a timeout of CAP_READ_TIMEOUT milliseconds, or
+ * CAP_READ_TIMEOUT*1000 microseconds.
*/
- inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
- } else
- inpkts = 0;
+ FD_ZERO(&set1);
+ FD_SET(pcap_fd, &set1);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+ if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+ /*
+ * "select()" says we can read from it without blocking; go for
+ * it.
+ */
+ inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+ } else
+ inpkts = 0;
#else
- inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+ inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
#endif
+ }
if (inpkts > 0)
ld.sync_packets += inpkts;
/* Only update once a second so as not to overload slow displays */
@@ -1057,7 +1289,7 @@
break;
}
}
- pcap_close(pch);
+ if (!ld.from_pipe) pcap_close(pch);
gtk_grab_remove(GTK_WIDGET(cap_w));
gtk_widget_destroy(GTK_WIDGET(cap_w));
diff -uNr ethereal/wiretap/libpcap.c ethereal.pipe/wiretap/libpcap.c
--- ethereal/wiretap/libpcap.c Sat May 20 01:06:53 2000
+++ ethereal.pipe/wiretap/libpcap.c Sat Jul 22 15:03:23 2000
@@ -33,58 +33,10 @@
/* See source to the "libpcap" library for information on the "libpcap"
file format. */
-/* Magic numbers in "libpcap" files.
-
- "libpcap" file records are written in the byte order of the host that
- 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
-
/* On some systems, the FDDI MAC addresses are bit-swapped. */
#if !defined(ultrix) && !defined(__alpha) && !defined(__bsdi__)
#define BIT_SWAPPED_MAC_ADDRS
#endif
-
-/* "libpcap" file header (minus magic number). */
-struct pcap_hdr {
- guint16 version_major; /* major version number */
- guint16 version_minor; /* minor version number */
- gint32 thiszone; /* GMT to local correction */
- guint32 sigfigs; /* accuracy of timestamps */
- guint32 snaplen; /* max length of captured packets, in octets */
- guint32 network; /* data link type */
-};
-
-/* "libpcap" record header. */
-struct pcaprec_hdr {
- guint32 ts_sec; /* timestamp seconds */
- guint32 ts_usec; /* timestamp microseconds */
- guint32 incl_len; /* number of octets of packet saved in file */
- 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);
diff -uNr ethereal/wiretap/libpcap.h ethereal.pipe/wiretap/libpcap.h
--- ethereal/wiretap/libpcap.h Sat Jan 22 07:22:39 2000
+++ ethereal.pipe/wiretap/libpcap.h Sat Jul 22 15:03:25 2000
@@ -21,6 +21,54 @@
*
*/
+/* Magic numbers in "libpcap" files.
+
+ "libpcap" file records are written in the byte order of the host that
+ 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
+
+/* "libpcap" file header (minus magic number). */
+struct pcap_hdr {
+ guint16 version_major; /* major version number */
+ guint16 version_minor; /* minor version number */
+ gint32 thiszone; /* GMT to local correction */
+ guint32 sigfigs; /* accuracy of timestamps */
+ guint32 snaplen; /* max length of captured packets, in octets */
+ guint32 network; /* data link type */
+};
+
+/* "libpcap" record header. */
+struct pcaprec_hdr {
+ guint32 ts_sec; /* timestamp seconds */
+ guint32 ts_usec; /* timestamp microseconds */
+ guint32 incl_len; /* number of octets of packet saved in file */
+ 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 */
+};
+
int libpcap_open(wtap *wth, int *err);
gboolean libpcap_dump_open(wtap_dumper *wdh, int *err);
int libpcap_dump_can_write_encap(int filetype, int encap);
- Prev by Date: Re: [ethereal-dev] What does reload do, and does quit save?
- Next by Date: [ethereal-dev] The Edit->Cut, Copy, and Paste menu items greyed out
- Previous by thread: Re: [ethereal-dev] mobile ipv6 dissectors
- Next by thread: [ethereal-dev] The Edit->Cut, Copy, and Paste menu items greyed out
- Index(es):





