Dumpcap+Qt: Add support for `-a packets:NUM` and `-b packets:NUM`.
authorGerald Combs <gerald@wireshark.org>
Wed, 31 Oct 2018 09:03:04 +0000 (10:03 +0100)
committerAnders Broman <a.broman58@gmail.com>
Fri, 9 Nov 2018 05:55:11 +0000 (05:55 +0000)
Add the ability to rotate files after a specified number of packets (`-b
packets:NUM`). Move some condition checks to capture_loop_write_packet_cb.

Add `-a packets:NUM` in order to be consistent. It is functionally
equivalent to the `-c` flag.

Add a corresponding "packets" option to the Capture Interfaces dialog
Output tab.

Add initial tests for autostop and ringbuffer conditions.

Change-Id: I66eb968927ed287deb8edb96db96d7c73526c257
Reviewed-on: https://code.wireshark.org/review/30534
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
12 files changed:
capchild/capture_sync.c
capture_opts.c
capture_opts.h
doc/dumpcap.pod
doc/tshark.pod
doc/wireshark.pod.template
docbook/release-notes.asciidoc
dumpcap.c
test/suite_capture.py
test/util_dump_dhcp_pcap.py
ui/qt/capture_interfaces_dialog.cpp
ui/qt/capture_interfaces_dialog.ui

