aboutsummaryrefslogtreecommitdiffstats
path: root/capture.c
diff options
context:
space:
mode:
authorOlivier Abad <oabad@noos.fr>2000-07-30 16:54:12 +0000
committerOlivier Abad <oabad@noos.fr>2000-07-30 16:54:12 +0000
commit9348644164a96c1a43e81ee80a9431adb05451f7 (patch)
treeadcbd3c7de6a664de0eb9eef34bce72866a25166 /capture.c
parent3d80db01df0b54fa0b84f265fd5421dca69d5f37 (diff)
Support for capturing packet data from a pipe (a FIFO, or standard input).
capture.c : - modified capture() to try to open an interface as a pipe if pcap_open_live() failed, and then read data in libpcap format from this pipe ; - add new functions used by capture() : pipe_open_live() and pipe_dispatch() which are equivalents to the pcap_ functions. libpcap.[ch] : - moved the MAGIC and headers definitions from libpcap.c to libpcap.h because capture() now needs it. svn path=/trunk/; revision=2181
Diffstat (limited to 'capture.c')
-rw-r--r--capture.c350
1 files changed, 313 insertions, 37 deletions
diff --git a/capture.c b/capture.c
index bbcdcef8b0..7e534631ec 100644
--- a/capture.c
+++ b/capture.c
@@ -1,7 +1,7 @@
/* capture.c
* Routines for packet capture windows
*
- * $Id: capture.c,v 1.113 2000/07/21 15:56:15 gram Exp $
+ * $Id: capture.c,v 1.114 2000/07/30 16:53:53 oabad Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -100,6 +100,9 @@
#include "prefs.h"
#include "globals.h"
+#include "wiretap/libpcap.h"
+#include "wiretap/wtap-int.h"
+
#include "packet-clip.h"
#include "packet-eth.h"
#include "packet-fddi.h"
@@ -134,6 +137,9 @@ typedef struct _loop_data {
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 +713,226 @@ cap_file_input_cb(gpointer data, gint source, GdkInputCondition condition)
*/
#define CAP_READ_TIMEOUT 250
+#ifndef _WIN32
+/* Take carre of byte order in the libpcap headers read from pipes.
+ * (function taken from wiretap/libpcap.c) */
+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);
+ }
+
+ /* 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 (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
+ * We check if "pipename" is "-" (stdin) or a FIFO, open it, and read the
+ * header.
+ * N.B. : we can't read the libpcap formats used in RedHat 6.1 or SuSE 6.3
+ * because we can't seek on pipes (see wiretap/libpcap.c for details) */
+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, and was running
+ a program using either standard or ss990417 libpcap. */
+ 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 either ss990915 or ss991029 libpcap. */
+ ld->byte_swapped = FALSE;
+ ld->modified = TRUE;
+ break;
+ case PCAP_SWAPPED_MAGIC:
+ /* Host that wrote it has a byte order opposite to ours,
+ and was running a program using either standard or
+ ss990417 libpcap. */
+ 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 either ss990915
+ or ss991029 libpcap. */
+ ld->byte_swapped = TRUE;
+ ld->modified = TRUE;
+ break;
+ default:
+ /* Not a "libpcap" type we know about. */
+ close(fd);
+ return -1;
+ }
+
+ /* Read the rest of the header */
+ 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;
+}
+
+/* We read one record from the pipe, take care of byte order in the record
+ * header, write the record in the capture file, and update capture statistics. */
+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;
+
+ /* read the record header */
+ 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;
+ 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;
+ return 0;
+ }
+ bytes_read += b;
+ }
+ /* take care of byte order */
+ adjust_header(ld, hdr, &rechdr.hdr);
+ if (rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) {
+ close(fd);
+ ld->go = FALSE;
+ return 0;
+ }
+ /* read the packet data */
+ bytes_read = read(fd, pd, rechdr.hdr.incl_len);
+ if (bytes_read <= 0) {
+ close(fd);
+ ld->go = FALSE;
+ 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;
+ return 0;
+ }
+ bytes_read += b;
+ }
+ /* dump the packet data to the capture file */
+ 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;
+
+ /* update capture statistics */
+ 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;
+}
+#endif
+
/* Do the low-level work of a capture.
Returns TRUE if it succeeds, FALSE otherwise. */
int
@@ -724,12 +950,16 @@ capture(void)
#ifdef linux
fd_set set1;
struct timeval timeout;
- int pcap_fd;
+ int pcap_fd = 0;
#endif
#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
#endif
+#ifndef _WIN32
+ int pipe_fd = -1;
+ struct pcap_hdr hdr;
+#endif
/* Initialize Windows Socket if we are in a WIN32 OS
This needs to be done before querying the interface for network/netmask */
@@ -748,6 +978,7 @@ capture(void)
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,6 +996,26 @@ capture(void)
pch = pcap_open_live(cfile.iface, cfile.snap, 1, CAP_READ_TIMEOUT, err_str);
if (pch == NULL) {
+#ifndef _WIN32
+ /* 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;
+ }
+#else
/* 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
@@ -774,13 +1025,15 @@ capture(void)
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);
+ "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;
+#endif
}
- 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 +1053,15 @@ capture(void)
}
/* 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 +1183,60 @@ capture(void)
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 +1330,10 @@ capture(void)
break;
}
}
- pcap_close(pch);
+ if (ld.from_pipe)
+ close(pipe_fd);
+ else
+ pcap_close(pch);
gtk_grab_remove(GTK_WIDGET(cap_w));
gtk_widget_destroy(GTK_WIDGET(cap_w));
@@ -1083,7 +1359,7 @@ error:
/* Display the dialog box ourselves; there's no parent. */
simple_dialog(ESD_TYPE_CRIT, NULL, errmsg);
}
- if (pch != NULL)
+ if (pch != NULL && !ld.from_pipe)
pcap_close(pch);
return FALSE;