forked from osmocom/wireshark
Add a "-S" flag to dumpcap, which prints out interface statistics. Use
this in the GUI rather than calling pcap_stats() directly. This gets rid of the last pcap_open_live() call in the GUI code. Update README.packaging. svn path=/trunk/; revision=22443
This commit is contained in:
parent
98309a6c83
commit
89a2966ced
|
@ -72,7 +72,7 @@ GList *get_interface_list(int *err, char **err_str);
|
|||
/* Error values from "get_interface_list()/capture_interface_list()". */
|
||||
#define CANT_GET_INTERFACE_LIST 1 /* error getting list */
|
||||
#define NO_INTERFACES_FOUND 2 /* list is empty */
|
||||
#define CANT_RUN_DUMPCAP 3 /* problem running 'dumpcap -I l' */
|
||||
#define CANT_RUN_DUMPCAP 3 /* problem running dumpcap */
|
||||
|
||||
void free_interface_list(GList *if_list);
|
||||
|
||||
|
|
139
capture.c
139
capture.c
|
@ -44,14 +44,18 @@
|
|||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h> /* needed to define AF_ values on UNIX */
|
||||
#endif
|
||||
|
@ -92,7 +96,16 @@
|
|||
#include "file_util.h"
|
||||
#include "log.h"
|
||||
|
||||
typedef struct if_stat_cache_item_s {
|
||||
char *name;
|
||||
struct pcap_stat ps;
|
||||
} if_stat_cache_item_t;
|
||||
|
||||
struct if_stat_cache_s {
|
||||
int stat_fd;
|
||||
int fork_child;
|
||||
GList *cache_list; /* List of if_stat_chache_entry_t */
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a capture.
|
||||
|
@ -675,7 +688,7 @@ capture_interface_list(int *err, char **err_str)
|
|||
|
||||
/* Check to see if we built a list */
|
||||
if (if_list == NULL) {
|
||||
if (*err_str)
|
||||
if (err_str && *err_str)
|
||||
*err_str = g_strdup("No interfaces found");
|
||||
*err = NO_INTERFACES_FOUND;
|
||||
}
|
||||
|
@ -722,7 +735,7 @@ capture_pcap_linktype_list(gchar *ifname, char **err_str)
|
|||
data_link_info = g_malloc(sizeof (data_link_info_t));
|
||||
data_link_info->dlt = (int) strtol(lt_parts[0], NULL, 10);
|
||||
data_link_info->name = g_strdup(lt_parts[1]);
|
||||
if (strcmp(lt_parts[2], "(not supported)") != NULL)
|
||||
if (strcmp(lt_parts[2], "(not supported)") != 0)
|
||||
data_link_info->description = g_strdup(lt_parts[2]);
|
||||
else
|
||||
data_link_info->description = NULL;
|
||||
|
@ -739,5 +752,119 @@ capture_pcap_linktype_list(gchar *ifname, char **err_str)
|
|||
return linktype_list;
|
||||
}
|
||||
|
||||
if_stat_cache_t *
|
||||
capture_stat_start(GList *if_list) {
|
||||
int stat_fd, fork_child;
|
||||
gchar *msg;
|
||||
if_stat_cache_t *sc = NULL;
|
||||
GList *if_entry;
|
||||
if_info_t *if_info;
|
||||
if_stat_cache_item_t *sc_item;
|
||||
|
||||
/* Fire up dumpcap. */
|
||||
/*
|
||||
* XXX - on systems with BPF, the number of BPF devices limits the
|
||||
* number of devices on which you can capture simultaneously.
|
||||
*
|
||||
* This means that
|
||||
*
|
||||
* 1) this might fail if you run out of BPF devices
|
||||
*
|
||||
* and
|
||||
*
|
||||
* 2) opening every interface could leave too few BPF devices
|
||||
* for *other* programs.
|
||||
*
|
||||
* It also means the system could end up getting a lot of traffic
|
||||
* that it has to pass through the networking stack and capture
|
||||
* mechanism, so opening all the devices and presenting packet
|
||||
* counts might not always be a good idea.
|
||||
*/
|
||||
if (sync_interface_stats_open(&stat_fd, &fork_child, &msg) == 0) {
|
||||
sc = g_malloc(sizeof(if_stat_cache_t));
|
||||
sc->stat_fd = stat_fd;
|
||||
sc->fork_child = fork_child;
|
||||
sc->cache_list = NULL;
|
||||
|
||||
/* Initialize the cache */
|
||||
for (if_entry = if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) {
|
||||
if_info = if_entry->data;
|
||||
sc_item = g_malloc0(sizeof(if_stat_cache_item_t));
|
||||
sc_item->name = g_strdup(if_info->name);
|
||||
sc->cache_list = g_list_append(sc->cache_list, sc_item);
|
||||
}
|
||||
}
|
||||
return sc;
|
||||
}
|
||||
|
||||
#define MAX_STAT_LINE_LEN 500
|
||||
|
||||
static void
|
||||
capture_stat_cache_update(if_stat_cache_t *sc) {
|
||||
gchar stat_line[MAX_STAT_LINE_LEN];
|
||||
gchar **stat_parts;
|
||||
GList *sc_entry;
|
||||
if_stat_cache_item_t *sc_item;
|
||||
|
||||
if (!sc)
|
||||
return;
|
||||
|
||||
while (sync_pipe_gets_nonblock(sc->stat_fd, stat_line, MAX_STAT_LINE_LEN) > 0) {
|
||||
g_strstrip(stat_line);
|
||||
stat_parts = g_strsplit(stat_line, "\t", 3);
|
||||
if (stat_parts[0] == NULL || stat_parts[1] == NULL ||
|
||||
stat_parts[2] == NULL) {
|
||||
g_strfreev(stat_parts);
|
||||
continue;
|
||||
}
|
||||
for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) {
|
||||
sc_item = sc_entry->data;
|
||||
if (strcmp(sc_item->name, stat_parts[0]) == 0) {
|
||||
sc_item->ps.ps_recv = (u_int) strtoul(stat_parts[1], NULL, 10);
|
||||
sc_item->ps.ps_drop = (u_int) strtoul(stat_parts[2], NULL, 10);
|
||||
}
|
||||
}
|
||||
g_strfreev(stat_parts);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
capture_stats(if_stat_cache_t *sc, char *ifname, struct pcap_stat *ps) {
|
||||
GList *sc_entry;
|
||||
if_stat_cache_item_t *sc_item;
|
||||
|
||||
if (!sc || !ifname || !ps) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
capture_stat_cache_update(sc);
|
||||
for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) {
|
||||
sc_item = sc_entry->data;
|
||||
if (strcmp(sc_item->name, ifname) == 0) {
|
||||
memcpy(ps, &sc_item->ps, sizeof(struct pcap_stat));
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
capture_stat_stop(if_stat_cache_t *sc) {
|
||||
GList *sc_entry;
|
||||
if_stat_cache_item_t *sc_item;
|
||||
gchar *msg;
|
||||
|
||||
if (!sc)
|
||||
return;
|
||||
|
||||
sync_interface_stats_close(&sc->stat_fd, &sc->fork_child, &msg);
|
||||
|
||||
for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) {
|
||||
sc_item = sc_entry->data;
|
||||
g_free(sc_item->name);
|
||||
g_free(sc_item);
|
||||
}
|
||||
g_free(sc);
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBPCAP */
|
||||
|
|
23
capture.h
23
capture.h
|
@ -81,6 +81,7 @@ extern void capture_input_cfilter_error_message(capture_options *capture_opts, c
|
|||
*/
|
||||
extern void capture_input_closed(capture_options *capture_opts);
|
||||
|
||||
#ifdef HAVE_LIBPCAP
|
||||
/**
|
||||
* Fetch the interface list from a child process.
|
||||
*/
|
||||
|
@ -92,4 +93,26 @@ extern GList *capture_interface_list(int *err, char **err_str);
|
|||
extern GList *capture_pcap_linktype_list(char *devname, char **err_str);
|
||||
|
||||
|
||||
struct if_stat_cache_s;
|
||||
typedef struct if_stat_cache_s if_stat_cache_t;
|
||||
|
||||
/**
|
||||
* Start gathering capture statistics for the interfaces specified.
|
||||
* @param A GList of if_info_t items
|
||||
* @return A pointer to the statistics state data.
|
||||
*/
|
||||
extern if_stat_cache_t * capture_stat_start(GList *if_list);
|
||||
|
||||
/**
|
||||
* Fetch capture statistics, similar to pcap_stats().
|
||||
*/
|
||||
struct pcap_stat; /* Stub in case we don't or haven't yet included pcap.h */
|
||||
extern gboolean capture_stats(if_stat_cache_t *sc, char *ifname, struct pcap_stat *ps);
|
||||
|
||||
/**
|
||||
* Stop gathering capture statistics.
|
||||
*/
|
||||
void capture_stat_stop(if_stat_cache_t *sc);
|
||||
#endif /* HAVE_LIBPCAP */
|
||||
|
||||
#endif /* capture.h */
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
@ -68,6 +72,10 @@
|
|||
#include "capture-pcap-util.h"
|
||||
#include <wiretap/file_util.h>
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
pcap_t *pch;
|
||||
} if_stat_t;
|
||||
|
||||
static gboolean capture_opts_output_to_pipe(const char *save_file, gboolean *is_pipe);
|
||||
|
||||
|
@ -558,6 +566,90 @@ capture_opts_list_interfaces(gboolean machine_readable)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Print the number of packets captured for each interface until we're killed. */
|
||||
int
|
||||
capture_opts_print_statistics(gboolean machine_readable)
|
||||
{
|
||||
GList *if_list, *if_entry, *stat_list = NULL, *stat_entry;
|
||||
if_info_t *if_info;
|
||||
if_stat_t *if_stat;
|
||||
int err;
|
||||
gchar *err_str;
|
||||
pcap_t *pch;
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
struct pcap_stat ps;
|
||||
|
||||
if_list = get_interface_list(&err, &err_str);
|
||||
if (if_list == NULL) {
|
||||
switch (err) {
|
||||
case CANT_GET_INTERFACE_LIST:
|
||||
cmdarg_err("%s", err_str);
|
||||
g_free(err_str);
|
||||
break;
|
||||
|
||||
case NO_INTERFACES_FOUND:
|
||||
cmdarg_err("There are no interfaces on which a capture can be done");
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
for (if_entry = g_list_first(if_list); if_entry != NULL; if_entry = g_list_next(if_entry)) {
|
||||
if_info = if_entry->data;
|
||||
pch = pcap_open_live(if_info->name, MIN_PACKET_SIZE, 0, 0, errbuf);
|
||||
|
||||
if (pch) {
|
||||
if_stat = g_malloc(sizeof(if_stat_t));
|
||||
if_stat->name = g_strdup(if_info->name);
|
||||
if_stat->pch = pch;
|
||||
stat_list = g_list_append(stat_list, if_stat);
|
||||
}
|
||||
}
|
||||
|
||||
if (!stat_list) {
|
||||
cmdarg_err("There are no interfaces on which a capture can be done");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (!machine_readable) {
|
||||
printf("%-15s %10s %10s\n", "Interface", "Received",
|
||||
"Dropped");
|
||||
}
|
||||
|
||||
while (1) { /* XXX - Add signal handling? */
|
||||
for (stat_entry = g_list_first(stat_list); stat_entry != NULL; stat_entry = g_list_next(stat_entry)) {
|
||||
if_stat = stat_entry->data;
|
||||
pcap_stats(if_stat->pch, &ps);
|
||||
|
||||
if (!machine_readable) {
|
||||
printf("%-15s %10d %10d\n", if_stat->name,
|
||||
ps.ps_recv, ps.ps_drop);
|
||||
} else {
|
||||
printf("%s\t%d\t%d\n", if_stat->name,
|
||||
ps.ps_recv, ps.ps_drop);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
Sleep(1 * 1000);
|
||||
#else
|
||||
sleep(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* XXX - Not reached. Should we look for 'q' in stdin? */
|
||||
for (stat_entry = g_list_first(stat_list); stat_entry != NULL; stat_entry = g_list_next(stat_entry)) {
|
||||
if_stat = stat_entry->data;
|
||||
pcap_close(if_stat->pch);
|
||||
g_free(if_stat->name);
|
||||
g_free(if_stat);
|
||||
}
|
||||
g_list_free(stat_list);
|
||||
free_interface_list(if_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void capture_opts_trim_snaplen(capture_options *capture_opts, int snaplen_min)
|
||||
{
|
||||
|
|
|
@ -120,6 +120,10 @@ capture_opts_list_link_layer_types(capture_options *capture_opts, gboolean machi
|
|||
extern int
|
||||
capture_opts_list_interfaces(gboolean machine_readable);
|
||||
|
||||
/* print interface statistics */
|
||||
extern int
|
||||
capture_opts_print_statistics(gboolean machine_readable);
|
||||
|
||||
/* trim the snaplen entry */
|
||||
extern void
|
||||
capture_opts_trim_snaplen(capture_options *capture_opts, int snaplen_min);
|
||||
|
|
212
capture_sync.c
212
capture_sync.c
|
@ -37,10 +37,13 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <fcntl.h>
|
||||
#include "epan/unicode-utils.h"
|
||||
#endif
|
||||
|
||||
|
@ -523,15 +526,17 @@ sync_pipe_start(capture_options *capture_opts) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Run dumpcap with the supplied arguments. On success, msg points to
|
||||
* a buffer containing the dumpcap output and returns 0. On failure, msg
|
||||
* points to the error message returned by dumpcap, and returns dumpcap's
|
||||
* exit value. In either case, msg must be freed with g_free().
|
||||
* Open dumpcap with the supplied arguments. On success, msg points to
|
||||
* a buffer containing the dumpcap output and returns 0. read_fd and
|
||||
* fork_child point to the pipe's file descriptor and child PID/handle,
|
||||
* respectively. On failure, msg points to the error message returned by
|
||||
* dumpcap, and returns dumpcap's exit value. In either case, msg must be
|
||||
* freed with g_free().
|
||||
*/
|
||||
/* XXX - This duplicates a lot of code in sync_pipe_start() */
|
||||
#define PIPE_BUF_SIZE 5120
|
||||
static int
|
||||
sync_pipe_run_command(const char** argv, gchar **msg) {
|
||||
sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar **msg) {
|
||||
#ifdef _WIN32
|
||||
HANDLE sync_pipe_read; /* pipe used to send messages from child to parent */
|
||||
HANDLE sync_pipe_write; /* pipe used to send messages from parent to child */
|
||||
|
@ -545,12 +550,9 @@ sync_pipe_run_command(const char** argv, gchar **msg) {
|
|||
int sync_pipe[2]; /* pipe used to send messages from child to parent */
|
||||
enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */
|
||||
#endif
|
||||
int fork_child = -1, fork_child_status;
|
||||
int sync_pipe_read_fd = -1;
|
||||
GString *msg_buf = NULL;
|
||||
gchar buf[PIPE_BUF_SIZE+1];
|
||||
int count;
|
||||
|
||||
*fork_child = -1;
|
||||
*read_fd = -1;
|
||||
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_run_command");
|
||||
|
||||
if (!msg) {
|
||||
|
@ -613,12 +615,12 @@ sync_pipe_run_command(const char** argv, gchar **msg) {
|
|||
g_free( (gpointer) argv);
|
||||
return CANT_RUN_DUMPCAP;
|
||||
}
|
||||
fork_child = (int) pi.hProcess;
|
||||
*fork_child = (int) pi.hProcess;
|
||||
g_string_free(args, TRUE);
|
||||
|
||||
/* associate the operating system filehandle to a C run-time file handle */
|
||||
/* (good file handle infos at: http://www.flounder.com/handles.htm) */
|
||||
sync_pipe_read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY);
|
||||
*read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY);
|
||||
|
||||
#else /* _WIN32 */
|
||||
if (pipe(sync_pipe) < 0) {
|
||||
|
@ -629,7 +631,7 @@ sync_pipe_run_command(const char** argv, gchar **msg) {
|
|||
return CANT_RUN_DUMPCAP;
|
||||
}
|
||||
|
||||
if ((fork_child = fork()) == 0) {
|
||||
if ((*fork_child = fork()) == 0) {
|
||||
/*
|
||||
* Child process - run dumpcap with the right arguments to make
|
||||
* it just capture with the specified capture parameters
|
||||
|
@ -643,7 +645,7 @@ sync_pipe_run_command(const char** argv, gchar **msg) {
|
|||
return CANT_RUN_DUMPCAP;
|
||||
}
|
||||
|
||||
sync_pipe_read_fd = sync_pipe[PIPE_READ];
|
||||
*read_fd = sync_pipe[PIPE_READ];
|
||||
#endif
|
||||
|
||||
g_free( (gpointer) argv[0]); /* exename */
|
||||
|
@ -662,31 +664,34 @@ sync_pipe_run_command(const char** argv, gchar **msg) {
|
|||
eth_close(sync_pipe[PIPE_WRITE]);
|
||||
#endif
|
||||
|
||||
if (fork_child == -1) {
|
||||
if (*fork_child == -1) {
|
||||
/* We couldn't even create the child process. */
|
||||
*msg = g_strdup_printf("Couldn't create child process: %s", strerror(errno));
|
||||
eth_close(sync_pipe_read_fd);
|
||||
eth_close(*read_fd);
|
||||
return CANT_RUN_DUMPCAP;
|
||||
}
|
||||
|
||||
/* we might wait for a moment till child is ready, so update screen now */
|
||||
main_window_update();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We were able to set up to read dumpcap's output. Do so and
|
||||
return its exit value. */
|
||||
msg_buf = g_string_new("");
|
||||
while ((count = eth_read(sync_pipe_read_fd, buf, PIPE_BUF_SIZE)) > 0) {
|
||||
buf[count] = '\0';
|
||||
g_string_append(msg_buf, buf);
|
||||
}
|
||||
static int
|
||||
#ifdef _WIN32
|
||||
sync_pipe_close_command(int *read_fd, int *fork_child, gchar **msg) {
|
||||
#else
|
||||
sync_pipe_close_command(int *read_fd, gchar **msg) {
|
||||
#endif
|
||||
int fork_child_status;
|
||||
|
||||
eth_close(sync_pipe_read_fd);
|
||||
eth_close(*read_fd);
|
||||
|
||||
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_open: wait till child closed");
|
||||
|
||||
#ifdef _WIN32
|
||||
if (_cwait(&fork_child_status, fork_child, _WAIT_CHILD) == -1) {
|
||||
g_string_free(msg_buf, TRUE);
|
||||
/* XXX - Should we signal the child somehow? */
|
||||
sync_pipe_kill(*fork_child);
|
||||
if (_cwait(&fork_child_status, *fork_child, _WAIT_CHILD) == -1) {
|
||||
*msg = g_strdup_printf("Child capture process stopped unexpectedly "
|
||||
"(errno:%u)", errno);
|
||||
return CANT_RUN_DUMPCAP;
|
||||
|
@ -697,7 +702,6 @@ sync_pipe_run_command(const char** argv, gchar **msg) {
|
|||
/* The child exited. */
|
||||
fork_child_status = WEXITSTATUS(fork_child_status);
|
||||
} else {
|
||||
g_string_free(msg_buf, TRUE);
|
||||
if (WIFSTOPPED(fork_child_status)) {
|
||||
/* It stopped, rather than exiting. "Should not happen." */
|
||||
*msg = g_strdup_printf("Child capture process stopped: %s",
|
||||
|
@ -716,16 +720,56 @@ sync_pipe_run_command(const char** argv, gchar **msg) {
|
|||
return CANT_RUN_DUMPCAP;
|
||||
}
|
||||
} else {
|
||||
g_string_free(msg_buf, TRUE);
|
||||
*msg = g_strdup_printf("Child capture process stopped unexpectedly "
|
||||
"(errno:%u)", errno);
|
||||
return CANT_RUN_DUMPCAP;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run dumpcap with the supplied arguments. On success, msg points to
|
||||
* a buffer containing the dumpcap output and returns 0. On failure, msg
|
||||
* points to the error message returned by dumpcap, and returns dumpcap's
|
||||
* exit value. In either case, msg must be freed with g_free().
|
||||
*/
|
||||
/* XXX - This duplicates a lot of code in sync_pipe_start() */
|
||||
#define PIPE_BUF_SIZE 5120
|
||||
static int
|
||||
sync_pipe_run_command(const char** argv, gchar **msg) {
|
||||
int sync_pipe_read_fd, fork_child, ret;
|
||||
gchar buf[PIPE_BUF_SIZE+1];
|
||||
GString *msg_buf = NULL;
|
||||
int count;
|
||||
|
||||
ret = sync_pipe_open_command(argv, &sync_pipe_read_fd, &fork_child, msg);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* We were able to set up to read dumpcap's output. Do so and
|
||||
return its exit value. */
|
||||
msg_buf = g_string_new("");
|
||||
while ((count = eth_read(sync_pipe_read_fd, buf, PIPE_BUF_SIZE)) > 0) {
|
||||
buf[count] = '\0';
|
||||
g_string_append(msg_buf, buf);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
ret = sync_pipe_close_command(&sync_pipe_read_fd, &fork_child, msg);
|
||||
#else
|
||||
ret = sync_pipe_close_command(&sync_pipe_read_fd, msg);
|
||||
#endif
|
||||
|
||||
if (ret) {
|
||||
g_string_free(msg_buf, TRUE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*msg = msg_buf->str;
|
||||
g_string_free(msg_buf, FALSE);
|
||||
return fork_child_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -804,6 +848,52 @@ sync_linktype_list_open(gchar *ifname, gchar **msg) {
|
|||
return sync_pipe_run_command(argv, msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start getting interface statistics using dumpcap. On success, read_fd
|
||||
* contains the file descriptor for the pipe's stdout, msg is unchanged,
|
||||
* and zero is returned. On failure, msg will point to an error message
|
||||
* that must be g_free()d and a nonzero error value will be returned.
|
||||
*/
|
||||
int
|
||||
sync_interface_stats_open(int *read_fd, int *fork_child, gchar **msg) {
|
||||
int argc;
|
||||
const char **argv;
|
||||
|
||||
if (!msg) {
|
||||
/* We can't return anything */
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_linktype_list_open");
|
||||
|
||||
argv = init_pipe_args(&argc);
|
||||
|
||||
if (!argv) {
|
||||
*msg = g_strdup_printf("We don't know where to find dumpcap.");
|
||||
return CANT_RUN_DUMPCAP;
|
||||
}
|
||||
|
||||
/* Ask for the linktype list */
|
||||
argv = sync_pipe_add_arg(argv, &argc, "-S");
|
||||
argv = sync_pipe_add_arg(argv, &argc, "-M");
|
||||
|
||||
/* dumpcap should be running in capture child mode (hidden feature) */
|
||||
#ifndef DEBUG_CHILD
|
||||
argv = sync_pipe_add_arg(argv, &argc, "-Z");
|
||||
#endif
|
||||
|
||||
return sync_pipe_open_command(argv, read_fd, fork_child, msg);
|
||||
}
|
||||
|
||||
/* Close down the stats process */
|
||||
int
|
||||
sync_interface_stats_close(int *read_fd, int *fork_child, gchar **msg) {
|
||||
#ifdef _WIN32
|
||||
return sync_pipe_close_command(read_fd, fork_child, msg);
|
||||
#else
|
||||
return sync_pipe_close_command(read_fd, msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* read a number of bytes from a pipe */
|
||||
/* (blocks until enough bytes read or an error occurs) */
|
||||
|
@ -812,7 +902,6 @@ pipe_read_bytes(int pipe, char *bytes, int required) {
|
|||
int newly;
|
||||
int offset = 0;
|
||||
|
||||
|
||||
while(required) {
|
||||
newly = read(pipe, &bytes[offset], required);
|
||||
if (newly == 0) {
|
||||
|
@ -835,6 +924,67 @@ pipe_read_bytes(int pipe, char *bytes, int required) {
|
|||
return offset;
|
||||
}
|
||||
|
||||
static gboolean pipe_data_available(int pipe) {
|
||||
#ifdef _WIN32 /* PeekNamedPipe */
|
||||
HANDLE hPipe = (HANDLE) _get_osfhandle(pipe);
|
||||
DWORD bytes_avail;
|
||||
|
||||
if (hPipe == INVALID_HANDLE_VALUE)
|
||||
return FALSE;
|
||||
|
||||
if (! PeekNamedPipe(hPipe, NULL, 0, NULL, &bytes_avail, NULL))
|
||||
return FALSE;
|
||||
|
||||
if (bytes_avail > 0)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
#else /* select */
|
||||
fd_set rfds;
|
||||
struct timeval timeout;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(pipe, &rfds);
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
if (select(pipe+1, &rfds, NULL, NULL, &timeout) > 0)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Read a line from a pipe, similar to fgets */
|
||||
int
|
||||
sync_pipe_gets_nonblock(int pipe, char *bytes, int max) {
|
||||
int newly;
|
||||
int offset = -1;
|
||||
|
||||
while(offset < max - 1) {
|
||||
offset++;
|
||||
if (! pipe_data_available(pipe))
|
||||
break;
|
||||
newly = read(pipe, &bytes[offset], 1);
|
||||
if (newly == 0) {
|
||||
/* EOF - not necessarily an error */
|
||||
break;
|
||||
} else if (newly < 0) {
|
||||
/* error */
|
||||
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
|
||||
"read from pipe %d: error(%u): %s", pipe, errno, strerror(errno));
|
||||
return newly;
|
||||
} else if (bytes[offset] == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset >= 0)
|
||||
bytes[offset] = '\0';
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
/* convert header values (indicator and 4-byte length) */
|
||||
static void
|
||||
pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) {
|
||||
|
|
|
@ -71,4 +71,17 @@ sync_interface_list_open(gchar **msg);
|
|||
extern int
|
||||
sync_linktype_list_open(gchar *ifname, gchar **msg);
|
||||
|
||||
/** Start getting interface statistics using dumpcap. */
|
||||
extern int
|
||||
sync_interface_stats_open(int *read_fd, int *fork_child, gchar **msg);
|
||||
|
||||
/** Stop gathering statistics. */
|
||||
extern int
|
||||
sync_interface_stats_close(int *read_fd, int *fork_child, gchar **msg);
|
||||
|
||||
/** Read a line from a pipe, similar to fgets. Non-blocking. */
|
||||
extern int
|
||||
sync_pipe_gets_nonblock(int pipe, char *bytes, int max);
|
||||
|
||||
|
||||
#endif /* capture_sync.h */
|
||||
|
|
|
@ -1,37 +1,66 @@
|
|||
Here's a brief list of information that might be useful to anyone
|
||||
distributing a software package containing Wireshark:
|
||||
The following guidelines should be followed by anyone distributing a software
|
||||
package containing Wireshark:
|
||||
|
||||
1. The canonical location for every Wireshark source release is
|
||||
1. URLs.
|
||||
|
||||
1.1. Wireshark web site.
|
||||
|
||||
The Wireshark web site URL is http://www.wireshark.org/ .
|
||||
|
||||
1.2. Wireshark releases.
|
||||
|
||||
The canonical location for every Wireshark source release is
|
||||
|
||||
http://www.wireshark.org/download/src/all-versions/, e.g.
|
||||
|
||||
http://www.wireshark.org/download/src/all-versions/wireshark-0.99.55.tar.bz2
|
||||
|
||||
If your packaging system downloads a copy of the Wireshark sources,
|
||||
use this location. Don't use http://www.wireshark.org/download/src.
|
||||
If your packaging system downloads a copy of the Wireshark sources, use
|
||||
this location. Don't use http://www.wireshark.org/download/src.
|
||||
|
||||
2. The Wireshark web site URL is http://www.wireshark.org/ .
|
||||
1.3. Artwork.
|
||||
|
||||
3. Wireshark is released under the GNU General Public License. Make sure
|
||||
your package complies with this license, or we send in the marmots.
|
||||
Logo and icon artwork can be found in the "image" directory in the
|
||||
distribution. This is available online at
|
||||
|
||||
4. Wireshark and the "fin" logo are registered trademarks of Gerald
|
||||
Combs.
|
||||
http://anonsvn.wireshark.org/wireshark/trunk/image/
|
||||
|
||||
5. Custom version information can be added by creating a file called
|
||||
"version.conf". See make-version.pl for details. If your package
|
||||
contains significant changes we recommend that you use this to
|
||||
differentiate it from official Wireshark releases.
|
||||
2. Licensing.
|
||||
|
||||
6. The SVN version corresponding to each release is in svnversion.h.
|
||||
It's defined as a string. If you need a numeric definition, let
|
||||
us know.
|
||||
Wireshark is released under the GNU General Public License. Make sure
|
||||
your package complies with this license, or we send in the marmots.
|
||||
|
||||
7. Wireshark icons, logos, and other artwork can be found in the
|
||||
"image" directory of the Wireshark sources.
|
||||
3. Privileges.
|
||||
|
||||
In versions up to and including 0.99.6, it was necessary to run
|
||||
Wireshark with elevated privileges in order to be able to capture
|
||||
traffic. With version 0.99.7, all function calls that require elevated
|
||||
privliges have been moved out of the GUI.
|
||||
|
||||
WIRESHARK CONTAINS OVER ONE POINT FIVE MILLION LINES OF SOURCE CODE. DO
|
||||
NOT RUN THEM AS ROOT.
|
||||
|
||||
4. Customization.
|
||||
|
||||
Custom version information can be added by creating a file called
|
||||
"version.conf". See make-version.pl for details. If your package
|
||||
contains significant changes we recommend that you use this to
|
||||
differentiate it from official Wireshark releases.
|
||||
|
||||
4.1. Source-level version detection.
|
||||
|
||||
The SVN version corresponding to each release is in svnversion.h. It's
|
||||
defined as a string. If you need a numeric definition, let us know.
|
||||
|
||||
5. Trademarks.
|
||||
|
||||
Wireshark and the "fin" logo are registered trademarks of Gerald Combs.
|
||||
|
||||
6. Spelling.
|
||||
|
||||
Wireshark is spelled with a capital "W", and with everything else lower
|
||||
case. E.g., "WireShark" is incorrect.
|
||||
|
||||
8. Wireshark is spelled with a capital "W", and with everything else
|
||||
lower case. E.g., "WireShark" is incorrect.
|
||||
|
||||
If you have a question not addressed here, send it to
|
||||
wireshark-dev@wireshark.org.
|
||||
|
|
21
dumpcap.c
21
dumpcap.c
|
@ -115,7 +115,8 @@ print_usage(gboolean print_ver) {
|
|||
fprintf(output, " -y <link type> link layer type (def: first appropriate)\n");
|
||||
fprintf(output, " -D print list of interfaces and exit\n");
|
||||
fprintf(output, " -L print list of link-layer types of iface and exit\n");
|
||||
fprintf(output, " -M for -D and -L, produce machine-readable output\n");
|
||||
fprintf(output, " -S print statistics for each interface once every second\n");
|
||||
fprintf(output, " -M for -D, -L, and -S produce machine-readable output\n");
|
||||
fprintf(output, "\n");
|
||||
fprintf(output, "Stop conditions:\n");
|
||||
fprintf(output, " -c <packet count> stop after n packets (def: infinite)\n");
|
||||
|
@ -249,9 +250,10 @@ main(int argc, char *argv[])
|
|||
gboolean list_interfaces = FALSE;
|
||||
gboolean list_link_layer_types = FALSE;
|
||||
gboolean machine_readable = FALSE;
|
||||
int status;
|
||||
gboolean print_statistics = FALSE;
|
||||
int status, run_once_args = 0;
|
||||
|
||||
#define OPTSTRING_INIT "a:b:c:Df:hi:LMps:vw:y:Z"
|
||||
#define OPTSTRING_INIT "a:b:c:Df:hi:LMpSs:vw:y:Z"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define OPTSTRING_WIN32 "B:"
|
||||
|
@ -369,9 +371,15 @@ main(int argc, char *argv[])
|
|||
/*** all non capture option specific ***/
|
||||
case 'D': /* Print a list of capture devices and exit */
|
||||
list_interfaces = TRUE;
|
||||
run_once_args++;
|
||||
break;
|
||||
case 'L': /* Print list of link-layer types and exit */
|
||||
list_link_layer_types = TRUE;
|
||||
run_once_args++;
|
||||
break;
|
||||
case 'S': /* Print interface statistics once a second */
|
||||
print_statistics = TRUE;
|
||||
run_once_args++;
|
||||
break;
|
||||
case 'M': /* For -D and -L, print machine-readable output */
|
||||
machine_readable = TRUE;
|
||||
|
@ -406,8 +414,8 @@ main(int argc, char *argv[])
|
|||
exit_main(1);
|
||||
}
|
||||
|
||||
if (list_interfaces && list_link_layer_types) {
|
||||
cmdarg_err("Only one of -D or -L may be supplied.");
|
||||
if (run_once_args > 1) {
|
||||
cmdarg_err("Only one of -D, -L, or -S may be supplied.");
|
||||
exit_main(1);
|
||||
} else if (list_link_layer_types) {
|
||||
/* We're supposed to list the link-layer types for an interface;
|
||||
|
@ -452,6 +460,9 @@ main(int argc, char *argv[])
|
|||
} else if (list_link_layer_types) {
|
||||
status = capture_opts_list_link_layer_types(capture_opts, machine_readable);
|
||||
exit_main(status);
|
||||
} else if (print_statistics) {
|
||||
status = capture_opts_print_statistics(machine_readable);
|
||||
exit_main(status);
|
||||
}
|
||||
|
||||
capture_opts_trim_snaplen(capture_opts, MIN_PACKET_SIZE);
|
||||
|
|
|
@ -107,7 +107,6 @@ GList *if_list;
|
|||
|
||||
/* the "runtime" data of one interface */
|
||||
typedef struct if_dlg_data_s {
|
||||
pcap_t *pch;
|
||||
GtkWidget *device_lb;
|
||||
GtkWidget *descr_lb;
|
||||
GtkWidget *ip_lb;
|
||||
|
@ -123,8 +122,6 @@ typedef struct if_dlg_data_s {
|
|||
if_info_t if_info;
|
||||
} if_dlg_data_t;
|
||||
|
||||
void update_if(if_dlg_data_t *if_dlg_data);
|
||||
|
||||
|
||||
/* start capture button was pressed */
|
||||
static void
|
||||
|
@ -185,48 +182,9 @@ capture_details_cb(GtkWidget *details_bt _U_, gpointer if_data)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* open a single interface */
|
||||
static void
|
||||
open_if(gchar *name, if_dlg_data_t *if_dlg_data)
|
||||
{
|
||||
gchar open_err_str[PCAP_ERRBUF_SIZE];
|
||||
|
||||
/*
|
||||
* XXX - on systems with BPF, the number of BPF devices limits the
|
||||
* number of devices on which you can capture simultaneously.
|
||||
*
|
||||
* This means that
|
||||
*
|
||||
* 1) this might fail if you run out of BPF devices
|
||||
*
|
||||
* and
|
||||
*
|
||||
* 2) opening every interface could leave too few BPF devices
|
||||
* for *other* programs.
|
||||
*
|
||||
* It also means the system could end up getting a lot of traffic
|
||||
* that it has to pass through the networking stack and capture
|
||||
* mechanism, so opening all the devices and presenting packet
|
||||
* counts might not always be a good idea.
|
||||
*/
|
||||
if_dlg_data->pch = pcap_open_live(name,
|
||||
MIN_PACKET_SIZE,
|
||||
capture_opts->promisc_mode, CAP_READ_TIMEOUT,
|
||||
open_err_str);
|
||||
|
||||
if (if_dlg_data->pch != NULL) {
|
||||
update_if(if_dlg_data);
|
||||
} else {
|
||||
printf("open_if: %s\n", open_err_str);
|
||||
gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), "error");
|
||||
gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), "error");
|
||||
}
|
||||
}
|
||||
|
||||
/* update a single interface */
|
||||
void
|
||||
update_if(if_dlg_data_t *if_dlg_data)
|
||||
update_if(if_dlg_data_t *if_dlg_data, if_stat_cache_t *sc)
|
||||
{
|
||||
struct pcap_stat stats;
|
||||
gchar *str;
|
||||
|
@ -243,8 +201,8 @@ update_if(if_dlg_data_t *if_dlg_data)
|
|||
* (Note also that some versions of libpcap, on some versions of UN*X,
|
||||
* have the same bug.)
|
||||
*/
|
||||
if (if_dlg_data->pch) {
|
||||
if(pcap_stats(if_dlg_data->pch, &stats) >= 0) {
|
||||
if (sc) {
|
||||
if(capture_stats(sc, if_dlg_data->device, &stats)) {
|
||||
#ifdef _WIN32
|
||||
diff = stats.ps_recv - if_dlg_data->last_packets;
|
||||
if_dlg_data->last_packets = stats.ps_recv;
|
||||
|
@ -269,31 +227,20 @@ update_if(if_dlg_data_t *if_dlg_data)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* close a single interface */
|
||||
static void
|
||||
close_if(if_dlg_data_t *if_dlg_data)
|
||||
{
|
||||
if(if_dlg_data->pch)
|
||||
pcap_close(if_dlg_data->pch);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* update all interfaces */
|
||||
static gboolean
|
||||
update_all(gpointer data)
|
||||
{
|
||||
GList *curr;
|
||||
int ifs;
|
||||
|
||||
if_stat_cache_t *sc = data;
|
||||
|
||||
if(!cap_if_w) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for(ifs = 0; (curr = g_list_nth(data, ifs)); ifs++) {
|
||||
update_if(curr->data);
|
||||
for(ifs = 0; (curr = g_list_nth(if_data, ifs)); ifs++) {
|
||||
update_if(curr->data, sc);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
@ -322,17 +269,15 @@ set_capture_if_dialog_for_capture_in_progress(gboolean capture_in_progress)
|
|||
|
||||
/* the window was closed, cleanup things */
|
||||
static void
|
||||
capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
|
||||
capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data)
|
||||
{
|
||||
GList *curr;
|
||||
int ifs;
|
||||
if_stat_cache_t *sc = user_data;
|
||||
|
||||
gtk_timeout_remove(timer_id);
|
||||
|
||||
for(ifs = 0; (curr = g_list_nth(if_data, ifs)); ifs++) {
|
||||
if_dlg_data_t *if_dlg_data = curr->data;
|
||||
|
||||
close_if(if_dlg_data);
|
||||
g_free(curr->data);
|
||||
}
|
||||
|
||||
|
@ -343,8 +288,10 @@ capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
|
|||
/* Note that we no longer have a "Capture Options" dialog box. */
|
||||
cap_if_w = NULL;
|
||||
|
||||
capture_stat_stop(sc);
|
||||
|
||||
#ifdef HAVE_AIRPCAP
|
||||
airpcap_set_toolbar_stop_capture(airpcap_if_active);
|
||||
airpcap_set_toolbar_stop_capture(airpcap_if_active);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -438,6 +385,7 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
|
|||
if_addr_t *ip_addr;
|
||||
GString *if_tool_str = g_string_new("");
|
||||
gchar *tmp_str;
|
||||
if_stat_cache_t *sc;
|
||||
|
||||
if (cap_if_w != NULL) {
|
||||
/* There's already a "Capture Interfaces" dialog box; reactivate it. */
|
||||
|
@ -567,6 +515,10 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
|
|||
gtk_widget_size_request(stop_bt, &requisition);
|
||||
height += requisition.height + 15;
|
||||
|
||||
/* Start gathering statistics (using dumpcap) */
|
||||
sc = capture_stat_start(if_list);
|
||||
|
||||
/* List the interfaces */
|
||||
for(ifs = 0; (curr = g_list_nth(if_list, ifs)); ifs++) {
|
||||
g_string_assign(if_tool_str, "");
|
||||
if_info = curr->data;
|
||||
|
@ -674,8 +626,6 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
|
|||
gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->details_bt, 8, 9, row, row+1);
|
||||
#endif
|
||||
|
||||
open_if(if_info->name, if_dlg_data);
|
||||
|
||||
if_data = g_list_append(if_data, if_dlg_data);
|
||||
|
||||
row++;
|
||||
|
@ -713,7 +663,7 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
|
|||
gtk_widget_grab_default(close_bt);
|
||||
|
||||
SIGNAL_CONNECT(cap_if_w, "delete_event", window_delete_event_cb, NULL);
|
||||
SIGNAL_CONNECT(cap_if_w, "destroy", capture_if_destroy_cb, NULL);
|
||||
SIGNAL_CONNECT(cap_if_w, "destroy", capture_if_destroy_cb, sc);
|
||||
|
||||
gtk_widget_show_all(cap_if_w);
|
||||
window_present(cap_if_w);
|
||||
|
@ -721,7 +671,7 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
|
|||
set_capture_if_dialog_for_capture_in_progress(is_capture_in_progress());
|
||||
|
||||
/* update the interface list every 1000ms */
|
||||
timer_id = gtk_timeout_add(1000, update_all, if_data);
|
||||
timer_id = gtk_timeout_add(1000, update_all, sc);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue