/* capture.c
* Routines for packet capture windows
*
- * $Id: capture.c,v 1.185 2002/07/16 05:43:39 guy Exp $
+ * $Id: capture.c,v 1.207 2003/05/15 13:33:53 deniel Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
- *
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifdef HAVE_LIBPCAP
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
# define MUST_DO_SELECT
#endif
-#include "gtk/main.h"
-#include "gtk/gtkglobals.h"
#include <epan/packet.h>
#include "file.h"
#include "capture.h"
#include "packet-ieee80211.h"
#include "packet-chdlc.h"
#include "packet-prism.h"
+#include "packet-ipfc.h"
+#include "packet-arcnet.h"
-#ifdef WIN32
+#ifdef _WIN32
#include "capture-wpcap.h"
#endif
#endif
static void capture_delete_cb(GtkWidget *, GdkEvent *, gpointer);
static void capture_stop_cb(GtkWidget *, gpointer);
-static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
- const u_char *);
+static void capture_pcap_cb(guchar *, const struct pcap_pkthdr *,
+ const guchar *);
static void get_capture_file_io_error(char *, int, const char *, int, gboolean);
static void popup_errmsg(const char *);
static void send_errmsg_to_parent(const char *);
static void adjust_header(loop_data *, struct pcap_hdr *, struct pcaprec_hdr *);
static int pipe_open_live(char *, struct pcap_hdr *, loop_data *, char *, int);
static int pipe_dispatch(int, loop_data *, struct pcap_hdr *, \
- struct pcaprec_modified_hdr *, u_char *, char *, int);
+ struct pcaprec_modified_hdr *, guchar *, char *, int);
#endif
/* Win32 needs the O_BINARY flag for open() */
/* Open a specified file, or create a temporary file, and start a capture
to the file in question. */
void
-do_capture(char *capfile_name)
+do_capture(const char *save_file)
{
char tmpname[128+1];
gboolean is_tempfile;
- u_char c;
+ guchar c;
int i;
guint byte_count;
char *msg;
int capture_succeeded;
gboolean stats_known;
struct pcap_stat stats;
-
- if (capfile_name != NULL) {
+ gchar *capfile_name;
+
+ if (save_file != NULL) {
+ /* If the Sync option is set, we return to the caller while the capture
+ * is in progress. Therefore we need to take a copy of save_file in
+ * case the caller destroys it after we return.
+ */
+ capfile_name = g_strdup(save_file);
if (capture_opts.ringbuffer_on) {
/* ringbuffer is enabled */
cfile.save_file_fd = ringbuf_init(capfile_name,
ringbuf_error_cleanup();
}
simple_dialog(ESD_TYPE_CRIT, NULL,
- file_open_error_message(errno, TRUE), capfile_name);
+ file_open_error_message(errno, TRUE, WTAP_FILE_PCAP), capfile_name);
}
+ g_free(capfile_name);
return;
}
close_cap_file(&cfile);
g_assert(cfile.save_file == NULL);
cfile.save_file = capfile_name;
+ /* cfile.save_file is "g_free"ed below, which is equivalent to
+ "g_free(capfile_name)". */
if (capture_opts.sync_mode) { /* do the capture in a child process */
char ssnap[24];
argv = add_arg(argv, &argc, sync_pipe_fd);
/* Convert filter string to a quote delimited string and pass to child */
+ filterstring = NULL;
if (cfile.cfilter != NULL && strlen(cfile.cfilter) != 0) {
argv = add_arg(argv, &argc, "-f");
filterstring = quote_encapsulate(cfile.cfilter);
/* Spawn process */
fork_child = spawnvp(_P_NOWAIT, ethereal_path, argv);
g_free(fontstring);
- g_free(filterstring);
+ if (filterstring) {
+ g_free(filterstring);
+ }
/* Keep a copy for later evaluation by _cwait() */
child_process = fork_child;
#else
- signal(SIGCHLD, SIG_IGN);
if (pipe(sync_pipe) < 0) {
/* Couldn't create the pipe between parent and child. */
error = errno;
case READ_ABORTED:
/* Exit by leaving the main loop, so that any quit functions
we registered get called. */
- gtk_main_quit();
+ if (gtk_main_level() > 0)
+ gtk_main_quit();
return;
}
}
/* There's stuff to read from the sync pipe, meaning the child has sent
us a message, or the sync pipe has closed, meaning the child has
closed it (perhaps because it exited). */
-static void
+static void
cap_file_input_cb(gpointer data, gint source _U_,
GdkInputCondition condition _U_)
{
capturing any more packets. Pick up its exit status, and
complain if it did anything other than exit with status 0. */
wait_for_child(FALSE);
-
+
/* Read what remains of the capture file, and finish the capture.
XXX - do something if this fails? */
switch (finish_tail_cap_file(cf, &err)) {
q++;
nread--;
break;
- }
+ }
}
/* Read from the capture file the number of records the child told us
sigmsg = "Segmentation violation";
break;
- /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO
+ /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO
Linux is POSIX compliant. These are not POSIX-defined signals ---
ISO/IEC 9945-1:1990 (IEEE Std 1003.1-1990), paragraph B.3.3.1.1 sez:
#define CAP_READ_TIMEOUT 250
#ifndef _WIN32
-/* Take carre of byte order in the libpcap headers read from pipes.
+/* Take care 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)
}
}
-/* Mimic pcap_open_live() for pipe captures
+/* 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
"Unexpected error from select: %s", strerror(errno));
goto error;
} else if (sel_ret > 0) {
- b = read(fd, &magic+bytes_read, sizeof magic-bytes_read);
+ b = read(fd, ((char *)&magic)+bytes_read, sizeof magic-bytes_read);
if (b <= 0) {
if (b == 0)
snprintf(errmsg, errmsgl, "End of file on pipe during open");
static int
pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr,
- struct pcaprec_modified_hdr *rechdr, u_char *data,
+ struct pcaprec_modified_hdr *rechdr, guchar *data,
char *errmsg, int errmsgl)
{
struct pcap_pkthdr phdr;
phdr.ts.tv_usec = rechdr->hdr.ts_usec;
phdr.caplen = rechdr->hdr.incl_len;
phdr.len = rechdr->hdr.orig_len;
-
- capture_pcap_cb((u_char *)ld, &phdr, data);
-
+
+ capture_pcap_cb((guchar *)ld, &phdr, data);
+
ld->pipe_state = STATE_EXPECT_REC_HDR;
return 1;
case PD_PIPE_ERR:
snprintf(errmsg, errmsgl, "Error reading from pipe: %s",
strerror(errno));
+ /* Fall through */
case PD_ERR:
- /* Fall out */
-
+ break;
}
ld->pipe_err = PIPERR;
capture(gboolean *stats_known, struct pcap_stat *stats)
{
GtkWidget *cap_w, *main_vb, *stop_bt, *counts_tb;
+ GtkWidget *counts_fr, *running_tb, *running_label, *running_time;
pcap_t *pch;
int pcap_encap;
int file_snaplen;
bpf_u_int32 netnum, netmask;
struct bpf_program fcode;
time_t upd_time, cur_time;
+ time_t start_time;
int err, inpkts;
condition *cnd_stop_capturesize = NULL;
condition *cnd_stop_timeout = NULL;
+ condition *cnd_ring_timeout = NULL;
unsigned int i;
static const char capstart_msg = SP_CAPSTART;
char errmsg[4096+1];
- gboolean dump_ok;
+ gboolean write_ok;
+ gboolean close_ok;
fd_set set1;
struct timeval timeout;
struct {
{ "TCP", &ld.counts.tcp, NULL, NULL, NULL },
{ "UDP", &ld.counts.udp, NULL, NULL, NULL },
{ "ICMP", &ld.counts.icmp, NULL, NULL, NULL },
+ { "ARP", &ld.counts.arp, NULL, NULL, NULL },
{ "OSPF", &ld.counts.ospf, NULL, NULL, NULL },
{ "GRE", &ld.counts.gre, NULL, NULL, NULL },
{ "NetBIOS", &ld.counts.netbios, NULL, NULL, NULL },
#define N_COUNTS (sizeof counts / sizeof counts[0])
-#ifdef _WIN32
- WORD wVersionRequested;
- WSADATA wsaData;
+#ifdef _WIN32
+ WORD wVersionRequested;
+ WSADATA wsaData;
#else
static const char ppamsg[] = "can't find PPA for ";
char *libpcap_warn;
int pipe_fd = -1;
struct pcap_hdr hdr;
struct pcaprec_modified_hdr rechdr;
- u_char pcap_data[WTAP_MAX_PACKET_SIZE];
+ guchar pcap_data[WTAP_MAX_PACKET_SIZE];
#endif
#ifdef MUST_DO_SELECT
int pcap_fd = 0;
signed/unsigned 64-bit int */
#define DECISIZE 20
- /* Initialize Windows Socket if we are in a WIN32 OS
+ /* Initialize Windows Socket if we are in a WIN32 OS
This needs to be done before querying the interface for network/netmask */
-#ifdef _WIN32
- wVersionRequested = MAKEWORD( 1, 1 );
- err = WSAStartup( wVersionRequested, &wsaData );
- if (err!=0) {
- snprintf(errmsg, sizeof errmsg,
- "Couldn't initialize Windows Sockets.");
- pch=NULL;
- goto error;
- }
-#endif
+#ifdef _WIN32
+ wVersionRequested = MAKEWORD( 1, 1 );
+ err = WSAStartup( wVersionRequested, &wsaData );
+ if (err!=0) {
+ snprintf(errmsg, sizeof errmsg,
+ "Couldn't initialize Windows Sockets.");
+ pch=NULL;
+ goto error;
+ }
+#endif
ld.go = TRUE;
ld.counts.total = 0;
ld.counts.netbios = 0;
ld.counts.vines = 0;
ld.counts.other = 0;
+ ld.counts.arp = 0;
ld.pdh = NULL;
/* We haven't yet gotten the capture statistics. */
open_err_str);
if (pch == NULL) {
+ /* We couldn't open "cfile.iface" as a network device. */
#ifdef _WIN32
- /* Well, we couldn't start the capture.
+ /* On Windows, we don't support capturing on pipes, so we give up.
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
"The capture session could not be initiated (%s).\n"
"Please check that you have the proper interface specified.\n"
"\n"
- "Note that the driver Ethereal uses for packet capture on Windows\n"
- "doesn't support capturing on PPP/WAN interfaces in Windows NT/2000/XP/.NET Server.\n",
+ "Note that the driver Ethereal uses for packet capture on Windows doesn't\n"
+ "support capturing on PPP/WAN interfaces in Windows NT/2000/XP/.NET Server.\n",
open_err_str);
goto error;
#else
goto error;
}
if (capture_opts.ringbuffer_on) {
- ld.pdh = ringbuf_init_wtap_dump_fdopen(WTAP_FILE_PCAP, ld.linktype,
+ ld.pdh = ringbuf_init_wtap_dump_fdopen(WTAP_FILE_PCAP, ld.linktype,
file_snaplen, &err);
} else {
ld.pdh = wtap_dump_fdopen(cfile.save_file_fd, WTAP_FILE_PCAP,
gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
gtk_widget_show(main_vb);
+ counts_fr = gtk_frame_new("Captured Frames");
+ gtk_box_pack_start(GTK_BOX(main_vb), counts_fr, FALSE, FALSE, 3);
+ gtk_widget_show(counts_fr);
+
/* Individual statistic elements */
counts_tb = gtk_table_new(N_COUNTS, 3, TRUE);
- gtk_box_pack_start(GTK_BOX(main_vb), counts_tb, TRUE, TRUE, 3);
+ gtk_container_add(GTK_CONTAINER(counts_fr), counts_tb);
+ gtk_container_border_width(GTK_CONTAINER(counts_tb), 5);
gtk_widget_show(counts_tb);
for (i = 0; i < N_COUNTS; i++) {
gtk_widget_show(counts[i].percent);
}
+ /* Running time */
+ running_tb = gtk_table_new(1, 3, TRUE);
+ gtk_box_pack_start(GTK_BOX(main_vb), running_tb, FALSE, FALSE, 3);
+ gtk_widget_show(running_tb);
+
+ running_label = gtk_label_new("Running");
+ gtk_misc_set_alignment(GTK_MISC(running_label), 0.0f, 0.0f);
+ gtk_widget_show(running_label);
+ gtk_table_attach_defaults(GTK_TABLE(running_tb),
+ running_label, 0, 1, 0, 1);
+
+ running_time = gtk_label_new("00:00:00");
+ gtk_misc_set_alignment(GTK_MISC(running_time), 0.0f, 0.0f);
+ gtk_widget_show(running_time);
+ gtk_table_attach(GTK_TABLE(running_tb),
+ running_time,
+ 1, 2, 0, 1, 0, 0, 5, 0);
+
/* allow user to either click a stop button, or the close button on
the window to stop a capture in progress. */
stop_bt = gtk_button_new_with_label ("Stop");
GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
gtk_signal_connect(GTK_OBJECT(cap_w), "delete_event",
GTK_SIGNAL_FUNC(capture_delete_cb), (gpointer) &ld);
- gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
gtk_widget_grab_default(stop_bt);
GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
gtk_widget_show(cap_w);
+ start_time = time(NULL);
upd_time = time(NULL);
#ifdef MUST_DO_SELECT
if (!ld.from_pipe) pcap_fd = pcap_fileno(pch);
if (capture_child)
signal(SIGUSR1, stop_capture);
#endif
- /* initialize capture stop conditions */
+ /* initialize capture stop conditions */
init_capture_stop_conditions();
/* create stop conditions */
if (capture_opts.has_autostop_filesize)
cnd_stop_capturesize =
- cnd_new(CND_CLASS_CAPTURESIZE,(long)capture_opts.autostop_filesize * 1000);
+ cnd_new(CND_CLASS_CAPTURESIZE,(long)capture_opts.autostop_filesize * 1000);
if (capture_opts.has_autostop_duration)
cnd_stop_timeout =
cnd_new(CND_CLASS_TIMEOUT,(gint32)capture_opts.autostop_duration);
+ if (capture_opts.ringbuffer_on && capture_opts.has_ring_duration)
+ cnd_ring_timeout =
+ cnd_new(CND_CLASS_TIMEOUT, capture_opts.ringbuffer_duration);
+
while (ld.go) {
while (gtk_events_pending()) gtk_main_iteration();
* "select()" says we can read from it without blocking; go for
* it.
*/
- inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+ inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (guchar *) &ld);
if (inpkts < 0) {
ld.pcap_err = TRUE;
ld.go = FALSE;
}
}
#else
- inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+ inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (guchar *) &ld);
if (inpkts < 0) {
ld.pcap_err = TRUE;
ld.go = FALSE;
if (inpkts > 0) {
ld.sync_packets += inpkts;
/* check capture stop conditons */
- if (cnd_stop_capturesize != NULL && cnd_eval(cnd_stop_capturesize,
+ if (cnd_stop_capturesize != NULL && cnd_eval(cnd_stop_capturesize,
(guint32)wtap_get_bytes_dumped(ld.pdh))){
/* Capture file reached its maximum size. */
if (capture_opts.ringbuffer_on) {
if (ringbuf_switch_file(&cfile, &ld.pdh, &ld.err)) {
/* File switch succeeded: reset the condition */
cnd_reset(cnd_stop_capturesize);
+ if (cnd_ring_timeout) {
+ cnd_reset(cnd_ring_timeout);
+ }
} else {
/* File switch failed: stop here */
ld.go = FALSE;
if (cur_time > upd_time) {
upd_time = cur_time;
+ /* calculate and display running time */
+ cur_time -= start_time;
+ snprintf(label_str, sizeof(label_str), "%02ld:%02ld:%02ld",
+ (long)(cur_time/3600), (long)((cur_time%3600)/60),
+ (long)(cur_time%60));
+ gtk_label_set(GTK_LABEL(running_time), label_str);
+
if (ld.sync_packets) {
for (i = 0; i < N_COUNTS; i++) {
snprintf(label_str, sizeof(label_str), "%d",
*counts[i].value_ptr);
-
+
gtk_label_set(GTK_LABEL(counts[i].value), label_str);
-
+
snprintf(label_str, sizeof(label_str), "(%.1f%%)",
pct(*counts[i].value_ptr, ld.counts.total));
-
+
gtk_label_set(GTK_LABEL(counts[i].percent), label_str);
}
if (cnd_stop_timeout != NULL && cnd_eval(cnd_stop_timeout)) {
/* The specified capture time has elapsed; stop the capture. */
ld.go = FALSE;
+ } else if (cnd_ring_timeout != NULL && cnd_eval(cnd_ring_timeout)) {
+ /* time elasped for this ring file, swith to the next */
+ if (ringbuf_switch_file(&cfile, &ld.pdh, &ld.err)) {
+ /* File switch succeeded: reset the condition */
+ cnd_reset(cnd_ring_timeout);
+ } else {
+ /* File switch failed: stop here */
+ ld.go = FALSE;
+ }
}
}
+
} /* while (ld.go) */
-
+
/* delete stop conditions */
if (cnd_stop_capturesize != NULL)
cnd_delete(cnd_stop_capturesize);
if (cnd_stop_timeout != NULL)
cnd_delete(cnd_stop_timeout);
+ if (cnd_ring_timeout != NULL)
+ cnd_delete(cnd_ring_timeout);
if (ld.pcap_err) {
snprintf(errmsg, sizeof(errmsg), "Error while capturing packets: %s",
popup_errmsg(errmsg);
#endif
- if (ld.err != 0) {
+ if (ld.err == 0)
+ write_ok = TRUE;
+ else {
get_capture_file_io_error(errmsg, sizeof(errmsg), cfile.save_file, ld.err,
FALSE);
popup_errmsg(errmsg);
+ write_ok = FALSE;
+ }
- /* A write failed, so we've already told the user there's a problem;
- if the close fails, there's no point in telling them about that
- as well. */
- if (capture_opts.ringbuffer_on) {
- ringbuf_wtap_dump_close(&cfile, &err);
- } else {
- wtap_dump_close(ld.pdh, &err);
- }
- } else {
- if (capture_opts.ringbuffer_on) {
- dump_ok = ringbuf_wtap_dump_close(&cfile, &err);
- } else {
- dump_ok = wtap_dump_close(ld.pdh, &err);
- }
- if (!dump_ok) {
- get_capture_file_io_error(errmsg, sizeof(errmsg), cfile.save_file, err,
- TRUE);
- popup_errmsg(errmsg);
- }
+ if (capture_opts.ringbuffer_on) {
+ close_ok = ringbuf_wtap_dump_close(&cfile, &err);
+ } else {
+ close_ok = wtap_dump_close(ld.pdh, &err);
+ }
+ /* If we've displayed a message about a write error, there's no point
+ in displaying another message about an error on close. */
+ if (!close_ok && write_ok) {
+ get_capture_file_io_error(errmsg, sizeof(errmsg), cfile.save_file, err,
+ TRUE);
+ popup_errmsg(errmsg);
}
+ /* Set write_ok to mean the write and the close were successful. */
+ write_ok = (write_ok && close_ok);
#ifndef _WIN32
/*
pcap_close(pch);
}
-#ifdef WIN32
+#ifdef _WIN32
/* Shut down windows sockets */
WSACleanup();
#endif
gtk_grab_remove(GTK_WIDGET(cap_w));
gtk_widget_destroy(GTK_WIDGET(cap_w));
- return TRUE;
+ return write_ok;
error:
if (capture_opts.ringbuffer_on) {
cfile.save_file = NULL;
popup_errmsg(errmsg);
-#ifndef WIN32
+#ifndef _WIN32
if (ld.from_pipe) {
if (pipe_fd >= 0)
close(pipe_fd);
static void
capture_stop_cb(GtkWidget *w _U_, gpointer data) {
loop_data *ld = (loop_data *) data;
-
+
ld->go = FALSE;
}
}
static void
-capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
- const u_char *pd)
+capture_pcap_cb(guchar *user, const struct pcap_pkthdr *phdr,
+ const guchar *pd)
{
struct wtap_pkthdr whdr;
union wtap_pseudo_header pseudo_header;
loop_data *ld = (loop_data *) user;
int err;
- if ((++ld->counts.total >= ld->max) && (ld->max > 0))
+ if ((++ld->counts.total >= ld->max) && (ld->max > 0))
{
ld->go = FALSE;
}
case WTAP_ENCAP_LOCALTALK:
capture_llap(&ld->counts);
break;
- case WTAP_ENCAP_ATM_SNIFFER:
+ case WTAP_ENCAP_ATM_PDUS:
capture_atm(&pseudo_header, pd, whdr.caplen, &ld->counts);
break;
+ case WTAP_ENCAP_IP_OVER_FC:
+ capture_ipfc(pd, whdr.caplen, &ld->counts);
+ break;
+ case WTAP_ENCAP_ARCNET:
+ capture_arcnet(pd, whdr.caplen, &ld->counts, FALSE, TRUE);
+ break;
+ case WTAP_ENCAP_ARCNET_LINUX:
+ capture_arcnet(pd, whdr.caplen, &ld->counts, TRUE, FALSE);
+ break;
/* XXX - some ATM drivers on FreeBSD might prepend a 4-byte ATM
pseudo-header to DLT_ATM_RFC1483, with LLC header following;
we might have to implement that at some point. */