X-Git-Url: http://git.samba.org/samba.git/?p=obnox%2Fwireshark%2Fwip.git;a=blobdiff_plain;f=dumpcap.c;h=21e342c1ffb33067d5881ea7c431eefb2404bcf7;hp=fd949d4684ed39e257c19a58780e26d2cebd9ba3;hb=d9bd6b6f4469d6c090a07eb657392529f53f4833;hpb=4fc161b16e881da975412729935adc8cd963c60c diff --git a/dumpcap.c b/dumpcap.c index fd949d4684..21e342c1ff 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -59,9 +59,7 @@ #include #include -#ifdef HAVE_GETOPT_H -#include -#else +#ifndef HAVE_GETOPT #include "wsutil/wsgetopt.h" #endif @@ -81,11 +79,13 @@ #include "version_info.h" #include "capture-pcap-util.h" +#ifdef _WIN32 +#include "capture-wpcap.h" +#endif /* _WIN32 */ #include "pcapio.h" #ifdef _WIN32 -#include #include "capture-wpcap.h" #include #endif @@ -123,15 +123,23 @@ /**#define DEBUG_DUMPCAP**/ /**#define DEBUG_CHILD_DUMPCAP**/ +#ifdef _WIN32 +#ifdef DEBUG_DUMPCAP +#include /* _getch() */ +#endif +#endif + #ifdef DEBUG_CHILD_DUMPCAP FILE *debug_log; /* for logging debug messages to */ /* a file if DEBUG_CHILD_DUMPCAP */ /* is defined */ #endif -#ifdef _WIN32 -#define USE_THREADS -#endif +static GAsyncQueue *pcap_queue; +static gint64 pcap_queue_bytes; +static gint64 pcap_queue_packets; +static gint64 pcap_queue_byte_limit = 1024 * 1024; +static gint64 pcap_queue_packet_limit = 1000; static gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Wireshark capture child */ #ifdef _WIN32 @@ -140,11 +148,6 @@ static HANDLE sig_pipe_handle = NULL; static gboolean signal_pipe_check_running(void); #endif -#ifdef USE_THREADS -static GAsyncQueue *cap_pipe_pending_q, *cap_pipe_done_q; -static GMutex *cap_pipe_read_mtx; -#endif - #ifdef SIGINFO static gboolean infodelay; /* if TRUE, don't print capture info in SIGINFO handler */ static gboolean infoprint; /* if TRUE, print capture info after clearing infodelay */ @@ -212,24 +215,18 @@ typedef enum { INITFILTER_OTHER_ERROR } initfilter_status_t; -typedef struct _loop_data { - /* common */ - gboolean go; /* TRUE as long as we're supposed to keep capturing */ - int err; /* if non-zero, error seen while capturing */ - gint packet_count; /* Number of packets we have already captured */ - gint packet_max; /* Number of packets we're supposed to capture - 0 means infinite */ - gint inpkts_to_sync_pipe; /* Packets not already send out to the sync_pipe */ -#ifdef SIGINFO - gboolean report_packet_count; /* Set by SIGINFO handler; print packet count */ -#endif - - /* pcap "input file" */ - pcap_t *pcap_h; /* pcap handle */ - gboolean pcap_err; /* TRUE if error from pcap */ +typedef struct _pcap_options { + guint32 received; + guint32 dropped; + pcap_t *pcap_h; #ifdef MUST_DO_SELECT int pcap_fd; /* pcap file descriptor */ #endif - + gboolean pcap_err; + guint interface_id; + GThread *tid; + int snaplen; + int linktype; /* capture pipe (unix only "input file") */ gboolean from_cap_pipe; /* TRUE if we are capturing data from a capture pipe */ struct pcap_hdr cap_pipe_hdr; /* Pcap header when capturing from a pipe */ @@ -241,9 +238,9 @@ typedef struct _loop_data { #endif gboolean cap_pipe_modified; /* TRUE if data in the pipe uses modified pcap headers */ gboolean cap_pipe_byte_swapped; /* TRUE if data in the pipe is byte swapped */ -#ifdef USE_THREADS +#if defined(_WIN32) char * cap_pipe_buf; /* Pointer to the data buffer we read into */ -#endif /* USE_THREADS */ +#endif int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */ int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */ enum { @@ -253,16 +250,36 @@ typedef struct _loop_data { STATE_READ_DATA } cap_pipe_state; enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } cap_pipe_err; +#if defined(_WIN32) + GMutex *cap_pipe_read_mtx; + GAsyncQueue *cap_pipe_pending_q, *cap_pipe_done_q; +#endif +} pcap_options; +typedef struct _loop_data { + /* common */ + gboolean go; /* TRUE as long as we're supposed to keep capturing */ + int err; /* if non-zero, error seen while capturing */ + gint packet_count; /* Number of packets we have already captured */ + gint packet_max; /* Number of packets we're supposed to capture - 0 means infinite */ + gint inpkts_to_sync_pipe; /* Packets not already send out to the sync_pipe */ +#ifdef SIGINFO + gboolean report_packet_count; /* Set by SIGINFO handler; print packet count */ +#endif + GArray *pcaps; /* output file(s) */ FILE *pdh; int save_file_fd; - int linktype; - int file_snaplen; long bytes_written; guint32 autostop_files; } loop_data; +typedef struct _pcap_queue_element { + pcap_options *pcap_opts; + struct pcap_pkthdr phdr; + u_char *pd; +} pcap_queue_element; + /* * Standard secondary message for unexpected errors. */ @@ -304,12 +321,13 @@ static gboolean need_timeout_workaround; * WaitForSingleObject. If it's less than 1000 WaitForSingleObject * will return immediately. */ -#ifndef USE_THREADS -#define PIPE_READ_TIMEOUT 250000 -#else +#if defined(_WIN32) #define PIPE_READ_TIMEOUT 100000 +#else +#define PIPE_READ_TIMEOUT 250000 #endif -static const char *cap_pipe_err_str; + +#define WRITER_THREAD_TIMEOUT 100000 /* usecs */ static void console_log_handler(const char *log_domain, GLogLevelFlags log_level, @@ -318,9 +336,12 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level, /* capture related options */ static capture_options global_capture_opts; static gboolean quiet = FALSE; +static gboolean use_threads = FALSE; -static void capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, - const u_char *pd); +static void capture_loop_write_packet_cb(u_char *pcap_opts_p, const struct pcap_pkthdr *phdr, + const u_char *pd); +static void capture_loop_queue_packet_cb(u_char *pcap_opts_p, const struct pcap_pkthdr *phdr, + const u_char *pd); static void capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, int err, gboolean is_close); @@ -328,9 +349,9 @@ static void WS_MSVC_NORETURN exit_main(int err) G_GNUC_NORETURN; static void report_new_capture_file(const char *filename); static void report_packet_count(int packet_count); -static void report_packet_drops(guint32 drops); +static void report_packet_drops(guint32 received, guint32 drops, gchar *name); static void report_capture_error(const char *error_msg, const char *secondary_error_msg); -static void report_cfilter_error(const char *cfilter, const char *errmsg); +static void report_cfilter_error(capture_options *capture_opts, guint i, const char *errmsg); #define MSG_MAX_LENGTH 4096 @@ -343,7 +364,7 @@ print_usage(gboolean print_ver) output = stdout; fprintf(output, "Dumpcap " VERSION "%s\n" - "Capture network packets and dump them into a libpcap file.\n" + "Capture network packets and dump them into a pcapng file.\n" "See http://www.wireshark.org for more information.\n", wireshark_svnversion); } else { @@ -368,11 +389,11 @@ print_usage(gboolean print_ver) #ifdef HAVE_BPF_IMAGE fprintf(output, " -d print generated BPF code for capture filter\n"); #endif - fprintf(output, " -S print statistics for each interface once every second\n"); + fprintf(output, " -S print statistics for each interface once per second\n"); fprintf(output, " -M for -D, -L, and -S, produce machine-readable output\n"); fprintf(output, "\n"); #ifdef HAVE_PCAP_REMOTE - fprintf(output, "\nRPCAP options:\n"); + fprintf(output, "RPCAP options:\n"); fprintf(output, " -r don't ignore own RPCAP traffic in capture\n"); fprintf(output, " -u use UDP for RPCAP data transfer\n"); fprintf(output, " -A : use RPCAP password authentication\n"); @@ -394,15 +415,17 @@ print_usage(gboolean print_ver) fprintf(output, " -b ... duration:NUM - switch to next file after NUM secs\n"); fprintf(output, " filesize:NUM - switch to next file after NUM KB\n"); fprintf(output, " files:NUM - ringbuffer: replace after NUM files\n"); - fprintf(output, " -n use pcapng format instead of pcap\n"); - /*fprintf(output, "\n");*/ + fprintf(output, " -n use pcapng format instead of pcap (default)\n"); + fprintf(output, " -P use libpcap format instead of pcapng\n"); + fprintf(output, "\n"); fprintf(output, "Miscellaneous:\n"); + fprintf(output, " -t use a separate thread per interface\n"); fprintf(output, " -q don't report packet capture counts\n"); fprintf(output, " -v print version information and exit\n"); fprintf(output, " -h display this help and exit\n"); fprintf(output, "\n"); - fprintf(output, "Example: dumpcap -i eth0 -a duration:60 -w output.pcap\n"); - fprintf(output, "\"Capture network packets from interface eth0 until 60s passed into output.pcap\"\n"); + fprintf(output, "Example: dumpcap -i eth0 -a duration:60 -w output.pcapng\n"); + fprintf(output, "\"Capture network packets from interface eth0 until 60s passed into output.pcapng\"\n"); fprintf(output, "\n"); fprintf(output, "Use Ctrl-C to stop capturing at any time.\n"); } @@ -514,7 +537,7 @@ relinquish_all_capabilities(void) cap_t caps = cap_init(); /* all capabilities initialized to off */ print_caps("Pre-clear"); if (cap_set_proc(caps)) { - cmdarg_err("cap_set_proc() fail return: %s", strerror(errno)); + cmdarg_err("cap_set_proc() fail return: %s", g_strerror(errno)); } print_caps("Post-clear"); cap_free(caps); @@ -522,14 +545,14 @@ relinquish_all_capabilities(void) #endif static pcap_t * -open_capture_device(capture_options *capture_opts, +open_capture_device(interface_options *interface_opts, char (*open_err_str)[PCAP_ERRBUF_SIZE]) { pcap_t *pcap_h; #ifdef HAVE_PCAP_CREATE int err; #endif -#ifdef HAVE_PCAP_REMOTE +#if defined(HAVE_PCAP_OPEN) && defined(HAVE_PCAP_REMOTE) struct pcap_rmtauth auth; #endif @@ -537,28 +560,33 @@ open_capture_device(capture_options *capture_opts, Some versions of libpcap may put warnings into the error buffer if they succeed; to tell if that's happened, we have to clear the error buffer, and check if it's still a null string. */ + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Entering open_capture_device()."); (*open_err_str)[0] = '\0'; -#ifdef HAVE_PCAP_OPEN +#if defined(HAVE_PCAP_OPEN) && defined(HAVE_PCAP_REMOTE) /* * If we're opening a remote device, use pcap_open(); that's currently * the only open routine that supports remote devices. */ - if (strncmp (capture_opts->iface, "rpcap://", 8) == 0) { - auth.type = capture_opts->auth_type == CAPTURE_AUTH_PWD ? + if (strncmp (interface_opts->name, "rpcap://", 8) == 0) { + auth.type = interface_opts->auth_type == CAPTURE_AUTH_PWD ? RPCAP_RMTAUTH_PWD : RPCAP_RMTAUTH_NULL; - auth.username = capture_opts->auth_username; - auth.password = capture_opts->auth_password; + auth.username = interface_opts->auth_username; + auth.password = interface_opts->auth_password; - pcap_h = pcap_open(capture_opts->iface, - capture_opts->has_snaplen ? capture_opts->snaplen : - WTAP_MAX_PACKET_SIZE, + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_open() using name %s, snaplen %d, promisc_mode %d, datatx_udp %d, nocap_rpcap %d.", + interface_opts->name, interface_opts->snaplen, interface_opts->promisc_mode, + interface_opts->datatx_udp, interface_opts->nocap_rpcap); + pcap_h = pcap_open(interface_opts->name, interface_opts->snaplen, /* flags */ - (capture_opts->promisc_mode ? PCAP_OPENFLAG_PROMISCUOUS : 0) | - (capture_opts->datatx_udp ? PCAP_OPENFLAG_DATATX_UDP : 0) | - (capture_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0), + (interface_opts->promisc_mode ? PCAP_OPENFLAG_PROMISCUOUS : 0) | + (interface_opts->datatx_udp ? PCAP_OPENFLAG_DATATX_UDP : 0) | + (interface_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0), CAP_READ_TIMEOUT, &auth, *open_err_str); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_open() returned %p.", (void *)pcap_h); } else -#endif /* HAVE_PCAP_OPEN */ +#endif { /* * If we're not opening a remote device, use pcap_create() and @@ -566,18 +594,32 @@ open_capture_device(capture_options *capture_opts, * size, otherwise use pcap_open_live(). */ #ifdef HAVE_PCAP_CREATE - pcap_h = pcap_create(capture_opts->iface, *open_err_str); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_create() using %s.", interface_opts->name); + pcap_h = pcap_create(interface_opts->name, *open_err_str); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_create() returned %p.", (void *)pcap_h); if (pcap_h != NULL) { - pcap_set_snaplen(pcap_h, capture_opts->has_snaplen ? capture_opts->snaplen : WTAP_MAX_PACKET_SIZE); - pcap_set_promisc(pcap_h, capture_opts->promisc_mode); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_set_snaplen() with snaplen %d.", interface_opts->snaplen); + pcap_set_snaplen(pcap_h, interface_opts->snaplen); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_set_snaplen() with promisc_mode %d.", interface_opts->promisc_mode); + pcap_set_promisc(pcap_h, interface_opts->promisc_mode); pcap_set_timeout(pcap_h, CAP_READ_TIMEOUT); - if (capture_opts->buffer_size > 1) { - pcap_set_buffer_size(pcap_h, capture_opts->buffer_size * 1024 * 1024); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "buffersize %d.", interface_opts->buffer_size); + if (interface_opts->buffer_size > 1) { + pcap_set_buffer_size(pcap_h, interface_opts->buffer_size * 1024 * 1024); } - if (capture_opts->monitor_mode) + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "monitor_mode %d.", interface_opts->monitor_mode); + if (interface_opts->monitor_mode) pcap_set_rfmon(pcap_h, 1); err = pcap_activate(pcap_h); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_activate() returned %d.", err); if (err < 0) { /* Failed to activate, set to NULL */ if (err == PCAP_ERROR) @@ -589,25 +631,17 @@ open_capture_device(capture_options *capture_opts, } } #else - pcap_h = pcap_open_live(capture_opts->iface, - capture_opts->has_snaplen ? capture_opts->snaplen : - WTAP_MAX_PACKET_SIZE, - capture_opts->promisc_mode, CAP_READ_TIMEOUT, + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_open_live() calling using name %s, snaplen %d, promisc_mode %d.", + interface_opts->name, interface_opts->snaplen, interface_opts->promisc_mode); + pcap_h = pcap_open_live(interface_opts->name, interface_opts->snaplen, + interface_opts->promisc_mode, CAP_READ_TIMEOUT, *open_err_str); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_open_live() returned %p.", (void *)pcap_h); #endif } - - /* If not using libcap: we now can now set euid/egid to ruid/rgid */ - /* to remove any suid privileges. */ - /* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities */ - /* (euid/egid have already previously been set to ruid/rgid. */ - /* (See comment in main() for details) */ -#ifndef HAVE_LIBCAP - relinquish_special_privs_perm(); -#else - relinquish_all_capabilities(); -#endif - + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "open_capture_device %s : %s", pcap_h ? "SUCCESS" : "FAILURE", interface_opts->name); return pcap_h; } @@ -646,41 +680,65 @@ get_capture_device_open_failure_messages(const char *open_err_str, libpcap_warn = ""; g_snprintf(errmsg, (gulong) errmsg_len, "The capture session could not be initiated (%s).", open_err_str); -#ifndef _WIN32 +#ifdef _WIN32 + if (!has_wpcap) { + g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, + "\n" + "In order to capture packets, WinPcap must be installed; see\n" + "\n" + " http://www.winpcap.org/\n" + "\n" + "or the mirror at\n" + "\n" + " http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/\n" + "\n" + "or the mirror at\n" + "\n" + " http://winpcap.cs.pu.edu.tw/\n" + "\n" + "for a downloadable version of WinPcap and for instructions on how to install\n" + "WinPcap."); + } else { + g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, + "\n" + "Please check that \"%s\" is the proper interface.\n" + "\n" + "\n" + "Help can be found at:\n" + "\n" + " http://wiki.wireshark.org/WinPcap\n" + " http://wiki.wireshark.org/CaptureSetup\n", + iface); + } +#else g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, "Please check to make sure you have sufficient permissions, and that you have " "the proper interface or pipe specified.%s", libpcap_warn); -#else - g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, - "\n" - "Please check that \"%s\" is the proper interface.\n" - "\n" - "\n" - "Help can be found at:\n" - "\n" - " http://wiki.wireshark.org/WinPcap\n" - " http://wiki.wireshark.org/CaptureSetup\n", - iface); #endif /* _WIN32 */ } /* Set the data link type on a pcap. */ static gboolean -set_pcap_linktype(pcap_t *pcap_h, capture_options *capture_opts, +set_pcap_linktype(pcap_t *pcap_h, int linktype, +#ifdef HAVE_PCAP_SET_DATALINK + char *name _U_, +#else + char *name, +#endif char *errmsg, size_t errmsg_len, char *secondary_errmsg, size_t secondary_errmsg_len) { char *set_linktype_err_str; - if (capture_opts->linktype == -1) + if (linktype == -1) return TRUE; /* just use the default */ #ifdef HAVE_PCAP_SET_DATALINK - if (pcap_set_datalink(pcap_h, capture_opts->linktype) == 0) + if (pcap_set_datalink(pcap_h, linktype) == 0) return TRUE; /* no error */ set_linktype_err_str = pcap_geterr(pcap_h); #else /* Let them set it to the type it is; reject any other request. */ - if (get_pcap_linktype(pcap_h, capture_opts->iface) == capture_opts->linktype) + if (get_pcap_linktype(pcap_h, name) == linktype) return TRUE; /* no error */ set_linktype_err_str = "That DLT isn't one of the DLTs supported by this device"; @@ -700,7 +758,7 @@ set_pcap_linktype(pcap_t *pcap_h, capture_options *capture_opts, static gboolean compile_capture_filter(const char *iface, pcap_t *pcap_h, - struct bpf_program *fcode, char *cfilter) + struct bpf_program *fcode, const char *cfilter) { bpf_u_int32 netnum, netmask; gchar lookup_net_err_str[PCAP_ERRBUF_SIZE]; @@ -719,7 +777,13 @@ compile_capture_filter(const char *iface, pcap_t *pcap_h, "Warning: Couldn't obtain netmask info (%s).", lookup_net_err_str);*/ netmask = 0; } - if (pcap_compile(pcap_h, fcode, cfilter, 1, netmask) < 0) + + /* + * Sigh. Older versions of libpcap don't properly declare the + * third argument to pcap_compile() as a const pointer. Cast + * away the warning. + */ + if (pcap_compile(pcap_h, fcode, (char *)cfilter, 1, netmask) < 0) return FALSE; return TRUE; } @@ -728,6 +792,7 @@ compile_capture_filter(const char *iface, pcap_t *pcap_h, static gboolean show_filter_code(capture_options *capture_opts) { + interface_options interface_opts; pcap_t *pcap_h; gchar open_err_str[PCAP_ERRBUF_SIZE]; char errmsg[MSG_MAX_LENGTH+1]; @@ -735,47 +800,61 @@ show_filter_code(capture_options *capture_opts) struct bpf_program fcode; struct bpf_insn *insn; u_int i; + guint j; + + for (j = 0; j < capture_opts->ifaces->len; j++) { + interface_opts = g_array_index(capture_opts->ifaces, interface_options, j); + pcap_h = open_capture_device(&interface_opts, &open_err_str); + if (pcap_h == NULL) { + /* Open failed; get messages */ + get_capture_device_open_failure_messages(open_err_str, + interface_opts.name, + errmsg, sizeof errmsg, + secondary_errmsg, + sizeof secondary_errmsg); + /* And report them */ + report_capture_error(errmsg, secondary_errmsg); + return FALSE; + } - pcap_h = open_capture_device(capture_opts, &open_err_str); - if (pcap_h == NULL) { - /* Open failed; get messages */ - get_capture_device_open_failure_messages(open_err_str, - capture_opts->iface, - errmsg, sizeof errmsg, - secondary_errmsg, - sizeof secondary_errmsg); - /* And report them */ - report_capture_error(errmsg, secondary_errmsg); - return FALSE; - } + /* Set the link-layer type. */ + if (!set_pcap_linktype(pcap_h, interface_opts.linktype, interface_opts.name, + errmsg, sizeof errmsg, + secondary_errmsg, sizeof secondary_errmsg)) { + pcap_close(pcap_h); + report_capture_error(errmsg, secondary_errmsg); + return FALSE; + } - /* Set the link-layer type. */ - if (!set_pcap_linktype(pcap_h, capture_opts, errmsg, sizeof errmsg, - secondary_errmsg, sizeof secondary_errmsg)) { + /* OK, try to compile the capture filter. */ + if (!compile_capture_filter(interface_opts.name, pcap_h, &fcode, + interface_opts.cfilter)) { + pcap_close(pcap_h); + report_cfilter_error(capture_opts, j, errmsg); + return FALSE; + } pcap_close(pcap_h); - report_capture_error(errmsg, secondary_errmsg); - return FALSE; - } - /* OK, try to compile the capture filter. */ - if (!compile_capture_filter(capture_opts->iface, pcap_h, &fcode, - capture_opts->cfilter)) { - pcap_close(pcap_h); - report_cfilter_error(capture_opts->cfilter, errmsg); - return FALSE; - } - pcap_close(pcap_h); + /* Now print the filter code. */ + insn = fcode.bf_insns; + for (i = 0; i < fcode.bf_len; insn++, i++) + printf("%s\n", bpf_image(insn, i)); + } + /* If not using libcap: we now can now set euid/egid to ruid/rgid */ + /* to remove any suid privileges. */ + /* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities */ + /* (euid/egid have already previously been set to ruid/rgid. */ + /* (See comment in main() for details) */ +#ifndef HAVE_LIBCAP + relinquish_special_privs_perm(); +#else + relinquish_all_capabilities(); +#endif if (capture_child) { /* Let our parent know we succeeded. */ pipe_write_block(2, SP_SUCCESS, NULL); } - - /* Now print the filter code. */ - insn = fcode.bf_insns; - - for (i = 0; i < fcode.bf_len; insn++, i++) - printf("%s\n", bpf_image(insn, i)); return TRUE; } #endif @@ -1223,6 +1302,7 @@ print_statistics_loop(gboolean machine_readable) if (if_list == NULL) { switch (err) { case CANT_GET_INTERFACE_LIST: + case DONT_HAVE_PCAP: cmdarg_err("%s", err_str); g_free(err_str); break; @@ -1281,8 +1361,7 @@ print_statistics_loop(gboolean machine_readable) } } #ifdef _WIN32 - if (! global_ld.from_cap_pipe) - Sleep(1 * 1000); + Sleep(1 * 1000); #else sleep(1); #endif @@ -1353,17 +1432,13 @@ capture_cleanup_handler(int signum _U_) static void -report_capture_count(void) +report_capture_count(gboolean reportit) { /* Don't print this if we're a capture child. */ - if (!capture_child) { - if (quiet) { - /* Report the count only if we aren't printing a packet count - as packets arrive. */ - fprintf(stderr, "Packets captured: %u\n", global_ld.packet_count); - /* stderr could be line buffered */ - fflush(stderr); - } + if (!capture_child && reportit) { + fprintf(stderr, "\rPackets captured: %u\n", global_ld.packet_count); + /* stderr could be line buffered */ + fflush(stderr); } } @@ -1372,7 +1447,7 @@ report_capture_count(void) static void report_counts_for_siginfo(void) { - report_capture_count(); + report_capture_count(quiet); infoprint = FALSE; /* we just reported it */ } @@ -1437,14 +1512,14 @@ relinquish_privs_except_capture(void) print_caps("Pre drop, pre set"); if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) { - cmdarg_err("prctl() fail return: %s", strerror(errno)); + cmdarg_err("prctl() fail return: %s", g_strerror(errno)); } cap_set_flag(caps, CAP_PERMITTED, cl_len, cap_list, CAP_SET); cap_set_flag(caps, CAP_INHERITABLE, cl_len, cap_list, CAP_SET); if (cap_set_proc(caps)) { - cmdarg_err("cap_set_proc() fail return: %s", strerror(errno)); + cmdarg_err("cap_set_proc() fail return: %s", g_strerror(errno)); } print_caps("Pre drop, post set"); @@ -1453,7 +1528,7 @@ relinquish_privs_except_capture(void) print_caps("Post drop, pre set"); cap_set_flag(caps, CAP_EFFECTIVE, cl_len, cap_list, CAP_SET); if (cap_set_proc(caps)) { - cmdarg_err("cap_set_proc() fail return: %s", strerror(errno)); + cmdarg_err("cap_set_proc() fail return: %s", g_strerror(errno)); } print_caps("Post drop, post set"); @@ -1494,7 +1569,7 @@ cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcapr } } -#ifdef USE_THREADS +#if defined(_WIN32) /* * Thread function that reads from a pipe and pushes the data * to the main application thread. @@ -1511,9 +1586,9 @@ cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcapr * we can let cap_pipe_read run independently, queuing up multiple reads * for the main thread (and possibly get rid of cap_pipe_read_mtx). */ -static void *cap_pipe_read(void *ld_ptr) +static void *cap_pipe_read(void *arg) { - loop_data *ld = (loop_data *)ld_ptr; + pcap_options *pcap_opts; int bytes_read; #ifdef _WIN32 BOOL res; @@ -1522,17 +1597,18 @@ static void *cap_pipe_read(void *ld_ptr) int b; #endif /* _WIN32 */ - while (ld->cap_pipe_err == PIPOK) { - g_async_queue_pop(cap_pipe_pending_q); /* Wait for our cue (ahem) from the main thread */ - g_mutex_lock(cap_pipe_read_mtx); + pcap_opts = (pcap_options *)arg; + while (pcap_opts->cap_pipe_err == PIPOK) { + g_async_queue_pop(pcap_opts->cap_pipe_pending_q); /* Wait for our cue (ahem) from the main thread */ + g_mutex_lock(pcap_opts->cap_pipe_read_mtx); bytes_read = 0; - while (bytes_read < (int) ld->cap_pipe_bytes_to_read) { + while (bytes_read < (int) pcap_opts->cap_pipe_bytes_to_read) { #ifdef _WIN32 /* If we try to use read() on a named pipe on Windows with partial * data it appears to return EOF. */ - res = ReadFile(ld->cap_pipe_h, ld->cap_pipe_buf+bytes_read, - ld->cap_pipe_bytes_to_read - bytes_read, + res = ReadFile(pcap_opts->cap_pipe_h, pcap_opts->cap_pipe_buf+bytes_read, + pcap_opts->cap_pipe_bytes_to_read - bytes_read, &b, NULL); bytes_read += b; @@ -1541,28 +1617,28 @@ static void *cap_pipe_read(void *ld_ptr) if (last_err == ERROR_MORE_DATA) { continue; } else if (last_err == ERROR_HANDLE_EOF || last_err == ERROR_BROKEN_PIPE || last_err == ERROR_PIPE_NOT_CONNECTED) { - ld->cap_pipe_err = PIPEOF; + pcap_opts->cap_pipe_err = PIPEOF; bytes_read = 0; break; } - ld->cap_pipe_err = PIPERR; + pcap_opts->cap_pipe_err = PIPERR; bytes_read = -1; break; - } else if (b == 0 && ld->cap_pipe_bytes_to_read > 0) { - ld->cap_pipe_err = PIPEOF; + } else if (b == 0 && pcap_opts->cap_pipe_bytes_to_read > 0) { + pcap_opts->cap_pipe_err = PIPEOF; bytes_read = 0; break; } #else /* _WIN32 */ - b = read(ld->cap_pipe_fd, ld->cap_pipe_buf+bytes_read, - ld->cap_pipe_bytes_to_read - bytes_read); + b = read(pcap_opts->cap_pipe_fd, pcap_opts->cap_pipe_buf+bytes_read, + pcap_opts->cap_pipe_bytes_to_read - bytes_read); if (b <= 0) { if (b == 0) { - ld->cap_pipe_err = PIPEOF; + pcap_opts->cap_pipe_err = PIPEOF; bytes_read = 0; break; } else { - ld->cap_pipe_err = PIPERR; + pcap_opts->cap_pipe_err = PIPERR; bytes_read = -1; break; } @@ -1571,30 +1647,27 @@ static void *cap_pipe_read(void *ld_ptr) } #endif /*_WIN32 */ } - ld->cap_pipe_bytes_read = bytes_read; - if (ld->cap_pipe_bytes_read >= ld->cap_pipe_bytes_to_read) { - g_async_queue_push(cap_pipe_done_q, ld->cap_pipe_buf); /* Any non-NULL value will do */ + pcap_opts->cap_pipe_bytes_read = bytes_read; + if (pcap_opts->cap_pipe_bytes_read >= pcap_opts->cap_pipe_bytes_to_read) { + g_async_queue_push(pcap_opts->cap_pipe_done_q, pcap_opts->cap_pipe_buf); /* Any non-NULL value will do */ } - g_mutex_unlock(cap_pipe_read_mtx); + g_mutex_unlock(pcap_opts->cap_pipe_read_mtx); } return NULL; } -#endif /* USE_THREADS */ +#endif +#if !defined(_WIN32) || defined(MUST_DO_SELECT) /* Provide select() functionality for a single file descriptor * on UNIX/POSIX. Windows uses cap_pipe_read via a thread. * - * Returns the same values as select. If an error is returned, - * the string cap_pipe_err_str should be used instead of errno. + * Returns the same values as select. */ static int cap_pipe_select(int pipe_fd) { fd_set rfds; struct timeval timeout; - int sel_ret; - - cap_pipe_err_str = "Unknown error"; FD_ZERO(&rfds); FD_SET(pipe_fd, &rfds); @@ -1602,11 +1675,9 @@ cap_pipe_select(int pipe_fd) timeout.tv_sec = PIPE_READ_TIMEOUT / 1000000; timeout.tv_usec = PIPE_READ_TIMEOUT % 1000000; - sel_ret = select(pipe_fd+1, &rfds, NULL, NULL, &timeout); - if (sel_ret < 0) - cap_pipe_err_str = strerror(errno); - return sel_ret; + return select(pipe_fd+1, &rfds, NULL, NULL, &timeout); } +#endif /* Mimic pcap_open_live() for pipe captures @@ -1617,28 +1688,32 @@ cap_pipe_select(int pipe_fd) * 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 void -cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, +cap_pipe_open_live(char *pipename, + pcap_options *pcap_opts, + struct pcap_hdr *hdr, char *errmsg, int errmsgl) { #ifndef _WIN32 - struct stat pipe_stat; + ws_statb64 pipe_stat; struct sockaddr_un sa; - int sel_ret; int b; - unsigned int bytes_read; int fd; #else /* _WIN32 */ #if 1 char *pncopy, *pos; wchar_t *err_str; #endif +#endif +#ifndef _WIN32 + int sel_ret; + unsigned int bytes_read; #endif guint32 magic = 0; #ifndef _WIN32 - ld->cap_pipe_fd = -1; + pcap_opts->cap_pipe_fd = -1; #else - ld->cap_pipe_h = INVALID_HANDLE_VALUE; + pcap_opts->cap_pipe_h = INVALID_HANDLE_VALUE; #endif g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: %s", pipename); @@ -1650,18 +1725,18 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, #ifndef _WIN32 fd = 0; /* read from stdin */ #else /* _WIN32 */ - ld->cap_pipe_h = GetStdHandle(STD_INPUT_HANDLE); + pcap_opts->cap_pipe_h = GetStdHandle(STD_INPUT_HANDLE); #endif /* _WIN32 */ } else { #ifndef _WIN32 - if (ws_stat(pipename, &pipe_stat) < 0) { + if (ws_stat64(pipename, &pipe_stat) < 0) { if (errno == ENOENT || errno == ENOTDIR) - ld->cap_pipe_err = PIPNEXIST; + pcap_opts->cap_pipe_err = PIPNEXIST; else { g_snprintf(errmsg, errmsgl, "The capture session could not be initiated " - "due to error getting information on pipe/socket: %s", strerror(errno)); - ld->cap_pipe_err = PIPERR; + "due to error getting information on pipe/socket: %s", g_strerror(errno)); + pcap_opts->cap_pipe_err = PIPERR; } return; } @@ -1670,8 +1745,8 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, if (fd == -1) { g_snprintf(errmsg, errmsgl, "The capture session could not be initiated " - "due to error on pipe open: %s", strerror(errno)); - ld->cap_pipe_err = PIPERR; + "due to error on pipe open: %s", g_strerror(errno)); + pcap_opts->cap_pipe_err = PIPERR; return; } } else if (S_ISSOCK(pipe_stat.st_mode)) { @@ -1679,8 +1754,8 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, if (fd == -1) { g_snprintf(errmsg, errmsgl, "The capture session could not be initiated " - "due to error on socket create: %s", strerror(errno)); - ld->cap_pipe_err = PIPERR; + "due to error on socket create: %s", g_strerror(errno)); + pcap_opts->cap_pipe_err = PIPERR; return; } sa.sun_family = AF_UNIX; @@ -1712,15 +1787,15 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, g_snprintf(errmsg, errmsgl, "The capture session coud not be initiated " "due to error on socket connect: Path name too long"); - ld->cap_pipe_err = PIPERR; + pcap_opts->cap_pipe_err = PIPERR; return; } b = connect(fd, (struct sockaddr *)&sa, sizeof sa); if (b == -1) { g_snprintf(errmsg, errmsgl, "The capture session coud not be initiated " - "due to error on socket connect: %s", strerror(errno)); - ld->cap_pipe_err = PIPERR; + "due to error on socket connect: %s", g_strerror(errno)); + pcap_opts->cap_pipe_err = PIPERR; return; } } else { @@ -1729,13 +1804,13 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, * Assume the user specified an interface on a system where * interfaces are in /dev. Pretend we haven't seen it. */ - ld->cap_pipe_err = PIPNEXIST; + pcap_opts->cap_pipe_err = PIPNEXIST; } else { g_snprintf(errmsg, errmsgl, "The capture session could not be initiated because\n" "\"%s\" is neither an interface nor a socket nor a pipe", pipename); - ld->cap_pipe_err = PIPERR; + pcap_opts->cap_pipe_err = PIPERR; } return; } @@ -1757,16 +1832,16 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, g_snprintf(errmsg, errmsgl, "The capture session could not be initiated because\n" "\"%s\" is neither an interface nor a pipe", pipename); - ld->cap_pipe_err = PIPNEXIST; + pcap_opts->cap_pipe_err = PIPNEXIST; return; } /* Wait for the pipe to appear */ while (1) { - ld->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL, - OPEN_EXISTING, 0, NULL); + pcap_opts->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL, + OPEN_EXISTING, 0, NULL); - if (ld->cap_pipe_h != INVALID_HANDLE_VALUE) + if (pcap_opts->cap_pipe_h != INVALID_HANDLE_VALUE) break; if (GetLastError() != ERROR_PIPE_BUSY) { @@ -1777,7 +1852,7 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, "due to error on pipe open: %s (error %d)", pipename, utf_16to8(err_str), GetLastError()); LocalFree(err_str); - ld->cap_pipe_err = PIPERR; + pcap_opts->cap_pipe_err = PIPERR; return; } @@ -1789,23 +1864,23 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, "pipe open: %s (error %d)", pipename, utf_16to8(err_str), GetLastError()); LocalFree(err_str); - ld->cap_pipe_err = PIPERR; + pcap_opts->cap_pipe_err = PIPERR; return; } } #endif /* _WIN32 */ } - ld->from_cap_pipe = TRUE; + pcap_opts->from_cap_pipe = TRUE; -#ifndef USE_THREADS +#ifndef _WIN32 /* read the pcap header */ bytes_read = 0; while (bytes_read < sizeof magic) { sel_ret = cap_pipe_select(fd); if (sel_ret < 0) { g_snprintf(errmsg, errmsgl, - "Unexpected error from select: %s", strerror(errno)); + "Unexpected error from select: %s", g_strerror(errno)); goto error; } else if (sel_ret > 0) { b = read(fd, ((char *)&magic)+bytes_read, sizeof magic-bytes_read); @@ -1814,58 +1889,58 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open"); else g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s", - strerror(errno)); + g_strerror(errno)); goto error; } bytes_read += b; } } -#else /* USE_THREADS */ - g_thread_create(&cap_pipe_read, ld, FALSE, NULL); +#else + g_thread_create(&cap_pipe_read, pcap_opts, FALSE, NULL); - ld->cap_pipe_buf = (char *) &magic; - ld->cap_pipe_bytes_read = 0; - ld->cap_pipe_bytes_to_read = sizeof(magic); + pcap_opts->cap_pipe_buf = (char *) &magic; + pcap_opts->cap_pipe_bytes_read = 0; + pcap_opts->cap_pipe_bytes_to_read = sizeof(magic); /* We don't have to worry about cap_pipe_read_mtx here */ - g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf); - g_async_queue_pop(cap_pipe_done_q); - if (ld->cap_pipe_bytes_read <= 0) { - if (ld->cap_pipe_bytes_read == 0) + g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); + g_async_queue_pop(pcap_opts->cap_pipe_done_q); + if (pcap_opts->cap_pipe_bytes_read <= 0) { + if (pcap_opts->cap_pipe_bytes_read == 0) g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open"); else g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s", - strerror(errno)); + g_strerror(errno)); goto error; } -#endif /* USE_THREADS */ +#endif 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->cap_pipe_byte_swapped = FALSE; - ld->cap_pipe_modified = FALSE; + pcap_opts->cap_pipe_byte_swapped = FALSE; + pcap_opts->cap_pipe_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->cap_pipe_byte_swapped = FALSE; - ld->cap_pipe_modified = TRUE; + pcap_opts->cap_pipe_byte_swapped = FALSE; + pcap_opts->cap_pipe_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->cap_pipe_byte_swapped = TRUE; - ld->cap_pipe_modified = FALSE; + pcap_opts->cap_pipe_byte_swapped = TRUE; + pcap_opts->cap_pipe_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->cap_pipe_byte_swapped = TRUE; - ld->cap_pipe_modified = TRUE; + pcap_opts->cap_pipe_byte_swapped = TRUE; + pcap_opts->cap_pipe_modified = TRUE; break; default: /* Not a "libpcap" type we know about. */ @@ -1873,14 +1948,14 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, goto error; } -#ifndef USE_THREADS +#ifndef _WIN32 /* Read the rest of the header */ bytes_read = 0; while (bytes_read < sizeof(struct pcap_hdr)) { sel_ret = cap_pipe_select(fd); if (sel_ret < 0) { g_snprintf(errmsg, errmsgl, - "Unexpected error from select: %s", strerror(errno)); + "Unexpected error from select: %s", g_strerror(errno)); goto error; } else if (sel_ret > 0) { b = read(fd, ((char *)hdr)+bytes_read, @@ -1890,55 +1965,55 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, g_snprintf(errmsg, errmsgl, "End of file on pipe header during open"); else g_snprintf(errmsg, errmsgl, "Error on pipe header during open: %s", - strerror(errno)); + g_strerror(errno)); goto error; } bytes_read += b; } } -#else /* USE_THREADS */ - ld->cap_pipe_buf = (char *) hdr; - ld->cap_pipe_bytes_read = 0; - ld->cap_pipe_bytes_to_read = sizeof(struct pcap_hdr); - g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf); - g_async_queue_pop(cap_pipe_done_q); - if (ld->cap_pipe_bytes_read <= 0) { - if (ld->cap_pipe_bytes_read == 0) +#else + pcap_opts->cap_pipe_buf = (char *) hdr; + pcap_opts->cap_pipe_bytes_read = 0; + pcap_opts->cap_pipe_bytes_to_read = sizeof(struct pcap_hdr); + g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); + g_async_queue_pop(pcap_opts->cap_pipe_done_q); + if (pcap_opts->cap_pipe_bytes_read <= 0) { + if (pcap_opts->cap_pipe_bytes_read == 0) g_snprintf(errmsg, errmsgl, "End of file on pipe header during open"); else g_snprintf(errmsg, errmsgl, "Error on pipe header header during open: %s", - strerror(errno)); + g_strerror(errno)); goto error; } -#endif /* USE_THREADS */ +#endif - if (ld->cap_pipe_byte_swapped) { + if (pcap_opts->cap_pipe_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); } - ld->linktype = hdr->network; + pcap_opts->linktype = hdr->network; if (hdr->version_major < 2) { g_snprintf(errmsg, errmsgl, "Unable to read old libpcap format"); goto error; } - ld->cap_pipe_state = STATE_EXPECT_REC_HDR; - ld->cap_pipe_err = PIPOK; + pcap_opts->cap_pipe_state = STATE_EXPECT_REC_HDR; + pcap_opts->cap_pipe_err = PIPOK; #ifndef _WIN32 - ld->cap_pipe_fd = fd; + pcap_opts->cap_pipe_fd = fd; #endif return; error: g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg); - ld->cap_pipe_err = PIPERR; + pcap_opts->cap_pipe_err = PIPERR; #ifndef _WIN32 ws_close(fd); - ld->cap_pipe_fd = -1; + pcap_opts->cap_pipe_fd = -1; #endif return; @@ -1948,12 +2023,12 @@ error: /* We read one record from the pipe, take care of byte order in the record * header, write the record to the capture file, and update capture statistics. */ static int -cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) +cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *errmsg, int errmsgl) { struct pcap_pkthdr phdr; enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR, PD_ERR } result; -#ifdef USE_THREADS +#ifdef _WIN32 GTimeVal wait_time; gpointer q_status; #else @@ -1967,30 +2042,30 @@ cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_dispatch"); #endif - switch (ld->cap_pipe_state) { + switch (pcap_opts->cap_pipe_state) { case STATE_EXPECT_REC_HDR: -#ifdef USE_THREADS - if (g_mutex_trylock(cap_pipe_read_mtx)) { +#ifdef _WIN32 + if (g_mutex_trylock(pcap_opts->cap_pipe_read_mtx)) { #endif - ld->cap_pipe_state = STATE_READ_REC_HDR; - ld->cap_pipe_bytes_to_read = ld->cap_pipe_modified ? + pcap_opts->cap_pipe_state = STATE_READ_REC_HDR; + pcap_opts->cap_pipe_bytes_to_read = pcap_opts->cap_pipe_modified ? sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr); - ld->cap_pipe_bytes_read = 0; + pcap_opts->cap_pipe_bytes_read = 0; -#ifdef USE_THREADS - ld->cap_pipe_buf = (char *) &ld->cap_pipe_rechdr; - g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf); - g_mutex_unlock(cap_pipe_read_mtx); +#ifdef _WIN32 + pcap_opts->cap_pipe_buf = (char *) &pcap_opts->cap_pipe_rechdr; + g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); + g_mutex_unlock(pcap_opts->cap_pipe_read_mtx); } #endif /* Fall through */ case STATE_READ_REC_HDR: -#ifndef USE_THREADS - b = read(ld->cap_pipe_fd, ((char *)&ld->cap_pipe_rechdr)+ld->cap_pipe_bytes_read, - ld->cap_pipe_bytes_to_read - ld->cap_pipe_bytes_read); +#ifndef _WIN32 + b = read(pcap_opts->cap_pipe_fd, ((char *)&pcap_opts->cap_pipe_rechdr)+pcap_opts->cap_pipe_bytes_read, + pcap_opts->cap_pipe_bytes_to_read - pcap_opts->cap_pipe_bytes_read); if (b <= 0) { if (b == 0) result = PD_PIPE_EOF; @@ -1998,48 +2073,48 @@ cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) result = PD_PIPE_ERR; break; } - ld->cap_pipe_bytes_read += b; -#else /* USE_THREADS */ + pcap_opts->cap_pipe_bytes_read += b; +#else g_get_current_time(&wait_time); g_time_val_add(&wait_time, PIPE_READ_TIMEOUT); - q_status = g_async_queue_timed_pop(cap_pipe_done_q, &wait_time); - if (ld->cap_pipe_err == PIPEOF) { + q_status = g_async_queue_timed_pop(pcap_opts->cap_pipe_done_q, &wait_time); + if (pcap_opts->cap_pipe_err == PIPEOF) { result = PD_PIPE_EOF; break; - } else if (ld->cap_pipe_err == PIPERR) { + } else if (pcap_opts->cap_pipe_err == PIPERR) { result = PD_PIPE_ERR; break; } if (!q_status) { return 0; } -#endif /* USE_THREADS */ - if ((ld->cap_pipe_bytes_read) < ld->cap_pipe_bytes_to_read) +#endif + if ((pcap_opts->cap_pipe_bytes_read) < pcap_opts->cap_pipe_bytes_to_read) return 0; result = PD_REC_HDR_READ; break; case STATE_EXPECT_DATA: -#ifdef USE_THREADS - if (g_mutex_trylock(cap_pipe_read_mtx)) { +#ifdef _WIN32 + if (g_mutex_trylock(pcap_opts->cap_pipe_read_mtx)) { #endif - ld->cap_pipe_state = STATE_READ_DATA; - ld->cap_pipe_bytes_to_read = ld->cap_pipe_rechdr.hdr.incl_len; - ld->cap_pipe_bytes_read = 0; + pcap_opts->cap_pipe_state = STATE_READ_DATA; + pcap_opts->cap_pipe_bytes_to_read = pcap_opts->cap_pipe_rechdr.hdr.incl_len; + pcap_opts->cap_pipe_bytes_read = 0; -#ifdef USE_THREADS - ld->cap_pipe_buf = (char *) data; - g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf); - g_mutex_unlock(cap_pipe_read_mtx); +#ifdef _WIN32 + pcap_opts->cap_pipe_buf = (char *) data; + g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); + g_mutex_unlock(pcap_opts->cap_pipe_read_mtx); } #endif /* Fall through */ case STATE_READ_DATA: -#ifndef USE_THREADS - b = read(ld->cap_pipe_fd, data+ld->cap_pipe_bytes_read, - ld->cap_pipe_bytes_to_read - ld->cap_pipe_bytes_read); +#ifndef _WIN32 + b = read(pcap_opts->cap_pipe_fd, data+pcap_opts->cap_pipe_bytes_read, + pcap_opts->cap_pipe_bytes_to_read - pcap_opts->cap_pipe_bytes_read); if (b <= 0) { if (b == 0) result = PD_PIPE_EOF; @@ -2047,23 +2122,23 @@ cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) result = PD_PIPE_ERR; break; } - ld->cap_pipe_bytes_read += b; -#else /* USE_THREADS */ + pcap_opts->cap_pipe_bytes_read += b; +#else g_get_current_time(&wait_time); g_time_val_add(&wait_time, PIPE_READ_TIMEOUT); - q_status = g_async_queue_timed_pop(cap_pipe_done_q, &wait_time); - if (ld->cap_pipe_err == PIPEOF) { + q_status = g_async_queue_timed_pop(pcap_opts->cap_pipe_done_q, &wait_time); + if (pcap_opts->cap_pipe_err == PIPEOF) { result = PD_PIPE_EOF; break; - } else if (ld->cap_pipe_err == PIPERR) { + } else if (pcap_opts->cap_pipe_err == PIPERR) { result = PD_PIPE_ERR; break; } if (!q_status) { return 0; } -#endif /* USE_THREADS */ - if ((ld->cap_pipe_bytes_read) < ld->cap_pipe_bytes_to_read) +#endif + if ((pcap_opts->cap_pipe_bytes_read) < pcap_opts->cap_pipe_bytes_to_read) return 0; result = PD_DATA_READ; break; @@ -2081,30 +2156,37 @@ cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) case PD_REC_HDR_READ: /* We've read the header. Take care of byte order. */ - cap_pipe_adjust_header(ld->cap_pipe_byte_swapped, &ld->cap_pipe_hdr, - &ld->cap_pipe_rechdr.hdr); - if (ld->cap_pipe_rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) { + cap_pipe_adjust_header(pcap_opts->cap_pipe_byte_swapped, &pcap_opts->cap_pipe_hdr, + &pcap_opts->cap_pipe_rechdr.hdr); + if (pcap_opts->cap_pipe_rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) { g_snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)", - ld->packet_count+1, ld->cap_pipe_rechdr.hdr.incl_len); + ld->packet_count+1, pcap_opts->cap_pipe_rechdr.hdr.incl_len); break; } - ld->cap_pipe_state = STATE_EXPECT_DATA; - return 0; + + if (pcap_opts->cap_pipe_rechdr.hdr.incl_len) { + pcap_opts->cap_pipe_state = STATE_EXPECT_DATA; + return 0; + } + /* no data to read? fall through */ case PD_DATA_READ: /* Fill in a "struct pcap_pkthdr", and process the packet. */ - phdr.ts.tv_sec = ld->cap_pipe_rechdr.hdr.ts_sec; - phdr.ts.tv_usec = ld->cap_pipe_rechdr.hdr.ts_usec; - phdr.caplen = ld->cap_pipe_rechdr.hdr.incl_len; - phdr.len = ld->cap_pipe_rechdr.hdr.orig_len; - - capture_loop_packet_cb((u_char *)ld, &phdr, data); + phdr.ts.tv_sec = pcap_opts->cap_pipe_rechdr.hdr.ts_sec; + phdr.ts.tv_usec = pcap_opts->cap_pipe_rechdr.hdr.ts_usec; + phdr.caplen = pcap_opts->cap_pipe_rechdr.hdr.incl_len; + phdr.len = pcap_opts->cap_pipe_rechdr.hdr.orig_len; - ld->cap_pipe_state = STATE_EXPECT_REC_HDR; + if (use_threads) { + capture_loop_queue_packet_cb((u_char *)pcap_opts, &phdr, data); + } else { + capture_loop_write_packet_cb((u_char *)pcap_opts, &phdr, data); + } + pcap_opts->cap_pipe_state = STATE_EXPECT_REC_HDR; return 1; case PD_PIPE_EOF: - ld->cap_pipe_err = PIPEOF; + pcap_opts->cap_pipe_err = PIPEOF; return -1; case PD_PIPE_ERR: @@ -2117,14 +2199,14 @@ cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) LocalFree(err_str); #else g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s", - strerror(errno)); + g_strerror(errno)); #endif /* Fall through */ case PD_ERR: break; } - ld->cap_pipe_err = PIPERR; + pcap_opts->cap_pipe_err = PIPERR; /* Return here rather than inside the switch to prevent GCC warning */ return -1; } @@ -2137,8 +2219,11 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, char *errmsg, size_t errmsg_len, char *secondary_errmsg, size_t secondary_errmsg_len) { - gchar open_err_str[PCAP_ERRBUF_SIZE]; - gchar *sync_msg_str; + gchar open_err_str[PCAP_ERRBUF_SIZE]; + gchar *sync_msg_str; + interface_options interface_opts; + pcap_options *pcap_opts; + guint i; #ifdef _WIN32 int err; gchar *sync_secondary_msg_str; @@ -2146,9 +2231,6 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, WSADATA wsaData; #endif - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_input : %s", capture_opts->iface); - - /* XXX - opening Winsock on tshark? */ /* Initialize Windows Socket if we are in a WIN32 OS @@ -2196,85 +2278,134 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, return FALSE; } #endif + if ((use_threads == FALSE) && + (capture_opts->ifaces->len > 1)) { + g_snprintf(errmsg, (gulong) errmsg_len, + "Using threads is required for capturing on mulitple interfaces!"); + return FALSE; + } - ld->pcap_h = open_capture_device(capture_opts, &open_err_str); - - if (ld->pcap_h != NULL) { - /* we've opened "iface" as a network device */ -#ifdef _WIN32 - /* try to set the capture buffer size */ - if (capture_opts->buffer_size > 1 && - pcap_setbuff(ld->pcap_h, capture_opts->buffer_size * 1024 * 1024) != 0) { - sync_secondary_msg_str = g_strdup_printf( - "The capture buffer size of %dMB seems to be too high for your machine,\n" - "the default of 1MB will be used.\n" - "\n" - "Nonetheless, the capture is started.\n", - capture_opts->buffer_size); - report_capture_error("Couldn't set the capture buffer size!", - sync_secondary_msg_str); - g_free(sync_secondary_msg_str); + for (i = 0; i < capture_opts->ifaces->len; i++) { + interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); + pcap_opts = (pcap_options *)g_malloc(sizeof (pcap_options)); + if (pcap_opts == NULL) { + g_snprintf(errmsg, (gulong) errmsg_len, + "Could not allocate memory."); + return FALSE; } + pcap_opts->received = 0; + pcap_opts->dropped = 0; + pcap_opts->pcap_h = NULL; +#ifdef MUST_DO_SELECT + pcap_opts->pcap_fd = -1; #endif + pcap_opts->pcap_err = FALSE; + pcap_opts->interface_id = i; + pcap_opts->tid = NULL; + pcap_opts->snaplen = 0; + pcap_opts->linktype = -1; + pcap_opts->from_cap_pipe = FALSE; + memset(&pcap_opts->cap_pipe_hdr, 0, sizeof(struct pcap_hdr)); + memset(&pcap_opts->cap_pipe_rechdr, 0, sizeof(struct pcaprec_modified_hdr)); +#ifdef _WIN32 + pcap_opts->cap_pipe_h = INVALID_HANDLE_VALUE; +#else + pcap_opts->cap_pipe_fd = -1; +#endif + pcap_opts->cap_pipe_modified = FALSE; + pcap_opts->cap_pipe_byte_swapped = FALSE; +#ifdef _WIN32 + pcap_opts->cap_pipe_buf = NULL; +#endif + pcap_opts->cap_pipe_bytes_to_read = 0; + pcap_opts->cap_pipe_bytes_read = 0; + pcap_opts->cap_pipe_state = 0; + pcap_opts->cap_pipe_err = PIPOK; +#ifdef _WIN32 +#if GLIB_CHECK_VERSION(2,31,0) + pcap_opts->cap_pipe_read_mtx = g_malloc(sizeof(GMutex)); + g_mutex_init(pcap_opts->cap_pipe_read_mtx); +#else + pcap_opts->cap_pipe_read_mtx = g_mutex_new(); +#endif + pcap_opts->cap_pipe_pending_q = g_async_queue_new(); + pcap_opts->cap_pipe_done_q = g_async_queue_new(); +#endif + g_array_append_val(ld->pcaps, pcap_opts); -#if defined(HAVE_PCAP_REMOTE) && defined(HAVE_PCAP_SETSAMPLING) - if ((capture_opts->sampling_method != CAPTURE_SAMP_NONE) && - (strncmp (capture_opts->iface, "rpcap://", 8) == 0)) - { - struct pcap_samp *samp; - - if ((samp = pcap_setsampling(ld->pcap_h)) != NULL) - { - switch (capture_opts->sampling_method) - { - case CAPTURE_SAMP_BY_COUNT: - samp->method = PCAP_SAMP_1_EVERY_N; - break; + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_input : %s", interface_opts.name); + pcap_opts->pcap_h = open_capture_device(&interface_opts, &open_err_str); - case CAPTURE_SAMP_BY_TIMER: - samp->method = PCAP_SAMP_FIRST_AFTER_N_MS; - break; + if (pcap_opts->pcap_h != NULL) { + /* we've opened "iface" as a network device */ +#ifdef _WIN32 + /* try to set the capture buffer size */ + if (interface_opts.buffer_size > 1 && + pcap_setbuff(pcap_opts->pcap_h, interface_opts.buffer_size * 1024 * 1024) != 0) { + sync_secondary_msg_str = g_strdup_printf( + "The capture buffer size of %dMB seems to be too high for your machine,\n" + "the default of 1MB will be used.\n" + "\n" + "Nonetheless, the capture is started.\n", + interface_opts.buffer_size); + report_capture_error("Couldn't set the capture buffer size!", + sync_secondary_msg_str); + g_free(sync_secondary_msg_str); + } +#endif - default: - sync_msg_str = g_strdup_printf( - "Unknown sampling method %d specified,\n" - "continue without packet sampling", - capture_opts->sampling_method); - report_capture_error("Couldn't set the capture " - "sampling", sync_msg_str); - g_free(sync_msg_str); +#if defined(HAVE_PCAP_SETSAMPLING) + if (interface_opts.sampling_method != CAPTURE_SAMP_NONE) { + struct pcap_samp *samp; + + if ((samp = pcap_setsampling(pcap_opts->pcap_h)) != NULL) { + switch (interface_opts.sampling_method) { + case CAPTURE_SAMP_BY_COUNT: + samp->method = PCAP_SAMP_1_EVERY_N; + break; + + case CAPTURE_SAMP_BY_TIMER: + samp->method = PCAP_SAMP_FIRST_AFTER_N_MS; + break; + + default: + sync_msg_str = g_strdup_printf( + "Unknown sampling method %d specified,\n" + "continue without packet sampling", + interface_opts.sampling_method); + report_capture_error("Couldn't set the capture " + "sampling", sync_msg_str); + g_free(sync_msg_str); + } + samp->value = interface_opts.sampling_param; + } else { + report_capture_error("Couldn't set the capture sampling", + "Cannot get packet sampling data structure"); } - samp->value = capture_opts->sampling_param; } - else - { - report_capture_error("Couldn't set the capture sampling", - "Cannot get packet sampling data structure"); - } - - } #endif - /* setting the data link type only works on real interfaces */ - if (!set_pcap_linktype(ld->pcap_h, capture_opts, errmsg, errmsg_len, - secondary_errmsg, secondary_errmsg_len)) - return FALSE; - ld->linktype = get_pcap_linktype(ld->pcap_h, capture_opts->iface); - } else { - /* We couldn't open "iface" as a network device. */ - /* Try to open it as a pipe */ - cap_pipe_open_live(capture_opts->iface, &ld->cap_pipe_hdr, ld, errmsg, (int) errmsg_len); + /* setting the data link type only works on real interfaces */ + if (!set_pcap_linktype(pcap_opts->pcap_h, interface_opts.linktype, interface_opts.name, + errmsg, errmsg_len, + secondary_errmsg, secondary_errmsg_len)) { + return FALSE; + } + pcap_opts->linktype = get_pcap_linktype(pcap_opts->pcap_h, interface_opts.name); + } else { + /* We couldn't open "iface" as a network device. */ + /* Try to open it as a pipe */ + cap_pipe_open_live(interface_opts.name, pcap_opts, &pcap_opts->cap_pipe_hdr, errmsg, (int) errmsg_len); #ifndef _WIN32 - if (ld->cap_pipe_fd == -1) + if (pcap_opts->cap_pipe_fd == -1) { #else - if (ld->cap_pipe_h == INVALID_HANDLE_VALUE) + if (pcap_opts->cap_pipe_h == INVALID_HANDLE_VALUE) { #endif - { - if (ld->cap_pipe_err == PIPNEXIST) { + if (pcap_opts->cap_pipe_err == PIPNEXIST) { /* Pipe doesn't exist, so output message for interface */ get_capture_device_open_failure_messages(open_err_str, - capture_opts->iface, + interface_opts.name, errmsg, errmsg_len, secondary_errmsg, @@ -2285,59 +2416,77 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, * filled in errmsg */ return FALSE; - } else + } else { /* cap_pipe_open_live() succeeded; don't want error message from pcap_open_live() */ open_err_str[0] = '\0'; - } + } + } /* XXX - will this work for tshark? */ #ifdef MUST_DO_SELECT - if (!ld->from_cap_pipe) { + if (!pcap_opts->from_cap_pipe) { #ifdef HAVE_PCAP_GET_SELECTABLE_FD - ld->pcap_fd = pcap_get_selectable_fd(ld->pcap_h); + pcap_opts->pcap_fd = pcap_get_selectable_fd(pcap_opts->pcap_h); #else - ld->pcap_fd = pcap_fileno(ld->pcap_h); + pcap_opts->pcap_fd = pcap_fileno(pcap_opts->pcap_h); #endif - } + } #endif - /* Does "open_err_str" contain a non-empty string? If so, "pcap_open_live()" - returned a warning; print it, but keep capturing. */ - if (open_err_str[0] != '\0') { - sync_msg_str = g_strdup_printf("%s.", open_err_str); - report_capture_error(sync_msg_str, ""); - g_free(sync_msg_str); + /* Does "open_err_str" contain a non-empty string? If so, "pcap_open_live()" + returned a warning; print it, but keep capturing. */ + if (open_err_str[0] != '\0') { + sync_msg_str = g_strdup_printf("%s.", open_err_str); + report_capture_error(sync_msg_str, ""); + g_free(sync_msg_str); + } + capture_opts->ifaces = g_array_remove_index(capture_opts->ifaces, i); + g_array_insert_val(capture_opts->ifaces, i, interface_opts); } + /* If not using libcap: we now can now set euid/egid to ruid/rgid */ + /* to remove any suid privileges. */ + /* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities */ + /* (euid/egid have already previously been set to ruid/rgid. */ + /* (See comment in main() for details) */ +#ifndef HAVE_LIBCAP + relinquish_special_privs_perm(); +#else + relinquish_all_capabilities(); +#endif return TRUE; } /* close the capture input file (pcap or capture pipe) */ static void capture_loop_close_input(loop_data *ld) { + guint i; + pcap_options *pcap_opts; + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input"); - /* if open, close the capture pipe "input file" */ + for (i = 0; i < ld->pcaps->len; i++) { + pcap_opts = g_array_index(ld->pcaps, pcap_options *, i); + /* if open, close the capture pipe "input file" */ #ifndef _WIN32 - if (ld->cap_pipe_fd >= 0) { - g_assert(ld->from_cap_pipe); - ws_close(ld->cap_pipe_fd); - ld->cap_pipe_fd = 0; - } + if (pcap_opts->cap_pipe_fd >= 0) { + g_assert(pcap_opts->from_cap_pipe); + ws_close(pcap_opts->cap_pipe_fd); + pcap_opts->cap_pipe_fd = -1; + } #else - if (ld->cap_pipe_h != INVALID_HANDLE_VALUE) { - CloseHandle(ld->cap_pipe_h); - ld->cap_pipe_h = INVALID_HANDLE_VALUE; - } + if (pcap_opts->cap_pipe_h != INVALID_HANDLE_VALUE) { + CloseHandle(pcap_opts->cap_pipe_h); + pcap_opts->cap_pipe_h = INVALID_HANDLE_VALUE; + } #endif - - /* if open, close the pcap "input file" */ - if(ld->pcap_h != NULL) { - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", (void *)ld->pcap_h); - g_assert(!ld->from_cap_pipe); - pcap_close(ld->pcap_h); - ld->pcap_h = NULL; + /* if open, close the pcap "input file" */ + if (pcap_opts->pcap_h != NULL) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", (void *)pcap_opts->pcap_h); + pcap_close(pcap_opts->pcap_h); + pcap_opts->pcap_h = NULL; + } } ld->go = FALSE; @@ -2352,7 +2501,7 @@ static void capture_loop_close_input(loop_data *ld) /* init the capture filter */ static initfilter_status_t capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, - gchar * iface, gchar * cfilter) + const gchar * name, const gchar * cfilter) { struct bpf_program fcode; @@ -2361,7 +2510,7 @@ capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, /* capture filters only work on real interfaces */ if (cfilter && !from_cap_pipe) { /* A capture filter was specified; set it up. */ - if (!compile_capture_filter(iface, pcap_h, &fcode, cfilter)) { + if (!compile_capture_filter(name, pcap_h, &fcode, cfilter)) { /* Treat this specially - our caller might try to compile this as a display filter and, if that succeeds, warn the user that the display and capture filter syntaxes are different. */ @@ -2386,16 +2535,19 @@ capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, static gboolean capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *errmsg, int errmsg_len) { - int err; + int err; + guint i; + pcap_options *pcap_opts; + interface_options interface_opts; + gboolean successful; g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_init_output"); - /* get snaplen */ - if (ld->from_cap_pipe) { - ld->file_snaplen = ld->cap_pipe_hdr.snaplen; - } else - { - ld->file_snaplen = pcap_snapshot(ld->pcap_h); + if ((capture_opts->use_pcapng == FALSE) && + (capture_opts->ifaces->len > 1)) { + g_snprintf(errmsg, errmsg_len, + "Using PCAPNG is required for capturing on mulitple interfaces! Use the -n option."); + return FALSE; } /* Set up to write to the capture file. */ @@ -2405,17 +2557,35 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err ld->pdh = libpcap_fdopen(ld->save_file_fd, &err); } if (ld->pdh) { - gboolean successful; - - ld->bytes_written = 0; if (capture_opts->use_pcapng) { char appname[100]; g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion); - successful = libpcap_write_session_header_block(ld->pdh, appname, &ld->bytes_written, &err) && - libpcap_write_interface_description_block(ld->pdh, capture_opts->iface, capture_opts->cfilter, ld->linktype, ld->file_snaplen, &ld->bytes_written, &err); + successful = libpcap_write_session_header_block(ld->pdh, appname, &ld->bytes_written, &err); + for (i = 0; successful && (i < capture_opts->ifaces->len); i++) { + interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); + pcap_opts = g_array_index(ld->pcaps, pcap_options *, i); + if (pcap_opts->from_cap_pipe) { + pcap_opts->snaplen = pcap_opts->cap_pipe_hdr.snaplen; + } else { + pcap_opts->snaplen = pcap_snapshot(pcap_opts->pcap_h); + } + successful = libpcap_write_interface_description_block(ld->pdh, + interface_opts.name, + interface_opts.cfilter?interface_opts.cfilter:"", + pcap_opts->linktype, + pcap_opts->snaplen, + &ld->bytes_written, + &err); + } } else { - successful = libpcap_write_file_header(ld->pdh, ld->linktype, ld->file_snaplen, + pcap_opts = g_array_index(ld->pcaps, pcap_options *, 0); + if (pcap_opts->from_cap_pipe) { + pcap_opts->snaplen = pcap_opts->cap_pipe_hdr.snaplen; + } else { + pcap_opts->snaplen = pcap_snapshot(pcap_opts->pcap_h); + } + successful = libpcap_write_file_header(ld->pdh, pcap_opts->linktype, pcap_opts->snaplen, &ld->bytes_written, &err); } if (!successful) { @@ -2439,7 +2609,7 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err g_snprintf(errmsg, errmsg_len, "The file to which the capture would be" " saved (\"%s\") could not be opened: %s.", - capture_opts->save_file, strerror(err)); + capture_opts->save_file, g_strerror(err)); } break; } @@ -2454,13 +2624,21 @@ static gboolean capture_loop_close_output(capture_options *capture_opts, loop_data *ld, int *err_close) { + unsigned int i; + pcap_options *pcap_opts; + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_output"); if (capture_opts->multi_files_on) { return ringbuf_libpcap_dump_close(&capture_opts->save_file, err_close); } else { if (capture_opts->use_pcapng) { - libpcap_write_interface_statistics_block(ld->pdh, 0, ld->pcap_h, &ld->bytes_written, err_close); + for (i = 0; i < global_ld.pcaps->len; i++) { + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); + if (!pcap_opts->from_cap_pipe) { + libpcap_write_interface_statistics_block(ld->pdh, i, pcap_opts->pcap_h, &ld->bytes_written, err_close); + } + } } return libpcap_dump_close(ld->pdh, err_close); } @@ -2478,28 +2656,28 @@ capture_loop_close_output(capture_options *capture_opts, loop_data *ld, int *err * indefinitely. */ static int -capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, - char *errmsg, int errmsg_len) +capture_loop_dispatch(loop_data *ld, + char *errmsg, int errmsg_len, pcap_options *pcap_opts) { int inpkts; gint packet_count_before; guchar pcap_data[WTAP_MAX_PACKET_SIZE]; -#ifndef USE_THREADS +#ifndef _WIN32 int sel_ret; #endif packet_count_before = ld->packet_count; - if (ld->from_cap_pipe) { + if (pcap_opts->from_cap_pipe) { /* dispatch from capture pipe */ #ifdef LOG_CAPTURE_VERBOSE g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe"); #endif -#ifndef USE_THREADS - sel_ret = cap_pipe_select(ld->cap_pipe_fd); +#ifndef _WIN32 + sel_ret = cap_pipe_select(pcap_opts->cap_pipe_fd); if (sel_ret <= 0) { if (sel_ret < 0 && errno != EINTR) { g_snprintf(errmsg, errmsg_len, - "Unexpected error from select: %s", strerror(errno)); + "Unexpected error from select: %s", g_strerror(errno)); report_capture_error(errmsg, please_report); ld->go = FALSE; } @@ -2507,12 +2685,12 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, /* * "select()" says we can read from the pipe without blocking */ -#endif /* USE_THREADS */ - inpkts = cap_pipe_dispatch(ld, pcap_data, errmsg, errmsg_len); +#endif + inpkts = cap_pipe_dispatch(ld, pcap_opts, pcap_data, errmsg, errmsg_len); if (inpkts < 0) { ld->go = FALSE; } -#ifndef USE_THREADS +#ifndef _WIN32 } #endif } @@ -2536,8 +2714,8 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, #ifdef LOG_CAPTURE_VERBOSE g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch with select"); #endif - if (ld->pcap_fd != -1) { - sel_ret = cap_pipe_select(ld->pcap_fd); + if (pcap_opts->pcap_fd != -1) { + sel_ret = cap_pipe_select(pcap_opts->pcap_fd); if (sel_ret > 0) { /* * "select()" says we can read from it without blocking; go for @@ -2548,19 +2726,22 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, * processing immediately, rather than processing all packets * in a batch before quitting. */ - inpkts = pcap_dispatch(ld->pcap_h, 1, capture_loop_packet_cb, - (u_char *)ld); + if (use_threads) { + inpkts = pcap_dispatch(pcap_opts->pcap_h, 1, capture_loop_queue_packet_cb, (u_char *)pcap_opts); + } else { + inpkts = pcap_dispatch(pcap_opts->pcap_h, 1, capture_loop_write_packet_cb, (u_char *)pcap_opts); + } if (inpkts < 0) { if (inpkts == -1) { /* Error, rather than pcap_breakloop(). */ - ld->pcap_err = TRUE; + pcap_opts->pcap_err = TRUE; } ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */ } } else { if (sel_ret < 0 && errno != EINTR) { g_snprintf(errmsg, errmsg_len, - "Unexpected error from select: %s", strerror(errno)); + "Unexpected error from select: %s", g_strerror(errno)); report_capture_error(errmsg, please_report); ld->go = FALSE; } @@ -2581,14 +2762,22 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, * after processing packets. We therefore process only one packet * at a time, so that we can check the pipe after every packet. */ - inpkts = pcap_dispatch(ld->pcap_h, 1, capture_loop_packet_cb, (u_char *) ld); + if (use_threads) { + inpkts = pcap_dispatch(pcap_opts->pcap_h, 1, capture_loop_queue_packet_cb, (u_char *)pcap_opts); + } else { + inpkts = pcap_dispatch(pcap_opts->pcap_h, 1, capture_loop_write_packet_cb, (u_char *)pcap_opts); + } #else - inpkts = pcap_dispatch(ld->pcap_h, -1, capture_loop_packet_cb, (u_char *) ld); + if (use_threads) { + inpkts = pcap_dispatch(pcap_opts->pcap_h, -1, capture_loop_queue_packet_cb, (u_char *)pcap_opts); + } else { + inpkts = pcap_dispatch(pcap_opts->pcap_h, -1, capture_loop_write_packet_cb, (u_char *)pcap_opts); + } #endif if (inpkts < 0) { if (inpkts == -1) { /* Error, rather than pcap_breakloop(). */ - ld->pcap_err = TRUE; + pcap_opts->pcap_err = TRUE; } ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */ } @@ -2615,11 +2804,16 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, in = 0; while(ld->go && - (in = pcap_next_ex(ld->pcap_h, &pkt_header, &pkt_data)) == 1) - capture_loop_packet_cb( (u_char *) ld, pkt_header, pkt_data); + (in = pcap_next_ex(pcap_opts->pcap_h, &pkt_header, &pkt_data)) == 1) { + if (use_threads) { + capture_loop_queue_packet_cb((u_char *)pcap_opts, pkt_header, pkt_data); + } else { + capture_loop_write_packet_cb((u_char *)pcap_opts, pkt_header, pkt_data); + } + } if(in < 0) { - ld->pcap_err = TRUE; + pcap_opts->pcap_err = TRUE; ld->go = FALSE; } } @@ -2636,8 +2830,8 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, #ifdef _WIN32 /* Isolate the Universally Unique Identifier from the interface. Basically, we - * want to grab only the characters between the '{' and '}' delimiters. - * + * want to grab only the characters between the '{' and '}' delimiters. + * * Returns a GString that must be freed with g_string_free(). */ static GString * isolate_uuid(const char *iface) @@ -2671,7 +2865,7 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, gboolean is_tempfile; g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_output: %s", - (capture_opts->save_file) ? capture_opts->save_file : ""); + (capture_opts->save_file) ? capture_opts->save_file : "(not specified)"); if (capture_opts->save_file != NULL) { /* We return to the caller while the capture is in progress. @@ -2719,15 +2913,21 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, is_tempfile = FALSE; } else { /* Choose a random name for the temporary capture buffer */ + if (global_capture_opts.ifaces->len > 1) { + prefix = g_strdup_printf("wireshark_%d_interfaces", global_capture_opts.ifaces->len); + } else { + gchar *basename; #ifdef _WIN32 - GString *iface; - - iface = isolate_uuid(capture_opts->iface); - prefix = g_strconcat("wireshark_", g_basename(iface->str), NULL); - g_string_free(iface, TRUE); + GString *iface; + iface = isolate_uuid(g_array_index(global_capture_opts.ifaces, interface_options, 0).name); + basename = g_path_get_basename(iface->str); + g_string_free(iface, TRUE); #else - prefix = g_strconcat("wireshark_", g_basename(capture_opts->iface), NULL); + basename = g_path_get_basename(g_array_index(global_capture_opts.ifaces, interface_options, 0).name); #endif + prefix = g_strconcat("wireshark_", basename, NULL); + g_free(basename); + } *save_file_fd = create_tempfile(&tmpname, prefix); g_free(prefix); capfile_name = g_strdup(tmpname); @@ -2739,7 +2939,7 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, if (is_tempfile) { g_snprintf(errmsg, errmsg_len, "The temporary file to which the capture would be saved (\"%s\") " - "could not be opened: %s.", capfile_name, strerror(errno)); + "could not be opened: %s.", capfile_name, g_strerror(errno)); } else { if (capture_opts->multi_files_on) { ringbuf_error_cleanup(); @@ -2748,7 +2948,7 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, g_snprintf(errmsg, errmsg_len, "The file to which the capture would be saved (\"%s\") " "could not be opened: %s.", capfile_name, - strerror(errno)); + g_strerror(errno)); } g_free(capfile_name); return FALSE; @@ -2765,12 +2965,6 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, } -#ifdef _WIN32 -#define TIME_GET() GetTickCount() -#else -#define TIME_GET() time(NULL) -#endif - /* Do the work of handling either the file size or file duration capture conditions being reached, and switching files or stopping. */ static gboolean @@ -2779,6 +2973,11 @@ do_file_switch_or_stop(capture_options *capture_opts, condition *cnd_autostop_size, condition *cnd_file_duration) { + guint i; + pcap_options *pcap_opts; + interface_options interface_opts; + gboolean successful; + if (capture_opts->multi_files_on) { if (cnd_autostop_files != NULL && cnd_eval(cnd_autostop_files, ++global_ld.autostop_files)) { @@ -2790,7 +2989,6 @@ do_file_switch_or_stop(capture_options *capture_opts, /* Switch to the next ringbuffer file */ if (ringbuf_switch_file(&global_ld.pdh, &capture_opts->save_file, &global_ld.save_file_fd, &global_ld.err)) { - gboolean successful; /* File switch succeeded: reset the conditions */ global_ld.bytes_written = 0; @@ -2798,10 +2996,21 @@ do_file_switch_or_stop(capture_options *capture_opts, char appname[100]; g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion); - successful = libpcap_write_session_header_block(global_ld.pdh, appname, &global_ld.bytes_written, &global_ld.err) && - libpcap_write_interface_description_block(global_ld.pdh, capture_opts->iface, capture_opts->cfilter, global_ld.linktype, global_ld.file_snaplen, &global_ld.bytes_written, &global_ld.err); + successful = libpcap_write_session_header_block(global_ld.pdh, appname, &(global_ld.bytes_written), &global_ld.err); + for (i = 0; successful && (i < capture_opts->ifaces->len); i++) { + interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); + successful = libpcap_write_interface_description_block(global_ld.pdh, + interface_opts.name, + interface_opts.cfilter?interface_opts.cfilter:"", + pcap_opts->linktype, + pcap_opts->snaplen, + &(global_ld.bytes_written), + &global_ld.err); + } } else { - successful = libpcap_write_file_header(global_ld.pdh, global_ld.linktype, global_ld.file_snaplen, + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, 0); + successful = libpcap_write_file_header(global_ld.pdh, pcap_opts->linktype, pcap_opts->snaplen, &global_ld.bytes_written, &global_ld.err); } if (!successful) { @@ -2810,9 +3019,9 @@ do_file_switch_or_stop(capture_options *capture_opts, global_ld.go = FALSE; return FALSE; } - if(cnd_autostop_size) + if (cnd_autostop_size) cnd_reset(cnd_autostop_size); - if(cnd_file_duration) + if (cnd_file_duration) cnd_reset(cnd_file_duration); libpcap_dump_flush(global_ld.pdh, NULL); if (!quiet) @@ -2832,12 +3041,37 @@ do_file_switch_or_stop(capture_options *capture_opts, return TRUE; } +static void * +pcap_read_handler(void* arg) +{ + pcap_options *pcap_opts; + char errmsg[MSG_MAX_LENGTH+1]; + + pcap_opts = (pcap_options *)arg; + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Started thread for interface %d.", + pcap_opts->interface_id); + + while (global_ld.go) { + /* dispatch incoming packets */ + capture_loop_dispatch(&global_ld, errmsg, sizeof(errmsg), pcap_opts); + } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Stopped thread for interface %d.", + pcap_opts->interface_id); + g_thread_exit(NULL); + return (NULL); +} + /* Do the low-level work of a capture. Returns TRUE if it succeeds, FALSE otherwise. */ static gboolean capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats) { - time_t upd_time, cur_time; +#ifdef WIN32 + DWORD upd_time, cur_time; /* GetTickCount() returns a "DWORD" (which is 'unsigned long') */ +#else + struct timeval upd_time, cur_time; +#endif int err_close; int inpkts; condition *cnd_file_duration = NULL; @@ -2849,6 +3083,9 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct gboolean cfilter_error = FALSE; char errmsg[MSG_MAX_LENGTH+1]; char secondary_errmsg[MSG_MAX_LENGTH+1]; + pcap_options *pcap_opts; + interface_options interface_opts; + guint i, error_index = 0; *errmsg = '\0'; *secondary_errmsg = '\0'; @@ -2860,22 +3097,12 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct global_ld.report_packet_count = FALSE; #endif if (capture_opts->has_autostop_packets) - global_ld.packet_max = capture_opts->autostop_packets; + global_ld.packet_max = capture_opts->autostop_packets; else - global_ld.packet_max = 0; /* no limit */ + global_ld.packet_max = 0; /* no limit */ global_ld.inpkts_to_sync_pipe = 0; global_ld.err = 0; /* no error seen yet */ - global_ld.pcap_err = FALSE; - global_ld.from_cap_pipe = FALSE; global_ld.pdh = NULL; -#ifndef _WIN32 - global_ld.cap_pipe_fd = -1; -#else - global_ld.cap_pipe_h = INVALID_HANDLE_VALUE; -#endif -#ifdef MUST_DO_SELECT - global_ld.pcap_fd = 0; -#endif global_ld.autostop_files = 0; global_ld.save_file_fd = -1; @@ -2890,25 +3117,34 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct secondary_errmsg, sizeof(secondary_errmsg))) { goto error; } + for (i = 0; i < capture_opts->ifaces->len; i++) { + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); + interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); + /* init the input filter from the network interface (capture pipe will do nothing) */ + /* + * When remote capturing WinPCap crashes when the capture filter + * is NULL. This might be a bug in WPCap. Therefore we provide an emtpy + * string. + */ + switch (capture_loop_init_filter(pcap_opts->pcap_h, pcap_opts->from_cap_pipe, + interface_opts.name, + interface_opts.cfilter?interface_opts.cfilter:"")) { - /* init the input filter from the network interface (capture pipe will do nothing) */ - switch (capture_loop_init_filter(global_ld.pcap_h, global_ld.from_cap_pipe, - capture_opts->iface, - capture_opts->cfilter)) { - - case INITFILTER_NO_ERROR: - break; + case INITFILTER_NO_ERROR: + break; - case INITFILTER_BAD_FILTER: - cfilter_error = TRUE; - g_snprintf(errmsg, sizeof(errmsg), "%s", pcap_geterr(global_ld.pcap_h)); - goto error; + case INITFILTER_BAD_FILTER: + cfilter_error = TRUE; + error_index = i; + g_snprintf(errmsg, sizeof(errmsg), "%s", pcap_geterr(pcap_opts->pcap_h)); + goto error; - case INITFILTER_OTHER_ERROR: - g_snprintf(errmsg, sizeof(errmsg), "Can't install filter (%s).", - pcap_geterr(global_ld.pcap_h)); - g_snprintf(secondary_errmsg, sizeof(secondary_errmsg), "%s", please_report); - goto error; + case INITFILTER_OTHER_ERROR: + g_snprintf(errmsg, sizeof(errmsg), "Can't install filter (%s).", + pcap_geterr(pcap_opts->pcap_h)); + g_snprintf(secondary_errmsg, sizeof(secondary_errmsg), "%s", please_report); + goto error; + } } /* If we're supposed to write to a capture file, open it for output @@ -2963,17 +3199,64 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct } /* init the time values */ - upd_time = TIME_GET(); +#ifdef WIN32 + upd_time = GetTickCount(); +#else + gettimeofday(&upd_time, NULL); +#endif g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop running!"); /* WOW, everything is prepared! */ /* please fasten your seat belts, we will enter now the actual capture loop */ + if (use_threads) { + pcap_queue = g_async_queue_new(); + pcap_queue_bytes = 0; + pcap_queue_packets = 0; + for (i = 0; i < global_ld.pcaps->len; i++) { + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); +#if GLIB_CHECK_VERSION(2,31,0) + /* XXX - Add an interface name here? */ + pcap_opts->tid = g_thread_new("Capture read", pcap_read_handler, pcap_opts); +#else + pcap_opts->tid = g_thread_create(pcap_read_handler, pcap_opts, TRUE, NULL); +#endif + } + } while (global_ld.go) { /* dispatch incoming packets */ - inpkts = capture_loop_dispatch(capture_opts, &global_ld, errmsg, - sizeof(errmsg)); - + if (use_threads) { + GTimeVal write_thread_time; + pcap_queue_element *queue_element; + + g_get_current_time(&write_thread_time); + g_time_val_add(&write_thread_time, WRITER_THREAD_TIMEOUT); + g_async_queue_lock(pcap_queue); + queue_element = g_async_queue_timed_pop_unlocked(pcap_queue, &write_thread_time); + if (queue_element) { + pcap_queue_bytes -= queue_element->phdr.caplen; + pcap_queue_packets -= 1; + } + g_async_queue_unlock(pcap_queue); + if (queue_element) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, + "Dequeued a packet of length %d captured on interface %d.", + queue_element->phdr.caplen, queue_element->pcap_opts->interface_id); + + capture_loop_write_packet_cb((u_char *) queue_element->pcap_opts, + &queue_element->phdr, + queue_element->pd); + g_free(queue_element->pd); + g_free(queue_element); + inpkts = 1; + } else { + inpkts = 0; + } + } else { + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, 0); + inpkts = capture_loop_dispatch(&global_ld, errmsg, + sizeof(errmsg), pcap_opts); + } #ifdef SIGINFO /* Were we asked to print packet counts by the SIGINFO handler? */ if (global_ld.report_packet_count) { @@ -3006,16 +3289,21 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct } } /* inpkts */ - /* Only update once a second (Win32: 500ms) so as not to overload slow - * displays. This also prevents too much context-switching between the - * dumpcap and wireshark processes */ -#ifdef _WIN32 + /* Only update once every 500ms so as not to overload slow displays. + * This also prevents too much context-switching between the dumpcap + * and wireshark processes. + */ #define DUMPCAP_UPD_TIME 500 + +#ifdef WIN32 + cur_time = GetTickCount(); /* Note: wraps to 0 if sys runs for 49.7 days */ + if ((cur_time - upd_time) > DUMPCAP_UPD_TIME) { /* wrap just causes an extra update */ #else -#define DUMPCAP_UPD_TIME 0 + gettimeofday(&cur_time, NULL); + if ((cur_time.tv_sec * 1000000 + cur_time.tv_usec) > + (upd_time.tv_sec * 1000000 + upd_time.tv_usec + DUMPCAP_UPD_TIME*1000)) { #endif - cur_time = TIME_GET(); - if ( (cur_time - upd_time) > DUMPCAP_UPD_TIME) { + upd_time = cur_time; #if 0 @@ -3051,10 +3339,46 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct continue; } /* cnd_file_duration */ } - - } /* while (global_ld.go) */ + } g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopping ..."); + if (use_threads) { + pcap_queue_element *queue_element; + + for (i = 0; i < global_ld.pcaps->len; i++) { + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Waiting for thread of interface %u...", + pcap_opts->interface_id); + g_thread_join(pcap_opts->tid); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Thread of interface %u terminated.", + pcap_opts->interface_id); + } + while (1) { + g_async_queue_lock(pcap_queue); + queue_element = g_async_queue_try_pop_unlocked(pcap_queue); + if (queue_element) { + pcap_queue_bytes -= queue_element->phdr.caplen; + pcap_queue_packets -= 1; + } + g_async_queue_unlock(pcap_queue); + if (queue_element == NULL) { + break; + } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, + "Dequeued a packet of length %d captured on interface %d.", + queue_element->phdr.caplen, queue_element->pcap_opts->interface_id); + capture_loop_write_packet_cb((u_char *)queue_element->pcap_opts, + &queue_element->phdr, + queue_element->pd); + g_free(queue_element->pd); + g_free(queue_element); + global_ld.inpkts_to_sync_pipe += 1; + if (capture_opts->output_to_pipe) { + libpcap_dump_flush(global_ld.pdh, NULL); + } + } + } + /* delete stop conditions */ if (cnd_file_duration != NULL) @@ -3066,41 +3390,45 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct if (cnd_autostop_duration != NULL) cnd_delete(cnd_autostop_duration); - /* did we had a pcap (input) error? */ - if (global_ld.pcap_err) { - /* On Linux, if an interface goes down while you're capturing on it, - you'll get a "recvfrom: Network is down" or - "The interface went down" error (ENETDOWN). - (At least you will if strerror() doesn't show a local translation - of the error.) - - On FreeBSD and OS X, if a network adapter disappears while - you're capturing on it, you'll get a "read: Device not configured" - error (ENXIO). (See previous parenthetical note.) - - On OpenBSD, you get "read: I/O error" (EIO) in the same case. - - These should *not* be reported to the Wireshark developers. */ - char *cap_err_str; - - cap_err_str = pcap_geterr(global_ld.pcap_h); - if (strcmp(cap_err_str, "recvfrom: Network is down") == 0 || - strcmp(cap_err_str, "The interface went down") == 0 || - strcmp(cap_err_str, "read: Device not configured") == 0 || - strcmp(cap_err_str, "read: I/O error") == 0 || - strcmp(cap_err_str, "read error: PacketReceivePacket failed") == 0) { - report_capture_error("The network adapter on which the capture was being done " - "is no longer running; the capture has stopped.", - ""); - } else { - g_snprintf(errmsg, sizeof(errmsg), "Error while capturing packets: %s", - cap_err_str); - report_capture_error(errmsg, please_report); + /* did we have a pcap (input) error? */ + for (i = 0; i < capture_opts->ifaces->len; i++) { + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); + if (pcap_opts->pcap_err) { + /* On Linux, if an interface goes down while you're capturing on it, + you'll get a "recvfrom: Network is down" or + "The interface went down" error (ENETDOWN). + (At least you will if g_strerror() doesn't show a local translation + of the error.) + + On FreeBSD and OS X, if a network adapter disappears while + you're capturing on it, you'll get a "read: Device not configured" + error (ENXIO). (See previous parenthetical note.) + + On OpenBSD, you get "read: I/O error" (EIO) in the same case. + + These should *not* be reported to the Wireshark developers. */ + char *cap_err_str; + + cap_err_str = pcap_geterr(pcap_opts->pcap_h); + if (strcmp(cap_err_str, "recvfrom: Network is down") == 0 || + strcmp(cap_err_str, "The interface went down") == 0 || + strcmp(cap_err_str, "read: Device not configured") == 0 || + strcmp(cap_err_str, "read: I/O error") == 0 || + strcmp(cap_err_str, "read error: PacketReceivePacket failed") == 0) { + report_capture_error("The network adapter on which the capture was being done " + "is no longer running; the capture has stopped.", + ""); + } else { + g_snprintf(errmsg, sizeof(errmsg), "Error while capturing packets: %s", + cap_err_str); + report_capture_error(errmsg, please_report); + } + break; + } else if (pcap_opts->from_cap_pipe && pcap_opts->cap_pipe_err == PIPERR) { + report_capture_error(errmsg, ""); + break; } } - else if (global_ld.from_cap_pipe && global_ld.cap_pipe_err == PIPERR) - report_capture_error(errmsg, ""); - /* did we have an output error while capturing? */ if (global_ld.err == 0) { write_ok = TRUE; @@ -3142,23 +3470,32 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct * mode, cap_pipe_open_live() will say "End of file on pipe during open". */ - report_capture_count(); /* print final capture count only if (quiet && !capture_child) */ + report_capture_count(TRUE); /* get packet drop statistics from pcap */ - if(global_ld.pcap_h != NULL) { - g_assert(!global_ld.from_cap_pipe); - /* Get the capture statistics, so we know how many packets were - dropped. */ - if (pcap_stats(global_ld.pcap_h, stats) >= 0) { - *stats_known = TRUE; - /* Let the parent process know. */ - report_packet_drops(stats->ps_drop); - } else { - g_snprintf(errmsg, sizeof(errmsg), - "Can't get packet-drop statistics: %s", - pcap_geterr(global_ld.pcap_h)); - report_capture_error(errmsg, please_report); + for (i = 0; i < capture_opts->ifaces->len; i++) { + guint32 received; + guint32 dropped; + + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); + interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); + received = pcap_opts->received; + dropped = pcap_opts->dropped; + if (pcap_opts->pcap_h != NULL) { + g_assert(!pcap_opts->from_cap_pipe); + /* Get the capture statistics, so we know how many packets were dropped. */ + if (pcap_stats(pcap_opts->pcap_h, stats) >= 0) { + *stats_known = TRUE; + /* Let the parent process know. */ + dropped += stats->ps_drop; + } else { + g_snprintf(errmsg, sizeof(errmsg), + "Can't get packet-drop statistics: %s", + pcap_geterr(pcap_opts->pcap_h)); + report_capture_error(errmsg, please_report); + } } + report_packet_drops(received, dropped, interface_opts.name); } /* close the input file (pcap or capture pipe) */ @@ -3176,20 +3513,20 @@ error: } else { /* We can't use the save file, and we have no FILE * for the stream to close in order to close it, so close the FD directly. */ - if(global_ld.save_file_fd != -1) { + if (global_ld.save_file_fd != -1) { ws_close(global_ld.save_file_fd); } /* We couldn't even start the capture, so get rid of the capture file. */ - if(capture_opts->save_file != NULL) { + if (capture_opts->save_file != NULL) { ws_unlink(capture_opts->save_file); g_free(capture_opts->save_file); } } capture_opts->save_file = NULL; if (cfilter_error) - report_cfilter_error(capture_opts->cfilter, errmsg); + report_cfilter_error(capture_opts, error_index, errmsg); else report_capture_error(errmsg, secondary_errmsg); @@ -3205,8 +3542,14 @@ error: static void capture_loop_stop(void) { #ifdef HAVE_PCAP_BREAKLOOP - if(global_ld.pcap_h != NULL) - pcap_breakloop(global_ld.pcap_h); + guint i; + pcap_options *pcap_opts; + + for (i = 0; i < global_ld.pcaps->len; i++) { + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); + if (pcap_opts->pcap_h != NULL) + pcap_breakloop(pcap_opts->pcap_h); + } #endif global_ld.go = FALSE; } @@ -3244,13 +3587,13 @@ capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, g_snprintf(errmsg, errmsglen, "The file to which the capture was being saved\n" "(\"%s\") could not be closed: %s.", - fname, strerror(err)); + fname, g_strerror(err)); } else { g_snprintf(errmsg, errmsglen, "An error occurred while writing to the file" " to which the capture was being saved\n" "(\"%s\"): %s.", - fname, strerror(err)); + fname, g_strerror(err)); } break; } @@ -3259,42 +3602,105 @@ capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, /* one packet was captured, process it */ static void -capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, - const u_char *pd) +capture_loop_write_packet_cb(u_char *pcap_opts_p, const struct pcap_pkthdr *phdr, + const u_char *pd) { - loop_data *ld = (loop_data *) (void *) user; + pcap_options *pcap_opts = (pcap_options *) (void *) pcap_opts_p; int err; /* We may be called multiple times from pcap_dispatch(); if we've set the "stop capturing" flag, ignore this packet, as we're not supposed to be saving any more packets. */ - if (!ld->go) + if (!global_ld.go) return; - if (ld->pdh) { + if (global_ld.pdh) { gboolean successful; + /* We're supposed to write the packet to a file; do so. If this fails, set "ld->go" to FALSE, to stop the capture, and set "ld->err" to the error. */ if (global_capture_opts.use_pcapng) { - successful = libpcap_write_enhanced_packet_block(ld->pdh, phdr, 0, pd, &ld->bytes_written, &err); + successful = libpcap_write_enhanced_packet_block(global_ld.pdh, phdr, pcap_opts->interface_id, pd, &global_ld.bytes_written, &err); } else { - successful = libpcap_write_packet(ld->pdh, phdr, pd, &ld->bytes_written, &err); + successful = libpcap_write_packet(global_ld.pdh, phdr, pd, &global_ld.bytes_written, &err); } if (!successful) { - ld->go = FALSE; - ld->err = err; + global_ld.go = FALSE; + global_ld.err = err; } else { - ld->packet_count++; + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, + "Wrote a packet of length %d captured on interface %u.", + phdr->caplen, pcap_opts->interface_id); + global_ld.packet_count++; + pcap_opts->received++; /* if the user told us to stop after x packets, do we already have enough? */ - if ((ld->packet_max > 0) && (ld->packet_count >= ld->packet_max)) - { - ld->go = FALSE; + if ((global_ld.packet_max > 0) && (global_ld.packet_count >= global_ld.packet_max)) { + global_ld.go = FALSE; } } } } +/* one packet was captured, queue it */ +static void +capture_loop_queue_packet_cb(u_char *pcap_opts_p, const struct pcap_pkthdr *phdr, + const u_char *pd) +{ + pcap_options *pcap_opts = (pcap_options *) (void *) pcap_opts_p; + pcap_queue_element *queue_element; + gboolean limit_reached; + + /* We may be called multiple times from pcap_dispatch(); if we've set + the "stop capturing" flag, ignore this packet, as we're not + supposed to be saving any more packets. */ + if (!global_ld.go) + return; + + queue_element = (pcap_queue_element *)g_malloc(sizeof(pcap_queue_element)); + if (queue_element == NULL) { + pcap_opts->dropped++; + return; + } + queue_element->pcap_opts = pcap_opts; + queue_element->phdr = *phdr; + queue_element->pd = (u_char *)g_malloc(phdr->caplen); + if (queue_element->pd == NULL) { + pcap_opts->dropped++; + g_free(queue_element); + return; + } + memcpy(queue_element->pd, pd, phdr->caplen); + g_async_queue_lock(pcap_queue); + if (((pcap_queue_byte_limit > 0) && (pcap_queue_bytes < pcap_queue_byte_limit)) && + ((pcap_queue_packet_limit > 0) && (pcap_queue_packets < pcap_queue_packet_limit))) { + limit_reached = FALSE; + g_async_queue_push_unlocked(pcap_queue, queue_element); + pcap_queue_bytes += phdr->caplen; + pcap_queue_packets += 1; + } else { + limit_reached = TRUE; + } + g_async_queue_unlock(pcap_queue); + if (limit_reached) { + pcap_opts->dropped++; + g_free(queue_element->pd); + g_free(queue_element); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, + "Dropped a packet of length %d captured on interface %u.", + phdr->caplen, pcap_opts->interface_id); + } else { + pcap_opts->received++; + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, + "Queued a packet of length %d captured on interface %u.", + phdr->caplen, pcap_opts->interface_id); + } + /* I don't want to hold the mutex over the debug output. So the + output may be wrong */ + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, + "Queue size is now %" G_GINT64_MODIFIER "d bytes (%" G_GINT64_MODIFIER "d packets)", + pcap_queue_bytes, pcap_queue_packets); +} /* And now our feature presentation... [ fade to music ] */ int @@ -3305,8 +3711,6 @@ main(int argc, char *argv[]) #ifdef _WIN32 WSADATA wsaData; - LPWSTR *wc_argv; - int wc_argc; #else struct sigaction action, oldaction; #endif @@ -3324,18 +3728,14 @@ main(int argc, char *argv[]) gboolean print_statistics = FALSE; int status, run_once_args = 0; gint i; + guint j; #if defined(__APPLE__) && defined(__LP64__) struct utsname osinfo; #endif + GString *str; #ifdef _WIN32 - /* Convert our arg list to UTF-8. */ - wc_argv = CommandLineToArgvW(GetCommandLineW(), &wc_argc); - if (wc_argv && wc_argc == argc) { - for (i = 0; i < argc; i++) { - argv[i] = g_utf16_to_utf8(wc_argv[i], -1, NULL, NULL, NULL); - } - } /* XXX else bail because something is horribly, horribly wrong? */ + arg_list_utf_16to8(argc, argv); #endif /* _WIN32 */ #ifdef _WIN32 @@ -3380,7 +3780,7 @@ main(int argc, char *argv[]) #define OPTSTRING_d "" #endif -#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:" OPTSTRING_d "Df:ghi:" OPTSTRING_I "L" OPTSTRING_m "Mnpq" OPTSTRING_r "Ss:" OPTSTRING_u "vw:y:Z:" +#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:" OPTSTRING_d "Df:ghi:" OPTSTRING_I "L" OPTSTRING_m "MnpPq" OPTSTRING_r "Ss:t" OPTSTRING_u "vw:y:Z:" #ifdef DEBUG_CHILD_DUMPCAP if ((debug_log = ws_fopen("dumpcap_debug_log.tmp","w")) == NULL) { @@ -3477,6 +3877,14 @@ main(int argc, char *argv[]) log_flags, console_log_handler, NULL /* user_data */); + /* Initialize the pcaps list */ + global_ld.pcaps = g_array_new(FALSE, FALSE, sizeof(pcap_options *)); + +#if !GLIB_CHECK_VERSION(2,31,0) + /* Initialize the thread system */ + g_thread_init(NULL); +#endif + #ifdef _WIN32 /* Load wpcap if possible. Do this before collecting the run-time version information */ load_wpcap(); @@ -3490,14 +3898,6 @@ main(int argc, char *argv[]) /* Set handler for Ctrl+C key */ SetConsoleCtrlHandler(capture_cleanup_handler, TRUE); - - /* Prepare to read from a pipe */ - if (!g_thread_supported ()) - g_thread_init (NULL); - cap_pipe_pending_q = g_async_queue_new(); - cap_pipe_done_q = g_async_queue_new(); - cap_pipe_read_mtx = g_mutex_new(); - #else /* Catch SIGINT and SIGTERM and, if we get either of them, clean up and exit. */ @@ -3612,9 +4012,6 @@ main(int argc, char *argv[]) by the command line parameters. */ capture_opts_init(&global_capture_opts, NULL); - /* Default to capturing the entire packet. */ - global_capture_opts.snaplen = WTAP_MAX_PACKET_SIZE; - /* We always save to a file - if no file was specified, we save to a temporary file. */ global_capture_opts.saving_to_file = TRUE; @@ -3652,6 +4049,7 @@ main(int argc, char *argv[]) case 'i': /* Use interface x */ case 'n': /* Use pcapng format */ case 'p': /* Don't capture in promiscuous mode */ + case 'P': /* Use pcap format */ case 's': /* Set the snapshot (capture) length */ case 'w': /* Write to capture file x */ case 'g': /* enable group read accesson file(s) */ @@ -3703,7 +4101,9 @@ main(int argc, char *argv[]) case 'q': /* Quiet */ quiet = TRUE; break; - + case 't': + use_threads = TRUE; + break; /*** all non capture option specific ***/ case 'D': /* Print a list of capture devices and exit */ list_interfaces = TRUE; @@ -3727,28 +4127,30 @@ main(int argc, char *argv[]) machine_readable = TRUE; break; default: - case '?': /* Bad flag - print usage message */ cmdarg_err("Invalid Option: %s", argv[optind-1]); + /* FALLTHROUGH */ + case '?': /* Bad flag - print usage message */ arg_error = TRUE; break; } } - argc -= optind; - argv += optind; - if (argc >= 1) { - /* user specified file name as regular command-line argument */ - /* XXX - use it as the capture file name (or something else)? */ - argc--; - argv++; - } - - if (argc != 0) { - /* - * Extra command line arguments were specified; complain. - * XXX - interpret as capture filter, as tcpdump and tshark do? - */ - cmdarg_err("Invalid argument: %s", argv[0]); - arg_error = TRUE; + if (!arg_error) { + argc -= optind; + argv += optind; + if (argc >= 1) { + /* user specified file name as regular command-line argument */ + /* XXX - use it as the capture file name (or something else)? */ + argc--; + argv++; + } + if (argc != 0) { + /* + * Extra command line arguments were specified; complain. + * XXX - interpret as capture filter, as tcpdump and tshark do? + */ + cmdarg_err("Invalid argument: %s", argv[0]); + arg_error = TRUE; + } } if (arg_error) { @@ -3767,8 +4169,13 @@ main(int argc, char *argv[]) exit_main(1); } } else { - /* We're supposed to capture traffic; was the ring buffer option - specified and, if so, does it make sense? */ + /* We're supposed to capture traffic; */ + /* Are we capturing on multiple interface? If so, use threads and pcapng. */ + if (global_capture_opts.ifaces->len > 1) { + use_threads = TRUE; + global_capture_opts.use_pcapng = TRUE; + } + /* Was the ring buffer option specified and, if so, does it make sense? */ if (global_capture_opts.multi_files_on) { /* Ring buffer works only under certain conditions: a) ring buffer does not work with temporary files; @@ -3802,6 +4209,7 @@ main(int argc, char *argv[]) if (if_list == NULL) { switch (err) { case CANT_GET_INTERFACE_LIST: + case DONT_HAVE_PCAP: cmdarg_err("%s", err_str); g_free(err_str); exit_main(2); @@ -3848,34 +4256,77 @@ main(int argc, char *argv[]) exit_main(1); } - /* Let the user know what interface was chosen. */ + /* Let the user know what interfaces were chosen. */ /* get_interface_descriptive_name() is not available! */ - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", global_capture_opts.iface); + if (capture_child) { + for (j = 0; j < global_capture_opts.ifaces->len; j++) { + interface_options interface_opts; + + interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", + interface_opts.name); + } + } else { + str = g_string_new(""); +#ifdef _WIN32 + if (global_capture_opts.ifaces->len < 2) { +#else + if (global_capture_opts.ifaces->len < 4) { +#endif + for (j = 0; j < global_capture_opts.ifaces->len; j++) { + interface_options interface_opts; + + interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j); + if (j > 0) { + if (global_capture_opts.ifaces->len > 2) { + g_string_append_printf(str, ","); + } + g_string_append_printf(str, " "); + if (j == global_capture_opts.ifaces->len - 1) { + g_string_append_printf(str, "and "); + } + } + g_string_append_printf(str, "%s", interface_opts.name); + } + } else { + g_string_append_printf(str, "%u interfaces", global_capture_opts.ifaces->len); + } + fprintf(stderr, "Capturing on %s\n", str->str); + g_string_free(str, TRUE); + } if (list_link_layer_types) { /* Get the list of link-layer types for the capture device. */ if_capabilities_t *caps; gchar *err_str; - - caps = get_if_capabilities(global_capture_opts.iface, - global_capture_opts.monitor_mode, &err_str); - if (caps == NULL) { - cmdarg_err("The capabilities of the capture device \"%s\" could not be obtained (%s).\n" - "Please check to make sure you have sufficient permissions, and that\n" - "you have the proper interface or pipe specified.", global_capture_opts.iface, err_str); - g_free(err_str); - exit_main(2); - } - if (caps->data_link_types == NULL) { - cmdarg_err("The capture device \"%s\" has no data link types.", global_capture_opts.iface); - exit_main(2); + guint i; + + for (i = 0; i < global_capture_opts.ifaces->len; i++) { + interface_options interface_opts; + + interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i); + caps = get_if_capabilities(interface_opts.name, + interface_opts.monitor_mode, &err_str); + if (caps == NULL) { + cmdarg_err("The capabilities of the capture device \"%s\" could not be obtained (%s).\n" + "Please check to make sure you have sufficient permissions, and that\n" + "you have the proper interface or pipe specified.", interface_opts.name, err_str); + g_free(err_str); + exit_main(2); + } + if (caps->data_link_types == NULL) { + cmdarg_err("The capture device \"%s\" has no data link types.", interface_opts.name); + exit_main(2); + } + if (machine_readable) /* tab-separated values to stdout */ + /* XXX: We need to change the format and adopt consumers */ + print_machine_readable_if_capabilities(caps); + else + /* XXX: We might want to print also the interface name */ + capture_opts_print_if_capabilities(caps, interface_opts.name, + interface_opts.monitor_mode); + free_if_capabilities(caps); } - if (machine_readable) /* tab-separated values to stdout */ - print_machine_readable_if_capabilities(caps); - else - capture_opts_print_if_capabilities(caps, - global_capture_opts.monitor_mode); - free_if_capabilities(caps); exit_main(0); } @@ -3902,6 +4353,7 @@ main(int argc, char *argv[]) /* capture failed */ exit_main(1); } + return 0; /* never here, make compiler happy */ } @@ -4012,7 +4464,7 @@ report_packet_count(int packet_count) } } -void +static void report_new_capture_file(const char *filename) { if(capture_child) { @@ -4046,23 +4498,34 @@ report_new_capture_file(const char *filename) } } -void -report_cfilter_error(const char *cfilter, const char *errmsg) +static void +report_cfilter_error(capture_options *capture_opts, guint i, const char *errmsg) { - if (capture_child) { - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Capture filter error: %s", errmsg); - pipe_write_block(2, SP_BAD_FILTER, errmsg); - } else { - fprintf(stderr, - "Invalid capture filter: \"%s\"!\n" - "\n" - "That string isn't a valid capture filter (%s).\n" - "See the User's Guide for a description of the capture filter syntax.\n", - cfilter, errmsg); + interface_options interface_opts; + char tmp[MSG_MAX_LENGTH+1+6]; + + if (i < capture_opts->ifaces->len) { + if (capture_child) { + g_snprintf(tmp, sizeof(tmp), "%u:%s", i, errmsg); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Capture filter error: %s", errmsg); + pipe_write_block(2, SP_BAD_FILTER, tmp); + } else { + /* + * clopts_step_invalid_capfilter in test/suite-clopts.sh MUST match + * the error message below. + */ + interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); + cmdarg_err( + "Invalid capture filter: \"%s\" for interface %s!\n" + "\n" + "That string isn't a valid capture filter (%s).\n" + "See the User's Guide for a description of the capture filter syntax.", + interface_opts.cfilter, interface_opts.name, errmsg); + } } } -void +static void report_capture_error(const char *error_msg, const char *secondary_error_msg) { if(capture_child) { @@ -4072,24 +4535,30 @@ report_capture_error(const char *error_msg, const char *secondary_error_msg) "Secondary Error: %s", secondary_error_msg); sync_pipe_errmsg_to_parent(2, error_msg, secondary_error_msg); } else { - fprintf(stderr, "%s\n", error_msg); + cmdarg_err("%s", error_msg); if (secondary_error_msg[0] != '\0') - fprintf(stderr, "%s\n", secondary_error_msg); + cmdarg_err_cont("%s", secondary_error_msg); } } -void -report_packet_drops(guint32 drops) +static void +report_packet_drops(guint32 received, guint32 drops, gchar *name) { char tmp[SP_DECISIZE+1+1]; g_snprintf(tmp, sizeof(tmp), "%u", drops); if(capture_child) { - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Packets dropped: %s", tmp); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Packets received/dropped on interface %s: %u/%u", + name, received, drops); + /* XXX: Need to provide interface id, changes to consumers required. */ pipe_write_block(2, SP_DROPS, tmp); } else { - fprintf(stderr, "Packets dropped: %s\n", tmp); + fprintf(stderr, + "Packets received/dropped on interface %s: %u/%u (%.1f%%)\n", + name, received, drops, + received ? 100.0 * received / (received + drops) : 0.0); /* stderr could be line buffered */ fflush(stderr); } @@ -4153,6 +4622,6 @@ signal_pipe_check_running(void) * indent-tabs-mode: nil * End: * - * vi: set shiftwidth=4 tabstop=8 expandtab + * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */