/* Edit capture files. We can delete packets, adjust timestamps, or * simply convert from one format to another format. * * $Id$ * * Originally written by Richard Sharpe. * Improved by Guy Harris. * Further improved by Richard Sharpe. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include /* * Just make sure we include the prototype for strptime as well * (needed for glibc 2.2) but make sure we do this only if not * yet defined. */ #ifndef __USE_XOPEN # define __USE_XOPEN #endif #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include "wtap.h" #ifdef NEED_GETOPT_H #include "getopt.h" #endif #ifdef _WIN32 #include /* getpid */ #ifdef HAVE_WINSOCK2_H #include #endif #endif #ifdef NEED_STRPTIME_H # include "strptime.h" #endif #include "epan/crypt/crypt-md5.h" #include "epan/plugins.h" #include "epan/report_err.h" #include "epan/filesystem.h" #include #include "epan/nstime.h" #include "svnversion.h" /* * Some globals so we can pass things to various routines */ struct select_item { int inclusive; int first, second; }; /* * Duplicate frame detection */ typedef struct _fd_hash_t { md5_byte_t digest[16]; guint32 len; nstime_t time; } fd_hash_t; #define DEFAULT_DUP_DEPTH 5 /* Used with -d */ #define MAX_DUP_DEPTH 1000000 /* the maximum window (and actual size of fd_hash[]) for de-duplication */ fd_hash_t fd_hash[MAX_DUP_DEPTH]; int dup_window = DEFAULT_DUP_DEPTH; int cur_dup_entry = 0; #define ONE_MILLION 1000000 #define ONE_BILLION 1000000000 /* Weights of different errors we can introduce */ /* We should probably make these command-line arguments */ /* XXX - Should we add a bit-level error? */ #define ERR_WT_BIT 5 /* Flip a random bit */ #define ERR_WT_BYTE 5 /* Substitute a random byte */ #define ERR_WT_ALNUM 5 /* Substitute a random character in [A-Za-z0-9] */ #define ERR_WT_FMT 2 /* Substitute "%s" */ #define ERR_WT_AA 1 /* Fill the remainder of the buffer with 0xAA */ #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA) #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1) struct time_adjustment { struct timeval tv; int is_negative; }; #define MAX_SELECTIONS 512 static struct select_item selectfrm[MAX_SELECTIONS]; static int max_selected = -1; static int keep_em = 0; static int out_file_type = WTAP_FILE_PCAP; /* default to "libpcap" */ static int out_frame_type = -2; /* Leave frame type alone */ static int verbose = 0; /* Not so verbose */ static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */ static nstime_t relative_time_window = {0, 0}; /* de-dup time window */ static double err_prob = 0.0; static time_t starttime = 0; static time_t stoptime = 0; static gboolean check_startstop = FALSE; static gboolean dup_detect = FALSE; static gboolean dup_detect_by_time = FALSE; static int find_dct2000_real_data(guint8 *buf); /* Add a selection item, a simple parser for now */ static gboolean add_selection(char *sel) { char *locn; char *next; if (++max_selected >= MAX_SELECTIONS) { /* Let the user know we stopped selecting */ printf("Out of room for packet selections!\n"); return(FALSE); } printf("Add_Selected: %s\n", sel); if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */ printf("Not inclusive ..."); selectfrm[max_selected].inclusive = 0; selectfrm[max_selected].first = atoi(sel); printf(" %i\n", selectfrm[max_selected].first); } else { printf("Inclusive ..."); next = locn + 1; selectfrm[max_selected].inclusive = 1; selectfrm[max_selected].first = atoi(sel); selectfrm[max_selected].second = atoi(next); printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second); } return(TRUE); } /* Was the packet selected? */ static int selected(int recno) { int i = 0; for (i = 0; i<= max_selected; i++) { if (selectfrm[i].inclusive) { if (selectfrm[i].first <= recno && selectfrm[i].second >= recno) return 1; } else { if (recno == selectfrm[i].first) return 1; } } return 0; } /* is the packet in the selected timeframe */ static gboolean check_timestamp(wtap *wth) { struct wtap_pkthdr* pkthdr = wtap_phdr(wth); return ( pkthdr->ts.secs >= starttime ) && ( pkthdr->ts.secs <= stoptime ); } static void set_time_adjustment(char *optarg) { char *frac, *end; long val; size_t frac_digits; if (!optarg) return; /* skip leading whitespace */ while (*optarg == ' ' || *optarg == '\t') { optarg++; } /* check for a negative adjustment */ if (*optarg == '-') { time_adj.is_negative = 1; optarg++; } /* collect whole number of seconds, if any */ if (*optarg == '.') { /* only fractional (i.e., .5 is ok) */ val = 0; frac = optarg; } else { val = strtol(optarg, &frac, 10); if (frac == NULL || frac == optarg || val == LONG_MIN || val == LONG_MAX) { fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n", optarg); exit(1); } if (val < 0) { /* implies '--' since we caught '-' above */ fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n", optarg); exit(1); } } time_adj.tv.tv_sec = val; /* now collect the partial seconds, if any */ if (*frac != '\0') { /* chars left, so get fractional part */ val = strtol(&(frac[1]), &end, 10); if (*frac != '.' || end == NULL || end == frac || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) { fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n", optarg); exit(1); } } else { return; /* no fractional digits */ } /* adjust fractional portion from fractional to numerator * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */ if (frac && end) { /* both are valid */ frac_digits = end - frac - 1; /* fractional digit count (remember '.') */ while(frac_digits < 6) { /* this is frac of 10^6 */ val *= 10; frac_digits++; } } time_adj.tv.tv_usec = val; } static void set_rel_time(char *optarg) { char *frac, *end; long val; size_t frac_digits; if (!optarg) return; /* skip leading whitespace */ while (*optarg == ' ' || *optarg == '\t') { optarg++; } /* ignore negative adjustment */ if (*optarg == '-') { optarg++; } /* collect whole number of seconds, if any */ if (*optarg == '.') { /* only fractional (i.e., .5 is ok) */ val = 0; frac = optarg; } else { val = strtol(optarg, &frac, 10); if (frac == NULL || frac == optarg || val == LONG_MIN || val == LONG_MAX) { fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n", optarg); exit(1); } if (val < 0) { /* implies '--' since we caught '-' above */ fprintf(stderr, "2: editcap: \"%s\" isn't a valid rel time value\n", optarg); exit(1); } } relative_time_window.secs = val; /* now collect the partial seconds, if any */ if (*frac != '\0') { /* chars left, so get fractional part */ val = strtol(&(frac[1]), &end, 10); if (*frac != '.' || end == NULL || end == frac || val < 0 || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) { fprintf(stderr, "3: editcap: \"%s\" isn't a valid rel time value\n", optarg); exit(1); } } else { return; /* no fractional digits */ } /* adjust fractional portion from fractional to numerator * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */ if (frac && end) { /* both are valid */ frac_digits = end - frac - 1; /* fractional digit count (remember '.') */ while(frac_digits < 9) { /* this is frac of 10^9 */ val *= 10; frac_digits++; } } relative_time_window.nsecs = val; } static gboolean is_duplicate(guint8* fd, guint32 len) { int i; md5_state_t ms; cur_dup_entry++; if (cur_dup_entry >= dup_window) cur_dup_entry = 0; /* Calculate our digest */ md5_init(&ms); md5_append(&ms, fd, len); md5_finish(&ms, fd_hash[cur_dup_entry].digest); fd_hash[cur_dup_entry].len = len; /* Look for duplicates */ for (i = 0; i < dup_window; i++) { if (i == cur_dup_entry) continue; if (fd_hash[i].len == fd_hash[cur_dup_entry].len && memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) { return TRUE; } } return FALSE; } static gboolean is_duplicate_rel_time(guint8* fd, guint32 len, const nstime_t *current) { int i; md5_state_t ms; cur_dup_entry++; if (cur_dup_entry >= dup_window) cur_dup_entry = 0; /* Calculate our digest */ md5_init(&ms); md5_append(&ms, fd, len); md5_finish(&ms, fd_hash[cur_dup_entry].digest); fd_hash[cur_dup_entry].len = len; fd_hash[cur_dup_entry].time.secs = current->secs; fd_hash[cur_dup_entry].time.nsecs = current->nsecs; /* * Look for relative time related duplicates. * This is hopefully a reasonably efficient mechanism for * finding duplicates by rel time in the fd_hash[] cache. * We check starting from the most recently added hash * entries and work backwards towards older packets. * This approach allows the dup test to be terminated * when the relative time of a cached entry is found to * be beyond the dup time window. * * Of course this assumes that the input trace file is * "well-formed" in the sense that the packet timestamps are * in strict chronologically increasing order (which is NOT * always the case!!). * * The fd_hash[] table was deliberatly created large (1,000,000). * Looking for time related duplicates in large trace files with * non-fractional dup time window values can potentially take * a long time to complete. */ for (i = cur_dup_entry - 1;; i--) { nstime_t delta; int cmp; if (i < 0) { i = dup_window - 1; } if (i == cur_dup_entry) { /* * We've decremented back to where we started. * Check no more! */ break; } if (nstime_is_unset(&(fd_hash[i].time))) { /* * We've decremented to an unused fd_hash[] entry. * Check no more! */ break; } nstime_delta(&delta, current, &fd_hash[i].time); if(delta.secs < 0 || delta.nsecs < 0) { /* * A negative delta implies that the current packet * has an absolute timestamp less than the cached packet * that it is being compared to. This is NOT a normal * situation since trace files usually have packets in * chronological order (oldest to newest). * * There are several possible ways to deal with this: * 1. 'continue' dup checking with the next cached frame. * 2. 'break' from looking for a duplicate of the current frame. * 3. Take the absolute value of the delta and see if that * falls within the specifed dup time window. * * Currently this code does option 1. But it would pretty * easy to add yet-another-editcap-option to select one of * the other behaviors for dealing with out-of-sequence * packets. */ continue; } cmp = nstime_cmp(&delta, &relative_time_window); if(cmp > 0) { /* * The delta time indicates that we are now looking at * cached packets beyond the specified dup time window. * Check no more! */ break; } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len && memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) { return TRUE; } } return FALSE; } static void usage(void) { fprintf(stderr, "Editcap %s" #ifdef SVNVERSION " (" SVNVERSION ")" #endif "\n", VERSION); fprintf(stderr, "Edit and/or translate the format of capture files.\n"); fprintf(stderr, "See http://www.wireshark.org for more information.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Usage: editcap [options] ... [ [-] ... ]\n"); fprintf(stderr, "\n"); fprintf(stderr, "A single packet or a range of packets can be selected.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Packet selection:\n"); fprintf(stderr, " -r keep the selected packets, default is to delete them\n"); fprintf(stderr, " -A don't output packets whose timestamp is before the\n"); fprintf(stderr, " given time (format as YYYY-MM-DD hh:mm:ss)\n"); fprintf(stderr, " -B don't output packets whose timestamp is after the\n"); fprintf(stderr, " given time (format as YYYY-MM-DD hh:mm:ss)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Duplicate packet removal:\n"); fprintf(stderr, " -d remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH); fprintf(stderr, " -D remove packet if duplicate, configurable .\n"); fprintf(stderr, " Valid values are 0 to %d.\n", MAX_DUP_DEPTH); fprintf(stderr, " NOTE: A of 0 with -v (verbose option) is\n"); fprintf(stderr, " useful to print MD5 hashes.\n"); fprintf(stderr, " -w remove packet if duplicate packet is found EQUAL TO OR\n"); fprintf(stderr, " LESS THAN prior to current packet.\n"); fprintf(stderr, " A is specified in relative seconds\n"); fprintf(stderr, " (e.g. 0.000001)\n"); fprintf(stderr, "\n"); fprintf(stderr, " NOTE: The use of the 'Duplicate packet removal' options with\n"); fprintf(stderr, " other editcap options except -v may not always work as expected.\n"); fprintf(stderr, " Specifically the -r and -t options will very likely NOT have the\n"); fprintf(stderr, " desired effect if combined with the -d, -D or -w.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Packet manipulation:\n"); fprintf(stderr, " -s truncate each packet to max. bytes of data\n"); fprintf(stderr, " -C chop each packet at the end by bytes\n"); fprintf(stderr, " -t