index c3ae9d31c46046d6f7141ee5af63d620261a7962..6da5f63f4b842903a2582ded4b0d0418ef9506cc 100644 (file)
@@ -194,26 +194,6 @@ init_pipe_args(int *argc) {
 gboolean
 sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, info_data_t* cap_data, void (*update_cb)(void))
 {
-    char ssnap[ARGV_NUMBER_LEN];
-    char scount[ARGV_NUMBER_LEN];
-    char sfilesize[ARGV_NUMBER_LEN];
-    char sfile_duration[ARGV_NUMBER_LEN];
-    char sfile_interval[ARGV_NUMBER_LEN];
-    char sring_num_files[ARGV_NUMBER_LEN];
-    char sautostop_files[ARGV_NUMBER_LEN];
-    char sautostop_filesize[ARGV_NUMBER_LEN];
-    char sautostop_duration[ARGV_NUMBER_LEN];
-#ifdef HAVE_PCAP_REMOTE
-    char sauth[256];
-#endif
-#ifdef HAVE_PCAP_SETSAMPLING
-    char ssampling[ARGV_NUMBER_LEN];
-#endif
-
-#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
-    char buffer_size[ARGV_NUMBER_LEN];
-#endif
-
 #ifdef _WIN32
     HANDLE sync_pipe_read;                  /* pipe used to send messages from child to parent */
     HANDLE sync_pipe_write;                 /* pipe used to send messages from child to parent */
@@ -272,36 +252,49 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
 
     if (capture_opts->multi_files_on) {
         if (capture_opts->has_autostop_filesize) {
+            char sfilesize[ARGV_NUMBER_LEN];
             argv = sync_pipe_add_arg(argv, &argc, "-b");
             g_snprintf(sfilesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize);
             argv = sync_pipe_add_arg(argv, &argc, sfilesize);
         }
 
         if (capture_opts->has_file_duration) {
+            char sfile_duration[ARGV_NUMBER_LEN];
             argv = sync_pipe_add_arg(argv, &argc, "-b");
             g_snprintf(sfile_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->file_duration);
             argv = sync_pipe_add_arg(argv, &argc, sfile_duration);
         }
 
         if (capture_opts->has_file_interval) {
+            char sfile_interval[ARGV_NUMBER_LEN];
             argv = sync_pipe_add_arg(argv, &argc, "-b");
             g_snprintf(sfile_interval, ARGV_NUMBER_LEN, "interval:%d",capture_opts->file_interval);
             argv = sync_pipe_add_arg(argv, &argc, sfile_interval);
         }
 
+        if (capture_opts->has_file_packets) {
+            char sfile_packets[ARGV_NUMBER_LEN];
+            argv = sync_pipe_add_arg(argv, &argc, "-b");
+            g_snprintf(sfile_packets, ARGV_NUMBER_LEN, "packets:%d",capture_opts->file_packets);
+            argv = sync_pipe_add_arg(argv, &argc, sfile_packets);
+        }
+
         if (capture_opts->has_ring_num_files) {
+            char sring_num_files[ARGV_NUMBER_LEN];
             argv = sync_pipe_add_arg(argv, &argc, "-b");
             g_snprintf(sring_num_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->ring_num_files);
             argv = sync_pipe_add_arg(argv, &argc, sring_num_files);
         }
 
         if (capture_opts->has_autostop_files) {
+            char sautostop_files[ARGV_NUMBER_LEN];
             argv = sync_pipe_add_arg(argv, &argc, "-a");
             g_snprintf(sautostop_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->autostop_files);
             argv = sync_pipe_add_arg(argv, &argc, sautostop_files);
         }
     } else {
         if (capture_opts->has_autostop_filesize) {
+            char sautostop_filesize[ARGV_NUMBER_LEN];
             argv = sync_pipe_add_arg(argv, &argc, "-a");
             g_snprintf(sautostop_filesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize);
             argv = sync_pipe_add_arg(argv, &argc, sautostop_filesize);
@@ -309,12 +302,14 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
     }
 
     if (capture_opts->has_autostop_packets) {
+        char scount[ARGV_NUMBER_LEN];
         argv = sync_pipe_add_arg(argv, &argc, "-c");
         g_snprintf(scount, ARGV_NUMBER_LEN, "%d",capture_opts->autostop_packets);
         argv = sync_pipe_add_arg(argv, &argc, scount);
     }
 
     if (capture_opts->has_autostop_duration) {
+        char sautostop_duration[ARGV_NUMBER_LEN];
         argv = sync_pipe_add_arg(argv, &argc, "-a");
         g_snprintf(sautostop_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->autostop_duration);
         argv = sync_pipe_add_arg(argv, &argc, sautostop_duration);
@@ -338,6 +333,7 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
             argv = sync_pipe_add_arg(argv, &argc, interface_opts->cfilter);
         }
         if (interface_opts->has_snaplen) {
+            char ssnap[ARGV_NUMBER_LEN];
             argv = sync_pipe_add_arg(argv, &argc, "-s");
             g_snprintf(ssnap, ARGV_NUMBER_LEN, "%d", interface_opts->snaplen);
             argv = sync_pipe_add_arg(argv, &argc, ssnap);
@@ -358,6 +354,7 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
 
 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
         if (interface_opts->buffer_size != DEFAULT_CAPTURE_BUFFER_SIZE) {
+            char buffer_size[ARGV_NUMBER_LEN];
             argv = sync_pipe_add_arg(argv, &argc, "-B");
             if(interface_opts->buffer_size == 0x00)
                 interface_opts->buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE;
@@ -380,6 +377,7 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
             argv = sync_pipe_add_arg(argv, &argc, "-r");
 
         if (interface_opts->auth_type == CAPTURE_AUTH_PWD) {
+            char sauth[256];
             argv = sync_pipe_add_arg(argv, &argc, "-A");
             g_snprintf(sauth, sizeof(sauth), "%s:%s",
                        interface_opts->auth_username,
@@ -390,6 +388,7 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
 
 #ifdef HAVE_PCAP_SETSAMPLING
         if (interface_opts->sampling_method != CAPTURE_SAMP_NONE) {
+            char ssampling[ARGV_NUMBER_LEN];
             argv = sync_pipe_add_arg(argv, &argc, "-m");
             g_snprintf(ssampling, ARGV_NUMBER_LEN, "%s:%d",
                        interface_opts->sampling_method == CAPTURE_SAMP_BY_COUNT ? "count" :
index 1a81b7fc320ebece329682636014f9a3695b5238..4750cddd192de6c8529d8f0facf9aa93159417c1 100644 (file)
@@ -101,6 +101,8 @@ capture_opts_init(capture_options *capture_opts)
     capture_opts->file_duration                   = 60.0;             /* 1 min */
     capture_opts->has_file_interval               = FALSE;
     capture_opts->file_interval                   = 60;               /* 1 min */
+    capture_opts->has_file_packets                = FALSE;
+    capture_opts->file_packets                    = 0;
     capture_opts->has_ring_num_files              = FALSE;
     capture_opts->ring_num_files                  = RINGBUFFER_MIN_NUM_FILES;
 
@@ -243,6 +245,7 @@ capture_opts_log(const char *log_domain, GLogLevelFlags log_level, capture_optio
     g_log(log_domain, log_level, "MultiFilesOn        : %u", capture_opts->multi_files_on);
     g_log(log_domain, log_level, "FileDuration    (%u) : %.3f", capture_opts->has_file_duration, capture_opts->file_duration);
     g_log(log_domain, log_level, "FileInterval    (%u) : %u", capture_opts->has_file_interval, capture_opts->file_interval);
+    g_log(log_domain, log_level, "FilePackets     (%u) : %u", capture_opts->has_file_packets, capture_opts->file_packets);
     g_log(log_domain, log_level, "RingNumFiles    (%u) : %u", capture_opts->has_ring_num_files, capture_opts->ring_num_files);
 
     g_log(log_domain, log_level, "AutostopFiles   (%u) : %u", capture_opts->has_autostop_files, capture_opts->autostop_files);
@@ -295,6 +298,9 @@ set_autostop_criterion(capture_options *capture_opts, const char *autostoparg)
         capture_opts->multi_files_on = TRUE;
         capture_opts->has_autostop_files = TRUE;
         capture_opts->autostop_files = get_positive_int(p,"autostop files");
+    } else if (strcmp(autostoparg,"packets") == 0) {
+        capture_opts->has_autostop_packets = TRUE;
+        capture_opts->autostop_packets = get_positive_int(p,"packet count");
     } else {
         return FALSE;
     }
@@ -401,6 +407,9 @@ get_ring_arguments(capture_options *capture_opts, const char *arg)
     } else if (strcmp(arg,"interval") == 0) {
         capture_opts->has_file_interval = TRUE;
         capture_opts->file_interval = get_positive_int(p, "ring buffer interval");
+    } else if (strcmp(arg,"packets") == 0) {
+        capture_opts->has_file_packets = TRUE;
+        capture_opts->file_packets = get_positive_int(p, "ring buffer packet count");
     }
 
     *colonp = ':';    /* put the colon back */
@@ -805,6 +814,7 @@ capture_opts_add_opt(capture_options *capture_opts, int opt, const char *optarg_
         break;
 #endif
     case 'c':        /* Capture n packets */
+        /* XXX Use set_autostop_criterion instead? */
         capture_opts->has_autostop_packets = TRUE;
         capture_opts->autostop_packets = get_positive_int(optarg_str_p, "packet count");
         break;
index a403b495a249d45c3f84bbb4f4638016521d8ed8..8f2e9648ea20e0e7ca98d376f15631f24f7e3b93 100644 (file)
@@ -293,13 +293,16 @@ typedef struct capture_options_tag {
     gdouble            file_duration;         /**< Switch file after n seconds */
     gboolean           has_file_interval;     /**< TRUE if ring interval specified */
     gint32             file_interval;         /**< Create time intervals of n seconds */
+    gboolean           has_file_packets;      /**< TRUE if ring packet count is
+                                                   specified */
+    int                file_packets;          /**< Switch file after n packets */
     gboolean           has_ring_num_files;    /**< TRUE if ring num_files specified */
     guint32            ring_num_files;        /**< Number of multiple buffer files */
 
     /* autostop conditions */
     gboolean           has_autostop_files;    /**< TRUE if maximum number of capture files
                                                    are specified */
-    gint32             autostop_files;        /**< Maximum number of capture files */
+    int                autostop_files;        /**< Maximum number of capture files */
 
     gboolean           has_autostop_packets;  /**< TRUE if maximum packet count is
                                                    specified */
index 87ec453ff8ab36c8aa977860b8538b2a062b5acd..ae2e024ff48886b2a3479ee31ba4fe2d9d17e34a 100644 (file)
@@ -74,13 +74,16 @@ where I<test> is one of:
 B<duration>:I<value> Stop writing to a capture file after I<value> seconds have
 elapsed. Floating point values (e.g. 0.5) are allowed.
 
+B<files>:I<value> Stop writing to capture files after I<value> number of files
+were written.
+
 B<filesize>:I<value> Stop writing to a capture file after it reaches a size of
 I<value> kB. If this option is used together with the -b option, dumpcap will
 stop writing to the current capture file and switch to the next one if filesize
 is reached.  Note that the filesize is limited to a maximum value of 2 GiB.
 
-B<files>:I<value> Stop writing to capture files after I<value> number of files
-were written.
+B<packets>:I<value> Stop writing to a capture file after I<value> packets
+have been written. Same as B<-c> E<lt>capture packet countE<gt>.
 
 =item -b  E<lt>capture ring buffer optionE<gt>
 
@@ -106,12 +109,6 @@ B<duration>:I<value> switch to the next file after I<value> seconds have
 elapsed, even if the current file is not completely filled up. Floating
 point values (e.g. 0.5) are allowed.
 
-B<interval>:I<value> switch to the next file when the time is an exact
-multiple of I<value> seconds
-
-B<filesize>:I<value> switch to the next file after it reaches a size of
-I<value> kB.  Note that the filesize is limited to a maximum value of 2 GiB.
-
 B<files>:I<value> begin again with the first file after I<value> number of
 files were written (form a ring buffer).  This value must be less than 100000.
 Caution should be used when using large numbers of files: some filesystems do
@@ -121,6 +118,15 @@ control when to go to the next file.  It should be noted that each B<-b>
 parameter takes exactly one criterion; to specify two criterion, each must be
 preceded by the B<-b> option.
 
+B<filesize>:I<value> switch to the next file after it reaches a size of
+I<value> kB.  Note that the filesize is limited to a maximum value of 2 GiB.
+
+B<interval>:I<value> switch to the next file when the time is an exact
+multiple of I<value> seconds
+
+B<packets>:I<value> switch to the next file after it contains I<value>
+packets.
+
 Example: B<-b filesize:1000 -b files:5> results in a ring buffer of five files
 of size one megabyte each.
 
@@ -148,7 +154,7 @@ the default capture buffer size is used instead.
 =item -c  E<lt>capture packet countE<gt>
 
 Set the maximum number of packets to read when capturing live
-data.
+data. Same as B<-a packets:>E<lt>capture packet countE<gt>.
 
 =item -C  E<lt>byte limitE<gt>
 
index 7fa123efaf6ad9c0139986b1e85132dae703c05c..b3e0924b74eac844f258a02e021b99098de79372 100644 (file)
@@ -218,6 +218,9 @@ where I<test> is one of:
 B<duration>:I<value> Stop writing to a capture file after I<value> seconds
 have elapsed. Floating point values (e.g. 0.5) are allowed.
 
+B<files>:I<value> Stop writing to capture files after I<value> number of files
+were written.
+
 B<filesize>:I<value> Stop writing to a capture file after it reaches a size of
 I<value> kB.  If this option is used together with the -b option, B<TShark>
 will stop writing to the current capture file and switch to the next one if
@@ -226,8 +229,8 @@ the file after the number of bytes read exceeds this number (the complete
 packet  will be read, so more bytes than this number may be read).  Note that
 the filesize is limited to a maximum value of 2 GiB.
 
-B<files>:I<value> Stop writing to capture files after I<value> number of files
-were written.
+B<packets>:I<value> switch to the next file after it contains I<value>
+packets. Same as B<-c>E<lt>capture packet countE<gt>.
 
 =item -b  E<lt>capture ring buffer optionE<gt>
 
@@ -253,12 +256,6 @@ B<duration>:I<value> switch to the next file after I<value> seconds have
 elapsed, even if the current file is not completely filled up. Floating
 point values (e.g. 0.5) are allowed.
 
-B<interval>:I<value> switch to the next file when the time is an exact
-multiple of I<value> seconds
-
-B<filesize>:I<value> switch to the next file after it reaches a size of
-I<value> kB.  Note that the filesize is limited to a maximum value of 2 GiB.
-
 B<files>:I<value> begin again with the first file after I<value> number of
 files were written (form a ring buffer).  This value must be less than 100000.
 Caution should be used when using large numbers of files: some filesystems do
@@ -268,6 +265,15 @@ control when to go to the next file.  It should be noted that each B<-b>
 parameter takes exactly one criterion; to specify two criterion, each must be
 preceded by the B<-b> option.
 
+B<filesize>:I<value> switch to the next file after it reaches a size of
+I<value> kB.  Note that the filesize is limited to a maximum value of 2 GiB.
+
+B<interval>:I<value> switch to the next file when the time is an exact
+multiple of I<value> seconds
+
+B<packets>:I<value> switch to the next file after it contains I<value>
+packets.
+
 Example: B<tshark -b filesize:1000 -b files:5> results in a ring buffer of five files
 of size one megabyte each.
 
@@ -295,7 +301,8 @@ the default capture buffer size is used instead.
 =item -c  E<lt>capture packet countE<gt>
 
 Set the maximum number of packets to read when capturing live
-data.  If reading a capture file, set the maximum number of packets to read.
+data. Same as B<-a packets:>E<lt>capture packet countE<gt>.
+If reading a capture file, set the maximum number of packets to read.
 
 =item -C  E<lt>configuration profileE<gt>
 
index 77eaeda232c9c2cf1d0e1cd57dc46fc955d98184..7cee21502ff84e721c16e42dfbaa5c48e4e48250 100644 (file)
@@ -242,14 +242,17 @@ where I<test> is one of:
 B<duration>:I<value> Stop writing to a capture file after I<value> seconds have
 elapsed. Floating point values (e.g. 0.5) are allowed.
 
+B<files>:I<value> Stop writing to capture files after I<value> number of files
+were written.
+
 B<filesize>:I<value> Stop writing to a capture file after it reaches a size of
 I<value> kB.  If this option is used together with the -b option, Wireshark
 will stop writing to the current capture file and switch to the next one if
 filesize is reached.  Note that the filesize is limited to a maximum value of
 2 GiB.
 
-B<files>:I<value> Stop writing to capture files after I<value> number of files
-were written.
+B<packets>:I<value> switch to the next file after it contains I<value>
+packets. Same as B<-c>E<lt>capture packet countE<gt>.
 
 =item -b  E<lt>capture ring buffer optionE<gt>
 
@@ -275,12 +278,6 @@ B<duration>:I<value> switch to the next file after I<value> seconds have
 elapsed, even if the current file is not completely filled up. Floating
 point values (e.g. 0.5) are allowed.
 
-B<interval>:I<value> switch to the next file when the time is an exact
-multiple of I<value> seconds
-
-B<filesize>:I<value> switch to the next file after it reaches a size of
-I<value> kB.  Note that the filesize is limited to a maximum value of 2 GiB.
-
 B<files>:I<value> begin again with the first file after I<value> number of
 files were written (form a ring buffer).  This value must be less than 100000.
 Caution should be used when using large numbers of files: some filesystems do
@@ -290,6 +287,15 @@ control when to go to the next file.  It should be noted that each B<-b>
 parameter takes exactly one criterion; to specify two criterion, each must be
 preceded by the B<-b> option.
 
+B<filesize>:I<value> switch to the next file after it reaches a size of
+I<value> kB.  Note that the filesize is limited to a maximum value of 2 GiB.
+
+B<interval>:I<value> switch to the next file when the time is an exact
+multiple of I<value> seconds
+
+B<packets>:I<value> switch to the next file after it contains I<value>
+packets.
+
 Example: B<-b filesize:1000 -b files:5> results in a ring buffer of five files
 of size one megabyte each.
 
@@ -317,7 +323,7 @@ the default capture buffer size is used instead.
 =item -c  E<lt>capture packet countE<gt>
 
 Set the maximum number of packets to read when capturing live
-data.
+data. Same as B<-a packets:>E<lt>capture packet countE<gt>.
 
 =item -C  E<lt>configuration profileE<gt>
 
index b66743f20188b479487a620b2b5af6651d38127f..4fad9febd7aa5245d5caea4bf42242647e3d140d 100644 (file)
@@ -60,6 +60,7 @@ since version 2.6.0:
 * When importing from hex dump, it's now possible to add an ExportPDU header with a payload name. This
   calls the specific dissector directly without lower protocols.
 * sshdump and ciscodump can now use a proxy for the ssh connection.
+* Dumpcap now supports the `-a packets:NUM` and `-b packets:NUM` options.
 
 === Removed Features and Support
 
index b66e964f795832fc7f4f2a6135606d7e52978d30..75903384228935e1927602263635950ee90225b7 100644 (file)
--- a/dumpcap.c
+++ b/dumpcap.c
@@ -291,8 +291,7 @@ 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      packets_captured;    /**< Number of packets we have already captured */
     guint     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 */
@@ -301,8 +300,14 @@ typedef struct _loop_data {
     /* output file(s) */
     FILE     *pdh;
     int       save_file_fd;
-    guint64   bytes_written;
-    guint32   autostop_files;
+    guint64   bytes_written;       /**< Bytes written for the current file. */
+    /* autostop conditions */
+    int       packets_written;     /**< Packets written for the current file. */
+    int       file_count;
+    /* ring buffer conditions */
+    GTimer  *file_duration_timer;
+    time_t   next_interval_time;
+    int      interval_s;
 } loop_data;
 
 typedef struct _pcap_queue_element {
@@ -314,14 +319,6 @@ typedef struct _pcap_queue_element {
     u_char             *pd;
 } pcap_queue_element;
 
-/* Conditions required by do_file_switch_or_stop */
-typedef struct _condition_data {
-    GTimer  *file_duration_timer;
-    time_t   next_interval_time;
-    int      interval_s;
-    unsigned autostop_files;
-} condition_data;
-
 /*
  * Standard secondary message for unexpected errors.
  */
@@ -455,6 +452,7 @@ print_usage(FILE *output)
     fprintf(output, "  -a <autostop cond.> ...  duration:NUM - stop after NUM seconds\n");
     fprintf(output, "                           filesize:NUM - stop this file after NUM kB\n");
     fprintf(output, "                              files:NUM - stop after NUM files\n");
+    fprintf(output, "                            packets:NUM - stop after NUM packets\n");
     /*fprintf(output, "\n");*/
     fprintf(output, "Output (files):\n");
     fprintf(output, "  -w <filename>            name of file to save (def: tempfile)\n");
@@ -463,6 +461,7 @@ print_usage(FILE *output)
     fprintf(output, "                           interval:NUM - create time intervals of 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, "                            packets:NUM - ringbuffer: replace after NUM packets\n");
     fprintf(output, "  -n                       use pcapng format instead of pcap (default)\n");
     fprintf(output, "  -P                       use libpcap format instead of pcapng\n");
     fprintf(output, "  --capture-comment <comment>\n");
@@ -1097,7 +1096,7 @@ report_capture_count(gboolean reportit)
 {
     /* Don't print this if we're a capture child. */
     if (!capture_child && reportit) {
-        fprintf(stderr, "\rPackets captured: %d\n", global_ld.packet_count);
+        fprintf(stderr, "\rPackets captured: %d\n", global_ld.packets_captured);
         /* stderr could be line buffered */
         fflush(stderr);
     }
@@ -2275,7 +2274,7 @@ pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errms
              * instead stop with an error.
              */
             g_snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)",
-                       ld->packet_count+1, pcap_info->rechdr.hdr.incl_len);
+                       ld->packets_captured+1, pcap_info->rechdr.hdr.incl_len);
             break;
         }
 
@@ -2505,7 +2504,7 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
             * instead stop with an error.
             */
             g_snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)",
-                    ld->packet_count+1, bh->block_total_length);
+                    ld->packets_captured+1, bh->block_total_length);
             break;
         }
 
@@ -3086,7 +3085,7 @@ capture_loop_dispatch(loop_data *ld,
     gint   packet_count_before;
     int    sel_ret;
 
-    packet_count_before = ld->packet_count;
+    packet_count_before = ld->packets_captured;
     if (pcap_src->from_cap_pipe) {
         /* dispatch from capture pipe */
 #ifdef LOG_CAPTURE_VERBOSE
@@ -3254,7 +3253,7 @@ capture_loop_dispatch(loop_data *ld,
     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: %d new packet%s", inpkts, plurality(inpkts, "", "s"));
 #endif
 
-    return ld->packet_count - packet_count_before;
+    return ld->packets_captured - packet_count_before;
 }
 
 #ifdef _WIN32
@@ -3462,8 +3461,7 @@ static time_t get_next_time_interval(int interval_s) {
 /* Do the work of handling either the file size or file duration capture
    conditions being reached, and switching files or stopping. */
 static gboolean
-do_file_switch_or_stop(capture_options *capture_opts,
-                       condition_data *conditions)
+do_file_switch_or_stop(capture_options *capture_opts)
 {
     guint             i;
     capture_src      *pcap_src;
@@ -3471,8 +3469,8 @@ do_file_switch_or_stop(capture_options *capture_opts,
     gboolean          successful;
 
     if (capture_opts->multi_files_on) {
-        if (conditions->autostop_files &&
-            ++global_ld.autostop_files >= conditions->autostop_files) {
+        if (capture_opts->has_autostop_files &&
+            ++global_ld.file_count >= capture_opts->autostop_files) {
             /* no files left: stop here */
             global_ld.go = FALSE;
             return FALSE;
@@ -3484,6 +3482,7 @@ do_file_switch_or_stop(capture_options *capture_opts,
 
             /* File switch succeeded: reset the conditions */
             global_ld.bytes_written = 0;
+            global_ld.packets_written = 0;
             pcap_src = g_array_index(global_ld.pcaps, capture_src *, 0);
             if (pcap_src->from_pcapng) {
                 /* Write the saved SHB and all IDBs to start of next file */
@@ -3554,11 +3553,11 @@ do_file_switch_or_stop(capture_options *capture_opts,
                 global_ld.go = FALSE;
                 return FALSE;
             }
-            if (conditions->file_duration_timer) {
-                g_timer_reset(conditions->file_duration_timer);
+            if (global_ld.file_duration_timer) {
+                g_timer_reset(global_ld.file_duration_timer);
             }
-            if (conditions->next_interval_time) {
-                conditions->next_interval_time = get_next_time_interval(conditions->interval_s);
+            if (global_ld.next_interval_time) {
+                global_ld.next_interval_time = get_next_time_interval(global_ld.interval_s);
             }
             fflush(global_ld.pdh);
             if (!quiet)
@@ -3611,7 +3610,6 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 #endif
     int               err_close;
     int               inpkts;
-    condition_data    conditions;
     GTimer           *autostop_duration_timer = NULL;
     gboolean          write_ok;
     gboolean          close_ok;
@@ -3627,19 +3625,18 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 
     /* init the loop data */
     global_ld.go                  = TRUE;
-    global_ld.packet_count        = 0;
+    global_ld.packets_captured    = 0;
 #ifdef SIGINFO
     global_ld.report_packet_count = FALSE;
 #endif
-    if (capture_opts->has_autostop_packets)
-        global_ld.packet_max      = capture_opts->autostop_packets;
-    else
-        global_ld.packet_max      = 0;        /* no limit */
     global_ld.inpkts_to_sync_pipe = 0;
     global_ld.err                 = 0;  /* no error seen yet */
     global_ld.pdh                 = NULL;
-    global_ld.autostop_files      = 0;
     global_ld.save_file_fd        = -1;
+    global_ld.file_count          = 0;
+    global_ld.file_duration_timer = NULL;
+    global_ld.next_interval_time  = 0;
+    global_ld.interval_s          = 0;
 
     /* We haven't yet gotten the capture statistics. */
     *stats_known      = FALSE;
@@ -3713,11 +3710,9 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
         report_new_capture_file(capture_opts->save_file);
     }
 
-    /* initialize capture stop (and alike) conditions */
-    memset(&conditions, 0, sizeof(conditions));
     if (capture_opts->has_file_interval) {
-        conditions.interval_s = capture_opts->file_interval;
-        conditions.next_interval_time = get_next_time_interval(conditions.interval_s);
+        global_ld.interval_s = capture_opts->file_interval;
+        global_ld.next_interval_time = get_next_time_interval(global_ld.interval_s);
     }
     /* create stop conditions */
     if (capture_opts->has_autostop_filesize) {
@@ -3731,11 +3726,8 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 
     if (capture_opts->multi_files_on) {
         if (capture_opts->has_file_duration) {
-            conditions.file_duration_timer = g_timer_new();
+            global_ld.file_duration_timer = g_timer_new();
         }
-
-        if (capture_opts->has_autostop_files)
-            conditions.autostop_files = capture_opts->autostop_files;
     }
 
     /* init the time values */
@@ -3809,8 +3801,8 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 #ifdef SIGINFO
         /* Were we asked to print packet counts by the SIGINFO handler? */
         if (global_ld.report_packet_count) {
-            fprintf(stderr, "%u packet%s captured\n", global_ld.packet_count,
-                    plurality(global_ld.packet_count, "", "s"));
+            fprintf(stderr, "%u packet%s captured\n", global_ld.packets_captured,
+                    plurality(global_ld.packets_captured, "", "s"));
             global_ld.report_packet_count = FALSE;
         }
 #endif
@@ -3825,14 +3817,6 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
         if (inpkts > 0) {
             global_ld.inpkts_to_sync_pipe += inpkts;
 
-            /* check capture size condition */
-            if (capture_opts->has_autostop_filesize &&
-                capture_opts->autostop_filesize > 0 &&
-                global_ld.bytes_written / 1000 >= capture_opts->autostop_filesize) {
-                /* Capture size limit reached, do we have another file? */
-                if (!do_file_switch_or_stop(capture_opts, &conditions))
-                    continue;
-            } /* cnd_autostop_size */
             if (capture_opts->output_to_pipe) {
                 fflush(global_ld.pdh);
             }
@@ -3882,16 +3866,16 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
             }
 
             /* check capture file duration condition */
-            if (conditions.file_duration_timer != NULL && g_timer_elapsed(conditions.file_duration_timer, NULL) >= capture_opts->file_duration) {
+            if (global_ld.file_duration_timer != NULL && g_timer_elapsed(global_ld.file_duration_timer, NULL) >= capture_opts->file_duration) {
                 /* duration limit reached, do we have another file? */
-                if (!do_file_switch_or_stop(capture_opts, &conditions))
+                if (!do_file_switch_or_stop(capture_opts))
                     continue;
             } /* cnd_file_duration */
 
             /* check capture file interval condition */
-            if (conditions.interval_s && time(NULL) >= conditions.next_interval_time) {
+            if (global_ld.interval_s && time(NULL) >= global_ld.next_interval_time) {
                 /* end of interval reached, do we have another file? */
-                if (!do_file_switch_or_stop(capture_opts, &conditions))
+                if (!do_file_switch_or_stop(capture_opts))
                     continue;
             } /* cnd_file_interval */
         }
@@ -3937,8 +3921,8 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 
 
     /* delete stop conditions */
-    if (conditions.file_duration_timer != NULL)
-        g_timer_destroy(conditions.file_duration_timer);
+    if (global_ld.file_duration_timer != NULL)
+        g_timer_destroy(global_ld.file_duration_timer);
     if (autostop_duration_timer != NULL)
         g_timer_destroy(autostop_duration_timer);
 
@@ -4210,10 +4194,13 @@ capture_loop_write_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_he
                   "Wrote a packet of length %d captured on interface %u.",
                    bh->block_total_length, pcap_src->interface_id);
 #endif
-            global_ld.packet_count++;
+            global_ld.packets_captured++;
+            global_ld.packets_written++;
             pcap_src->received++;
+
             /* if the user told us to stop after x packets, do we already have enough? */
-            if ((global_ld.packet_max > 0) && (global_ld.packet_count >= global_ld.packet_max)) {
+            if (global_capture_opts.has_autostop_packets && global_ld.packets_captured >= global_capture_opts.autostop_packets) {
+                fflush(global_ld.pdh);
                 global_ld.go = FALSE;
             }
         }
@@ -4271,11 +4258,28 @@ capture_loop_write_packet_cb(u_char *pcap_src_p, const struct pcap_pkthdr *phdr,
                   "Wrote a packet of length %d captured on interface %u.",
                    phdr->caplen, pcap_src->interface_id);
 #endif
-            global_ld.packet_count++;
+            global_ld.packets_captured++;
+            global_ld.packets_written++;
             pcap_src->received++;
-            /* if the user told us to stop after x packets, do we already have enough? */
-            if ((global_ld.packet_max > 0) && (global_ld.packet_count >= global_ld.packet_max)) {
+
+            /* check -c NUM / -a packets:NUM */
+            if (global_capture_opts.has_autostop_packets && global_ld.packets_captured >= global_capture_opts.autostop_packets) {
+                fflush(global_ld.pdh);
                 global_ld.go = FALSE;
+                return;
+            }
+            /* check -b packets:NUM */
+            if (global_capture_opts.has_file_packets && global_ld.packets_written >= global_capture_opts.file_packets) {
+                do_file_switch_or_stop(&global_capture_opts);
+                return;
+            }
+            /* check -a filesize:NUM */
+            if (global_capture_opts.has_autostop_filesize &&
+                global_capture_opts.autostop_filesize > 0 &&
+                global_ld.bytes_written / 1000 >= global_capture_opts.autostop_filesize) {
+                /* Capture size limit reached, do we have another file? */
+                do_file_switch_or_stop(&global_capture_opts);
+                return;
             }
         }
     }
@@ -5034,9 +5038,10 @@ real_main(int argc, char *argv[])
             }
             if (!global_capture_opts.has_autostop_filesize &&
                 !global_capture_opts.has_file_duration &&
-                !global_capture_opts.has_file_interval) {
-                cmdarg_err("Ring buffer requested, but no maximum capture file size, duration"
-                           "or interval were specified.");
+                !global_capture_opts.has_file_interval &&
+                !global_capture_opts.has_file_packets) {
+                cmdarg_err("Ring buffer requested, but no maximum capture file size, duration "
+                           "interval, or packets were specified.");
 #if 0
                 /* XXX - this must be redesigned as the conditions changed */
                 global_capture_opts.multi_files_on = FALSE;
index cdb78b4a2fdb4bc340cccdee60b48c6f4dd06bc4..eb1a4ffcab62eb2b0702387ad91386cf21e7ced2 100644 (file)
@@ -10,6 +10,7 @@
 '''Capture tests'''
 
 import config
+import glob
 import os
 import re
 import subprocess
@@ -17,6 +18,7 @@ import subprocesstest
 import sys
 import time
 import unittest
+import uuid
 
 capture_duration = 5
 
@@ -139,11 +141,6 @@ def check_capture_stdin(self, cmd=None):
     if (pipe_returncode == 0):
         self.checkPacketCount(8)
 
-def check_capture_2multi_10packets(self, cmd=None):
-    # This was present in the Bash version but was incorrect and not part of any suite.
-    # It's apparently intended to test file rotation.
-    self.skipTest('Not yet implemented')
-
 def check_capture_read_filter(self, cmd=None):
     if not config.canCapture():
         self.skipTest('Test requires capture privileges and an interface.')
@@ -207,6 +204,86 @@ def check_capture_snapshot_len(self, cmd=None):
     if (capture_returncode == 0):
         self.checkPacketCount(0, cap_file=testout2_file)
 
+def check_dumpcap_autostop_stdin(self, packets=None, filesize=None):
+    # Similar to check_capture_stdin.
+    cmd = config.cmd_dumpcap
+    capture_file = os.path.join(config.capture_dir, 'dhcp.pcap')
+    testout_file = self.filename_from_id(testout_pcap)
+    cat100_dhcp_cmd = subprocesstest.cat_dhcp_command('cat100')
+    condition='oops:invalid'
+
+    self.assertTrue(packets is not None or filesize is not None, 'Need one of packets or filesize')
+    self.assertFalse(packets is not None and filesize is not None, 'Need one of packets or filesize')
+
+    if packets is not None:
+        condition = 'packets:{}'.format(packets)
+    elif filesize is not None:
+        condition = 'filesize:{}'.format(filesize)
+
+    capture_cmd = subprocesstest.capture_command(cmd,
+        '-i', '-',
+        '-w', testout_file,
+        '-a', condition,
+        shell=True
+    )
+    pipe_proc = self.runProcess(cat100_dhcp_cmd + ' | ' + capture_cmd, shell=True)
+    pipe_returncode = pipe_proc.returncode
+    self.assertEqual(pipe_returncode, 0)
+    self.assertTrue(os.path.isfile(testout_file))
+    if (pipe_returncode != 0):
+        return
+
+    if packets is not None:
+        self.checkPacketCount(packets)
+    elif filesize is not None:
+        capturekb = os.path.getsize(testout_file) / 1000
+        self.assertGreaterEqual(capturekb, filesize)
+
+def check_dumpcap_ringbuffer_stdin(self, packets=None, filesize=None):
+    # Similar to check_capture_stdin.
+    cmd = config.cmd_dumpcap
+    capture_file = os.path.join(config.capture_dir, 'dhcp.pcap')
+    rb_unique = 'dhcp_rb_' + uuid.uuid4().hex[:6] # Random ID
+    testout_file = '{}.{}.pcapng'.format(self.id(), rb_unique)
+    testout_glob = '{}.{}_*.pcapng'.format(self.id(), rb_unique)
+    cat100_dhcp_cmd = subprocesstest.cat_dhcp_command('cat100')
+    condition='oops:invalid'
+
+    self.assertTrue(packets is not None or filesize is not None, 'Need one of packets or filesize')
+    self.assertFalse(packets is not None and filesize is not None, 'Need one of packets or filesize')
+
+    if packets is not None:
+        condition = 'packets:{}'.format(packets)
+    elif filesize is not None:
+        condition = 'filesize:{}'.format(filesize)
+
+    capture_cmd = subprocesstest.capture_command(cmd,
+        '-i', '-',
+        '-w', testout_file,
+        '-a', 'files:2',
+        '-b', condition,
+        shell=True
+    )
+    pipe_proc = self.runProcess(cat100_dhcp_cmd + ' | ' + capture_cmd, shell=True)
+    pipe_returncode = pipe_proc.returncode
+    self.assertEqual(pipe_returncode, 0)
+    if (pipe_returncode != 0):
+        return
+
+    rb_files = glob.glob(testout_glob)
+    for rbf in rb_files:
+        self.cleanup_files.append(rbf)
+
+    self.assertEqual(len(rb_files), 2)
+
+    for rbf in rb_files:
+        self.assertTrue(os.path.isfile(rbf))
+        if packets is not None:
+            self.checkPacketCount(packets, cap_file=rbf)
+        elif filesize is not None:
+            capturekb = os.path.getsize(rbf) / 1000
+            self.assertGreaterEqual(capturekb, filesize)
+
 class case_wireshark_capture(subprocesstest.SubprocessTestCase):
     def test_wireshark_capture_10_packets_to_file(self):
         '''Capture 10 packets from the network to a file using Wireshark'''
@@ -270,3 +347,24 @@ class case_dumpcap_capture(subprocesstest.SubprocessTestCase):
     def test_dumpcap_capture_snapshot_len(self):
         '''Capture truncated packets using Dumpcap'''
         check_capture_snapshot_len(self, cmd=config.cmd_dumpcap)
+
+class case_dumpcap_autostop(subprocesstest.SubprocessTestCase):
+    # duration, filesize, packets, files
+    def test_dumpcap_autostop_filesize(self):
+        '''Capture from stdin using Dumpcap until we reach a file size limit'''
+        check_dumpcap_autostop_stdin(self, filesize=15)
+
+    def test_dumpcap_autostop_packets(self):
+        '''Capture from stdin using Dumpcap until we reach a packet limit'''
+        check_dumpcap_autostop_stdin(self, packets=97) # Last prime before 100. Arbitrary.
+
+class case_dumpcap_ringbuffer(subprocesstest.SubprocessTestCase):
+    # duration, interval, filesize, packets, files
+    # Need a function that finds ringbuffer file names.
+    def test_dumpcap_ringbuffer_filesize(self):
+        '''Capture from stdin using Dumpcap and write multiple files until we reach a file size limit'''
+        check_dumpcap_ringbuffer_stdin(self, filesize=15)
+
+    def test_dumpcap_ringbuffer_packets(self):
+        '''Capture from stdin using Dumpcap and write multiple files until we reach a packet limit'''
+        check_dumpcap_ringbuffer_stdin(self, packets=47) # Last prime before 50. Arbitrary.
index 2e0b24281830d3a6cc81279b3fabcfc5d86fc53c..28b382376719270056557627e6197403342c41cd 100755 (executable)
@@ -17,8 +17,8 @@ import sys
 
 def main():
     parser = argparse.ArgumentParser(description='Dump dhcp.pcap')
-    parser.add_argument('dump_type', choices=['cat', 'slow', 'raw'],
-        help='cat: Just dump the file. slow: Dump the file, pause, and dump its packet records. raw: Dump only the packet records.')
+    parser.add_argument('dump_type', choices=['cat', 'cat100', 'slow', 'raw'],
+        help='cat: Just dump the file. cat100: Dump 100 packet records. slow: Dump the file, pause, and dump its packet records. raw: Dump only the packet records.')
     args = parser.parse_args()
 
     dhcp_pcap = os.path.join(os.path.dirname(__file__), 'captures', 'dhcp.pcap')
@@ -27,12 +27,17 @@ def main():
     contents = dhcp_fd.read()
     if args.dump_type != 'raw':
         os.write(1, contents)
-    if args.dump_type == 'cat':
+    if args.dump_type == 'cat100':
+        # The capture contains 4 packets. Write 96 more.
+        for _ in range(24):
+            os.write(1, contents[24:])
+    if args.dump_type.startswith('cat'):
         sys.exit(0)
     if args.dump_type == 'slow':
         time.sleep(1.5)
     # slow, raw
     os.write(1, contents[24:])
+
     sys.exit(0)
 
 if __name__ == '__main__':
index 0bef6cba84f1d056b9222febca2ab0beb79fba17..b9dc86ff9c94208f3a5e035fea07969fc25cbd37 100644 (file)
@@ -549,6 +549,10 @@ void CaptureInterfacesDialog::updateInterfaces()
     }
 
     ui->gbNewFileAuto->setChecked(global_capture_opts.multi_files_on);
+    ui->PktCheckBox->setChecked(global_capture_opts.has_file_packets);
+    if (global_capture_opts.has_file_packets) {
+        ui->PktSpinBox->setValue(global_capture_opts.file_packets);
+    }
     ui->MBCheckBox->setChecked(global_capture_opts.has_autostop_filesize);
     ui->SecsCheckBox->setChecked(global_capture_opts.has_file_interval);
     if (global_capture_opts.has_autostop_filesize) {
@@ -850,6 +854,10 @@ bool CaptureInterfacesDialog::saveOptionsToPreferences()
                 break;
             }
          }
+         global_capture_opts.has_file_packets = ui->PktCheckBox->isChecked();
+         if (global_capture_opts.has_file_packets) {
+             global_capture_opts.file_packets = ui->PktSpinBox->value();
+         }
          global_capture_opts.has_autostop_filesize = ui->MBCheckBox->isChecked();
          if (global_capture_opts.has_autostop_filesize) {
              global_capture_opts.autostop_filesize = ui->MBSpinBox->value();
@@ -878,9 +886,12 @@ bool CaptureInterfacesDialog::saveOptionsToPreferences()
              QMessageBox::warning(this, tr("Error"),
                                       tr("Multiple files: No capture file name given. You must specify a filename if you want to use multiple files."));
              return false;
-         } else if (!global_capture_opts.has_autostop_filesize && !global_capture_opts.has_file_interval) {
+         } else if (!global_capture_opts.has_autostop_filesize &&
+                    !global_capture_opts.has_file_interval &&
+                    !global_capture_opts.has_file_duration &&
+                    !global_capture_opts.has_file_packets) {
              QMessageBox::warning(this, tr("Error"),
-                                      tr("Multiple files: No file limit given. You must specify a file size or interval at which is switched to the next capture file\n if you want to use multiple files."));
+                                      tr("Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file."));
              g_free(global_capture_opts.save_file);
              global_capture_opts.save_file = NULL;
              return false;
index 7d3c416f27184972b6937d37e4d215d553327045..181f5df332cc5fdf218c8db666cca416097b88b5 100644 (file)
           <bool>true</bool>
          </property>
          <layout class="QGridLayout" name="gridLayout">
-          <item row="1" column="1">
-           <widget class="QSpinBox" name="MBSpinBox">
+          <item row="0" column="3" rowspan="4">
+           <spacer name="horizontalSpacer_8">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item row="2" column="0">
+           <widget class="QCheckBox" name="MBCheckBox">
+            <property name="text">
+             <string/>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="0">
+           <widget class="QCheckBox" name="SecsCheckBox">
+            <property name="text">
+             <string/>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="1">
+           <widget class="QSpinBox" name="SecsSpinBox">
             <property name="toolTip">
-             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If the selected file size is exceeded, capturing switches to the next file.&lt;/p&gt;&lt;p&gt;PLEASE NOTE: One option MUST be selected.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+             <string>If the selected file size is exceeded, capturing switches to the next file.
+PLEASE NOTE: One option MUST be selected.</string>
             </property>
             <property name="wrapping">
              <bool>true</bool>
             </property>
            </widget>
           </item>
-          <item row="1" column="2">
-           <widget class="QComboBox" name="MBComboBox">
-            <property name="toolTip">
-             <string>If the selected file size is exceeded, capturing switches to the next file.
-PLEASE NOTE: One option MUST be selected.</string>
-            </property>
-            <item>
-             <property name="text">
-              <string>kilobytes</string>
-             </property>
-            </item>
-            <item>
-             <property name="text">
-              <string>megabytes</string>
-             </property>
-            </item>
-            <item>
-             <property name="text">
-              <string>gigabytes</string>
-             </property>
-            </item>
-           </widget>
-          </item>
-          <item row="2" column="2">
+          <item row="3" column="2">
            <widget class="QComboBox" name="SecsComboBox">
             <property name="toolTip">
              <string>If the selected file size is exceeded, capturing switches to the next file.
@@ -331,10 +336,9 @@ PLEASE NOTE: One option MUST be selected.</string>
            </widget>
           </item>
           <item row="2" column="1">
-           <widget class="QSpinBox" name="SecsSpinBox">
+           <widget class="QSpinBox" name="MBSpinBox">
             <property name="toolTip">
-             <string>If the selected file size is exceeded, capturing switches to the next file.
-PLEASE NOTE: One option MUST be selected.</string>
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If the selected file size is exceeded, capturing switches to the next file.&lt;/p&gt;&lt;p&gt;PLEASE NOTE: One option MUST be selected.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
             </property>
             <property name="wrapping">
              <bool>true</bool>
@@ -353,30 +357,56 @@ PLEASE NOTE: One option MUST be selected.</string>
             </property>
            </widget>
           </item>
-          <item row="0" column="3" rowspan="3">
-           <spacer name="horizontalSpacer_8">
-            <property name="orientation">
-             <enum>Qt::Horizontal</enum>
-            </property>
-            <property name="sizeHint" stdset="0">
-             <size>
-              <width>40</width>
-              <height>20</height>
-             </size>
+          <item row="2" column="2">
+           <widget class="QComboBox" name="MBComboBox">
+            <property name="toolTip">
+             <string>If the selected file size is exceeded, capturing switches to the next file.
+PLEASE NOTE: One option MUST be selected.</string>
             </property>
-           </spacer>
+            <item>
+             <property name="text">
+              <string>kilobytes</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>megabytes</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>gigabytes</string>
+             </property>
+            </item>
+           </widget>
           </item>
           <item row="1" column="0">
-           <widget class="QCheckBox" name="MBCheckBox">
+           <widget class="QCheckBox" name="PktCheckBox">
             <property name="text">
              <string/>
             </property>
            </widget>
           </item>
-          <item row="2" column="0">
-           <widget class="QCheckBox" name="SecsCheckBox">
+          <item row="1" column="1">
+           <widget class="QSpinBox" name="PktSpinBox">
+            <property name="toolTip">
+             <string>Switch to the next file after the specified number of packets have been captured.</string>
+            </property>
+            <property name="buttonSymbols">
+             <enum>QAbstractSpinBox::PlusMinus</enum>
+            </property>
+            <property name="maximum">
+             <number>2147483647</number>
+            </property>
+            <property name="value">
+             <number>100000</number>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="2">
+           <widget class="QLabel" name="PktLabel">
             <property name="text">
-             <string/>
+             <string>packets</string>
             </property>
            </widget>
           </item>