From e5d4b4a86e4e10c1b4db75b7d72b30f0dca77bfc Mon Sep 17 00:00:00 2001 From: jc44 Date: Mon, 26 Oct 2009 11:56:18 +0000 Subject: [PATCH] New analyse mode for pcapreport in an effort to produce useful summary info. Part 1 of an ongoing effort to inprove pcapreport --HG-- extra : convert_revision : svn%3Aeff31bef-be4a-0410-a8fe-e47997df2690/trunk%40148 --- pcapreport.c | 824 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 605 insertions(+), 219 deletions(-) diff --git a/pcapreport.c b/pcapreport.c index eb32fdc..f863806 100644 --- a/pcapreport.c +++ b/pcapreport.c @@ -30,10 +30,12 @@ #include #include +#include #include #include #include #include +#include #ifdef _WIN32 #include #else // _WIN32 @@ -51,6 +53,75 @@ #include "misc_fns.h" #include "ts_fns.h" +typedef struct pcapreport_stream_struct pcapreport_stream_t; + +#define JITTER_BUF_SIZE 1024 + +typedef struct jitter_el_struct { + uint32_t t; + int delta; +} jitter_el_t; + +typedef struct jitter_env_struct { + int min_val; + int max_val; + int in_n; + int out_n; + int len; + jitter_el_t buf[JITTER_BUF_SIZE]; +} jitter_env_t; + +typedef struct pcapreport_section_struct pcapreport_section_t; +struct pcapreport_section_struct { + pcapreport_section_t * next; + unsigned int section_no; + unsigned int jitter_max; + uint32_t pkt_start; + uint32_t pkt_last; + uint64_t time_start; // 90kHz + uint64_t time_last; + uint64_t pcr_start; // 90kHz + uint64_t pcr_last; +}; + +struct pcapreport_stream_struct { + pcapreport_stream_t * hash_next; + + char *output_name; + FILE *output_file; + uint32_t output_dest_addr; + uint32_t output_dest_port; + + int stream_no; + int force; // We have an explicit filter - try harder + int ts_good; // Not a boolean -ve is bad, +ve is good + int seen_good; + int seen_bad; + int multiple_pcr_pids; + + TS_reader_p ts_r; + + uint32_t pcr_pid; + + // The temporary read buffer used by our ts reader. + byte *tmp_buf; + uint32_t tmp_len; + + // ts packet counter for error reporting. + uint32_t ts_counter; + + /*! How far do we need to skew (in 90kHz units) to signal a discontinuity? */ + int64_t skew_discontinuity_threshold; + + int64_t last_time_offset; + + pcapreport_section_t * section_first; + pcapreport_section_t * section_last; + + jitter_env_t jitter; +}; + + typedef struct pcapreport_ctx_struct { int use_stdin; @@ -60,65 +131,148 @@ typedef struct pcapreport_ctx_struct int dump_extra; int time_report; int verbose; + int analyse; + int stream_count; PCAP_reader_p pcreader; pcap_hdr_t pcap_hdr; - char *output_name; - FILE *output_file; - uint32_t output_dest_addr; - uint32_t output_dest_port; - - TS_reader_p ts_r; - - // The temporary read buffer used by our ts reader. - byte *tmp_buf; - uint32_t tmp_len; - - // ts packet counter for error reporting. - uint32_t ts_counter; // packet counter. uint32_t pkt_counter; - // Last continuity counter. - int last_cc; + uint32_t filter_dest_addr; + uint32_t filter_dest_port; - // Do we think we currently have cc? - int have_cc; + const char * output_name_base; - /*! In 90kHz units, what do you reckon you need to add to - * PCR to make it into packet time? - */ - int64_t pcr_time_offset; + int64_t opt_skew_discontinuity_threshold; - /*! What was the last difference between pcr_time_offset and - * the measured time offset? - */ - int64_t last_time_offset; - - /*! Do we think pcr_time_offset is valid at the minute? */ - int pcr_time_offset_valid; - - /*! How far do we need to skew (in 90kHz units) to signal a discontinuity? */ - int64_t skew_discontinuity_threshold; - - /*! Time of last discontinuity, in us */ - int64_t last_d_us; + uint64_t time_start; // 90kHz + uint32_t time_usec; + time_t time_sec; + pcapreport_stream_t * stream_hash[256]; } pcapreport_ctx_t; + +static unsigned int +jitter_value(const jitter_env_t * const je) +{ + return je->max_val - je->min_val; +} + +static unsigned int +jitter_add(jitter_env_t * const je, const int delta, const uint32_t time, const uint32_t range) +{ + jitter_el_t * const eob = je->buf + JITTER_BUF_SIZE; + jitter_el_t * const in_el = je->buf + je->in_n; + jitter_el_t * out_el = je->buf + je->out_n; + jitter_el_t * const next_el = (je->in_n == JITTER_BUF_SIZE - 1) ? je->buf : in_el + 1; + int needs_scan = FALSE; + + + // 1st expire anything we no longer want - in any case expire one if + // we are about to overflow. + while (in_el != out_el && (time - in_el->t < range || out_el == next_el)) + { + if (in_el->delta == je->min_val || in_el->delta == je->max_val) + needs_scan = TRUE; + + // Inc with wrap + if (++out_el >= eob) + out_el = je->buf; + } + + if (needs_scan || in_el == out_el) + { + // Only recalc max & min if we have expired a previous one + const jitter_el_t * el = out_el; + int min_val = delta; + int max_val = delta; + + while (el != in_el) + { + if (el->delta > max_val) + max_val = el->delta; + if (el->delta < min_val) + min_val = el->delta; + + if (++el >= eob) + el = je->buf; + } + + je->max_val = max_val; + je->min_val = min_val; + } + else + { + // If we haven't expired a previous min or max just check to see if this + // is a new one + if (delta > je->max_val) + je->max_val = delta; + if (delta < je->min_val) + je->min_val = delta; + } + + // Now add to the end + in_el->t = time; + in_el->delta = delta; + + // and update the environment + je->in_n = next_el - je->buf; + je->out_n = out_el - je->buf; + + return jitter_value(je); +} + +static void +jitter_clear(jitter_env_t * const je) +{ + je->in_n = 0; + je->out_n = 0; + je->max_val = 0; + je->min_val = 0; +} + +static pcapreport_section_t * +section_create(pcapreport_stream_t * const st) +{ + pcapreport_section_t * const tsect = calloc(1, sizeof(*tsect)); + pcapreport_section_t * const last = st->section_last; + + if (tsect == NULL) + return NULL; + + // Bind into stream + + if (last == NULL) + { + // Empty chain - add as first el + st->section_first = tsect; + } + else + { + // Add to end + tsect->section_no = last->section_no + 1; + last->next = tsect; + } + st->section_last = tsect; + + return tsect; +} + // Discontinuity threshold is 6s. #define SKEW_DISCONTINUITY_THRESHOLD (6*90000) static int digest_times_read(void *handle, byte *out_buf, size_t len) { - pcapreport_ctx_t *ctx = (pcapreport_ctx_t *)handle; - int nr_bytes = (len < ctx->tmp_len ? len : ctx->tmp_len); - int new_tmp_len = ctx->tmp_len - nr_bytes; + pcapreport_stream_t * const st = handle; + int nr_bytes = (len < st->tmp_len ? len : st->tmp_len); + int new_tmp_len = st->tmp_len - nr_bytes; - memcpy(out_buf, ctx->tmp_buf, nr_bytes); - memmove(ctx->tmp_buf, &ctx->tmp_buf[nr_bytes], + memcpy(out_buf, st->tmp_buf, nr_bytes); + memmove(st->tmp_buf, &st->tmp_buf[nr_bytes], new_tmp_len); - ctx->tmp_len = new_tmp_len; + st->tmp_len = new_tmp_len; // fprint_msg(">> read %d bytes from intermediate buffer. \n", nr_bytes); return nr_bytes; @@ -131,22 +285,31 @@ static int digest_times_seek(void *handle, offset_t val) return 1; } +static uint64_t +pkt_time(const pcaprec_hdr_t * const pcap_pkt_hdr) +{ + return (((int64_t)pcap_pkt_hdr->ts_usec*9)/100) + + ((int64_t)pcap_pkt_hdr->ts_sec * 90000); +} + + static int digest_times(pcapreport_ctx_t *ctx, - pcaprec_hdr_t *pcap_pkt_hdr, - ethernet_packet_t *epkt, - ipv4_header_t *ipv4_header, - ipv4_udp_header_t *udp_header, + pcapreport_stream_t * const st, + const pcaprec_hdr_t *pcap_pkt_hdr, + const ethernet_packet_t *epkt, + const ipv4_header_t *ipv4_header, + const ipv4_udp_header_t *udp_header, const byte *data, const uint32_t len) { int rv; - if (!ctx->ts_r) + if (st->ts_r == NULL) { - rv = build_TS_reader_with_fns(ctx, + rv = build_TS_reader_with_fns(st, digest_times_read, digest_times_seek, - &ctx->ts_r); + &st->ts_r); if (rv) { print_err( "### pcapreport: Cannot create ts reader.\n"); @@ -156,9 +319,9 @@ static int digest_times(pcapreport_ctx_t *ctx, // Add all our data to the pool. - ctx->tmp_buf = (byte *)realloc(ctx->tmp_buf, ctx->tmp_len + len); - memcpy(&ctx->tmp_buf[ctx->tmp_len], data, len); - ctx->tmp_len += len; + st->tmp_buf = (byte *)realloc(st->tmp_buf, st->tmp_len + len); + memcpy(&st->tmp_buf[st->tmp_len], data, len); + st->tmp_len += len; // Now read out all the ts packets we can. while (1) @@ -166,7 +329,7 @@ static int digest_times(pcapreport_ctx_t *ctx, byte *pkt; int rv; - rv = read_next_TS_packet(ctx->ts_r, &pkt); + rv = read_next_TS_packet(st->ts_r, &pkt); if (rv == EOF) { // Got to EOF - return for more data @@ -187,8 +350,9 @@ static int digest_times(pcapreport_ctx_t *ctx, &payload, &payload_len); if (rv) { - fprint_msg(">> WARNING: TS packet %d [ packet %d @ %d.%d s ] cannot be split.\n", - ctx->ts_counter, ctx->pkt_counter, + fprint_msg(">%d> WARNING: TS packet %d [ packet %d @ %d.%d s ] cannot be split.\n", + st->stream_no, + st->ts_counter, ctx->pkt_counter, pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec); } else @@ -200,122 +364,137 @@ static int digest_times(pcapreport_ctx_t *ctx, { int has_pcr; uint64_t pcr; - uint64_t t_pcr; + const uint64_t t_pcr = pkt_time(pcap_pkt_hdr); int64_t pcr_time_offset; get_PCR_from_adaptation_field(adapt, adapt_len, &has_pcr, &pcr); + if (has_pcr) { - fprint_msg(">> Found PCR %lld at %d.%d s \n", - pcr, pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec); + int64_t skew; - // PCR pops out in 27MHz units. Let's do all our comparisons - // in 90kHz. - pcr = pcr / 300; - t_pcr = (((int64_t)pcap_pkt_hdr->ts_usec*9)/100) + - ((int64_t)pcap_pkt_hdr->ts_sec * 90000); - - // fprint_msg("pcr = %lld t_pcr = %lld diff = %lld\n", - // pcr, t_pcr, t_pcr - pcr); - - pcr_time_offset = ((int64_t)t_pcr - (int64_t)pcr); - if (ctx->pcr_time_offset_valid) + if (ctx->time_report) { - int64_t skew = (pcr_time_offset - ctx->pcr_time_offset); + fprint_msg(">%d> Found PCR %lld at %d.%d s \n", st->stream_no, + pcr, pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec); + } - if (skew > ctx->skew_discontinuity_threshold || - skew < -ctx->skew_discontinuity_threshold) + if (st->pcr_pid == 0) + st->pcr_pid = pid; + + if (pid != st->pcr_pid) + { + // *** If this happens often then fix to track each Pid + if (!st->multiple_pcr_pids) { - fprint_msg(">> Skew discontinuity! Skew = %lld (> %lld) at" - " ts = %d network = %d (PCR %lld Time %d.%d)\n", - skew, ctx->skew_discontinuity_threshold, - ctx->ts_counter, ctx->pkt_counter, - pcr, pcap_pkt_hdr->ts_sec, - pcap_pkt_hdr->ts_usec); - - ctx->pcr_time_offset = pcr_time_offset; + fprint_msg("!%d! Multiple pids detected: pids: %d,%d,...\n", + st->stream_no, st->pcr_pid, pid); } - else - { - int64_t rel_tim; - double skew_rate; - - rel_tim = ((int64_t)pcap_pkt_hdr->ts_usec) + - ((int64_t)pcap_pkt_hdr->ts_sec * 1000000); - - rel_tim -= ctx->last_d_us; - - skew_rate = (double)skew / ((double)((double)rel_tim / (60*1000000))); - - fprint_msg(">> [ts %d net %d ] PCR %lld Time %d.%d [rel %d.%d] - skew = %lld (delta = %lld, rate = %.4g PTS/min)\n", - ctx->ts_counter, ctx->pkt_counter, - pcr, - pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec, - (int)(rel_tim / (int64_t)1000000), - (int)rel_tim%1000000, - skew, pcr_time_offset - ctx->last_time_offset, - skew_rate); - } - ctx->last_time_offset = pcr_time_offset; + st->multiple_pcr_pids = TRUE; } else { - ctx->pcr_time_offset = ctx->last_time_offset = - pcr_time_offset; - ctx->last_d_us = - ((int64_t)pcap_pkt_hdr->ts_usec) + - (((int64_t)pcap_pkt_hdr->ts_sec) * 1000000); + // PCR pops out in 27MHz units. Let's do all our comparisons + // in 90kHz. + pcr /= 300; - ctx->pcr_time_offset_valid = 1; + // fprint_msg("pcr = %lld t_pcr = %lld diff = %lld\n", + // pcr, t_pcr, t_pcr - pcr); + + pcr_time_offset = ((int64_t)t_pcr - (int64_t)pcr); + + skew = st->section_last == NULL ? 0LL : + pcr_time_offset - (st->section_last->time_start - st->section_last->pcr_start); + + if (st->section_last == NULL || + skew > st->skew_discontinuity_threshold || + skew < -st->skew_discontinuity_threshold) + { + pcapreport_section_t * const tsect = section_create(st); + + if (tsect->section_no != 0) + { + fprint_msg(">%d> Skew discontinuity! Skew = %lld (> %lld) at" + " ts = %d network = %d (PCR %lld Time %d.%d)\n", + st->stream_no, + skew, st->skew_discontinuity_threshold, + st->ts_counter, ctx->pkt_counter, + pcr, pcap_pkt_hdr->ts_sec, + pcap_pkt_hdr->ts_usec); + } + + tsect->pkt_last = + tsect->pkt_start = ctx->pkt_counter; + tsect->pcr_last = + tsect->pcr_start = pcr; + tsect->time_last = + tsect->time_start = t_pcr; + + jitter_clear(&st->jitter); + st->last_time_offset = 0; + } + else + { + pcapreport_section_t * const tsect = st->section_last; + + // Extract jitter over up to the last 10s. skew will be within + // an int by now + unsigned int cur_jitter = jitter_add(&st->jitter, (int)skew, + (uint32_t)(t_pcr & 0xffffffffU), 90000 * 10); + + if (tsect->jitter_max < cur_jitter) + tsect->jitter_max = cur_jitter; + + if (ctx->time_report) + { + int64_t rel_tim = t_pcr - tsect->time_start; // 90kHz + double skew_rate = (double)skew / ((double)((double)rel_tim / (60*90000))); + + fprint_msg(">%d> [ts %d net %d ] PCR %lld Time %d.%d [rel %d.%d] - skew = %lld (delta = %lld, rate = %.4g PTS/min) - jitter=%u\n", + st->stream_no, + st->ts_counter, ctx->pkt_counter, + pcr, + pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec, + (int)(rel_tim / (int64_t)1000000), + (int)rel_tim%1000000, + skew, pcr_time_offset - st->last_time_offset, + skew_rate, cur_jitter); + } + + // Remember where we are for posterity + tsect->pcr_last = pcr; + tsect->time_last = t_pcr; + tsect->pkt_last = ctx->pkt_counter; + + st->last_time_offset = pcr_time_offset; + } } } } - - -#if 0 - // CC? - cc = pkt[3]&0x0f; - //fprint_msg("cc = %d \n", cc); - if (!ctx->have_cc) - { - ctx->have_cc = 1; - } - else - { - if (cc != ((ctx->last_cc + 1)&0xf)) - { - fprint_msg(">> CC discontinuity! ts = %d network = %d expected %d got %d.\n", - ctx->ts_counter, ctx->pkt_counter, - (ctx->last_cc+1)&0xf, - cc); - } - } - ctx->last_cc = cc; -#endif } - - ++ctx->ts_counter; + ++st->ts_counter; } } } -static int write_out_packet(pcapreport_ctx_t *ctx, +static int write_out_packet(pcapreport_ctx_t * const ctx, + pcapreport_stream_t * const st, const byte *data, const uint32_t len) { int rv; - if (ctx->output_name) + if (st->output_name) { - if (!ctx->output_file) + if (!st->output_file) { - ctx->output_file = fopen(ctx->output_name, "wb"); - if (!ctx->output_file) + st->output_file = fopen(st->output_name, "wb"); + if (!st->output_file) { fprint_err("### pcapreport: Cannot open %s .\n", - ctx->output_name); + st->output_name); return 1; } } @@ -324,19 +503,182 @@ static int write_out_packet(pcapreport_ctx_t *ctx, { fprint_msg("++ Dumping %d bytes to output file.\n", len); } - rv = fwrite(data, len, 1, ctx->output_file); + rv = fwrite(data, len, 1, st->output_file); if (rv != 1) { fprint_err( "### pcapreport: Couldn't write %d bytes" " to %s (error = %d).\n", - len, ctx->output_name, - ferror(ctx->output_file)); + len, st->output_name, + ferror(st->output_file)); return 1; } } return 0; } +int +stream_ts_check(pcapreport_ctx_t * const ctx, pcapreport_stream_t * const st, + const byte *data, + const uint32_t len) +{ + const byte * ptr; + + if (st->force) + st->ts_good = 10; + + if (len % 188 != 0) + --st->ts_good; + else + ++st->ts_good; + + for (ptr = data; ptr < data + len; ptr += 188) + { + if (*ptr != 0x47) + --st->ts_good; + else + ++st->ts_good; + } + + if (st->ts_good > 10) + st->ts_good = 10; + if (st->ts_good < -10) + st->ts_good = -10; + + if (st->ts_good <= 0) + { + ++st->seen_bad; + return FALSE; + } + + ++st->seen_good; + return TRUE; +} + +pcapreport_stream_t * +stream_create(pcapreport_ctx_t * const ctx, uint32_t const dest_addr, const uint32_t dest_port) +{ + pcapreport_stream_t * const st = calloc(1, sizeof(*st)); + st->stream_no = ctx->stream_count++; + st->output_dest_addr = dest_addr; + st->output_dest_port = dest_port; + + st->skew_discontinuity_threshold = ctx->opt_skew_discontinuity_threshold; + + // If the dest:port is fully specified then avoid guesswork + st->force = ctx->filter_dest_addr != 0 && ctx->filter_dest_port != 0; + + if (ctx->output_name_base != NULL) + { + size_t len = strlen(ctx->output_name_base); + st->output_name = malloc(len + 32); + memcpy(st->output_name, ctx->output_name_base, len + 1); + + // If we have been given a unique filter then assume they actually want + // that name! + if (ctx->filter_dest_addr == 0 || ctx->filter_dest_port == 0) + { + sprintf(st->output_name + len, "_%u.%u.%u.%u_%u.ts", + dest_addr >> 24, (dest_addr >> 16) & 0xff, + (dest_addr >> 8) & 0xff, dest_addr & 0xff, + dest_port); + } + } + + if (st->output_name != NULL) + { + fprint_msg("pcapreport: Dumping all packets for %s:%d to %s\n", + ipv4_addr_to_string(st->output_dest_addr), + st->output_dest_port, + st->output_name); + } + + return st; +} + +void +stream_analysis(pcapreport_ctx_t * const ctx, pcapreport_stream_t * const st) +{ + uint32_t dest_addr = st->output_dest_addr; + fprint_msg("Stream %d: Dest %u.%u.%u.%u:%u\n", + st->stream_no, + dest_addr >> 24, (dest_addr >> 16) & 0xff, + (dest_addr >> 8) & 0xff, dest_addr & 0xff, + st->output_dest_port); + if (st->seen_good == 0) + { + // Cut the rest of the stats short if they are meaningless + fprint_msg(" No TS detected: Pkts=%u\n", st->seen_bad); + } + else + { + const pcapreport_section_t * tsect; + + fprint_msg(" Pkts: Good=%d, Bad=%d\n", st->seen_good, st->seen_bad); + fprint_msg(" PCR PID: %d (%#x)%s\n", st->pcr_pid, st->pcr_pid, + !st->multiple_pcr_pids ? "" : " ### Other PCR PIDs in stream - not tracked"); + + for (tsect = st->section_first; tsect != NULL; tsect = tsect->next) + { + uint64_t time_offset = ctx->time_start; + int64_t time_len = tsect->time_last - tsect->time_start; + int64_t pcr_len = tsect->pcr_last - tsect->pcr_start; + int64_t drift = time_len - pcr_len; + fprint_msg(" Section %d:", tsect->section_no); + fprint_msg(" Pkt: %u->%u\n", tsect->pkt_start, tsect->pkt_last); + fprint_msg(" Tim: %llu->%llu (%lld)\n", tsect->time_start - time_offset, tsect->time_last - time_offset, time_len); + fprint_msg(" PCR: %llu->%llu (%lld)\n", tsect->pcr_start, tsect->pcr_last, pcr_len); + fprint_msg(" Drift: diff=%lld; rate=%lld/min; 1s per %llds\n", + time_len - pcr_len, drift * 60LL * 90000LL / time_len, + drift == 0 ? 0LL : time_len / drift); + fprint_msg(" Max jitter: %d\n", tsect->jitter_max); + } + } + + fprint_msg("\n"); +} + + +unsigned int +stream_hash(uint32_t const dest_addr, const uint32_t dest_port) +{ + uint32_t x = dest_addr ^ dest_port; + x ^= x >> 16; + return (x ^ (x >> 8)) & 0xff; +} + +pcapreport_stream_t * +stream_find(pcapreport_ctx_t * const ctx, uint32_t const dest_addr, const uint32_t dest_port) +{ + const unsigned int h = stream_hash(dest_addr, dest_port); + pcapreport_stream_t ** pst = ctx->stream_hash + h; + pcapreport_stream_t * st; + + while ((st = *pst) != NULL) + { + if (st->output_dest_addr == dest_addr && st->output_dest_port == dest_port) + return st; + pst = &st->hash_next; + } + + if ((st = stream_create(ctx, dest_addr, dest_port)) == NULL) + return NULL; + + *pst = st; + return st; +} + +void +stream_close(pcapreport_ctx_t * const ctx, pcapreport_stream_t ** pst) +{ + pcapreport_stream_t * const st = *pst; + *pst = NULL; + + if (st->output_file != NULL) + fclose(st->output_file); + if (st->output_name != NULL) + free(st->output_name); + free(st); +} static void print_usage() { @@ -347,29 +689,35 @@ static void print_usage() REPORT_VERSION("pcapreport"); print_msg( "\n" - " Report on a pcap capture file.\n" + "Report on a pcap capture file.\n" "\n" - " -output , -o Dump selected UDP payloads to the named output file.\n" - " -d \n" - " -d : Select data with the given destination IP and port.\n" - " If the is not specified, it defaults to 0\n" - " (see below).\n" - " -dump-data, -D Dump any data in the input file to stdout.\n" - " -extra-dump, -e Dump only data which isn't being sent to the -o file.\n" - " -times, -t Report on PCR vs PCAP timing for the destination specified in -d.\n" - " -verbose, -v Output metadata about every packet.\n" + " -output\n" + " -o , Dump selected UDP payloads to output file(s)\n" + " Uses given filename if : specified,\n" + " otherwise appends _ to filename per TS\n" + " -a Analyse. Produces summary info on every TS in the pcap\n" + " -d :\n" + " -d Select data with the given destination IP and port.\n" + " If the is not specified, it defaults to 0\n" + " (see below).\n" + " -dump-data, -D Dump any data in the input file to stdout.\n" + " -extra-dump, -e Dump only data which isn't being sent to the -o file.\n" + " -times, -t Report continuously on PCR vs PCAP timing for the\n" + " destination specified in -d.\n" + " -verbose, -v Output metadata about every packet.\n" " -skew-discontinuity-threshold \n" - " -skew Gives the skew discontinuity threshold in 90kHz units.\n" + " -skew Gives the skew discontinuity threshold in 90kHz units.\n" "\n" - " -err stdout Write error messages to standard output (the default)\n" - " -err stderr Write error messages to standard error (Unix traditional)\n" + " -err stdout Write error messages to standard output (the default)\n" + " -err stderr Write error messages to standard error (Unix traditional)\n" "\n" - " Specifying 0.0.0.0 for destination IP will capture all hosts, specifying 0 destination\n" - " port will capture all ports on the destination host.\n" + "Specifying 0.0.0.0 for destination IP will capture all hosts, specifying 0\n" + "as a destination port will capture all ports on the destination host.\n" "\n" - " Network packet and TS packet numbers start at 0.\n" + "Network packet numbers start at 1 (like wireshark)\n" + "TS packet numbers start at 0.\n" "\n" - " Positive skew means that we received too low a PCR for this timestamp.\n" + "Positive skew means that we received too low a PCR for this timestamp.\n" "\n" ); } @@ -379,9 +727,10 @@ int main(int argc, char **argv) { int err = 0; int ii = 1; - pcapreport_ctx_t ctx = {0}; + pcapreport_ctx_t sctx = {0}; + pcapreport_ctx_t * const ctx = &sctx; - ctx.skew_discontinuity_threshold = SKEW_DISCONTINUITY_THRESHOLD; + ctx->opt_skew_discontinuity_threshold = SKEW_DISCONTINUITY_THRESHOLD; if (argc < 2) { @@ -419,17 +768,22 @@ int main(int argc, char **argv) !strcmp("-output", argv[ii]) || !strcmp("-o", argv[ii])) { CHECKARG("pcapreport",ii); - ctx.output_name = argv[++ii]; + ctx->output_name_base = argv[++ii]; } else if (!strcmp("--times", argv[ii]) || !strcmp("-times", argv[ii]) || !strcmp("-t", argv[ii])) { - ++ctx.time_report; + ++ctx->time_report; + } + else if (!strcmp("--analyse", argv[ii]) || + !strcmp("-a", argv[ii])) + { + ctx->analyse = TRUE; } else if (!strcmp("--verbose", argv[ii]) || !strcmp("-verbose", argv[ii]) || !strcmp("-v", argv[ii])) { - ++ctx.verbose; + ++ctx->verbose; } else if (!strcmp("--d", argv[ii]) || !strcmp("-d", argv[ii])) @@ -442,8 +796,8 @@ int main(int argc, char **argv) if (err) return 1; ++ii; - ctx.output_dest_port = port; - if (ipv4_string_to_addr(&ctx.output_dest_addr, hostname)) + ctx->filter_dest_port = port; + if (ipv4_string_to_addr(&ctx->filter_dest_addr, hostname)) { fprint_err( "### pcapreport: '%s' is not a host IP address (names are not allowed!)\n", hostname); @@ -453,12 +807,12 @@ int main(int argc, char **argv) else if (!strcmp("--dump-data", argv[ii]) || !strcmp("-dump-data", argv[ii]) || !strcmp("-D", argv[ii])) { - ++ctx.dump_data; + ++ctx->dump_data; } else if (!strcmp("--extra-dump", argv[ii]) || !strcmp("-extra-dump", argv[ii]) || !strcmp("-E", argv[ii])) { - ++ctx.dump_extra; + ++ctx->dump_extra; } else if (!strcmp("--skew-discontinuity-threshold", argv[ii]) || !strcmp("-skew-discontinuity-threshold", argv[ii]) || @@ -468,7 +822,7 @@ int main(int argc, char **argv) CHECKARG("pcapreport",ii); err = int_value("pcapreport", argv[ii], argv[ii+1], TRUE, 0, &val); if (err) return 1; - ctx.skew_discontinuity_threshold = val; + ctx->opt_skew_discontinuity_threshold = val; ++ii; } else @@ -480,34 +834,34 @@ int main(int argc, char **argv) } else { - if (ctx.had_input_name) + if (ctx->had_input_name) { fprint_err( "### pcapreport: Unexpected '%s'\n", argv[ii]); return 1; } else { - ctx.input_name = argv[ii]; - ctx.had_input_name = TRUE; + ctx->input_name = argv[ii]; + ctx->had_input_name = TRUE; } } ++ii; } - if (!ctx.had_input_name) + if (!ctx->had_input_name) { print_err("### pcapreport: No input file specified\n"); return 1; } - fprint_msg("%s\n",ctx.input_name); + fprint_msg("%s\n",ctx->input_name); - err = pcap_open(&ctx.pcreader, &ctx.pcap_hdr, ctx.input_name); + err = pcap_open(&ctx->pcreader, &ctx->pcap_hdr, ctx->input_name); if (err) { fprint_err("### pcapreport: Unable to open input file %s for reading " "PCAP (code %d)\n", - ctx.had_input_name?ctx.input_name:"", err); + ctx->had_input_name?ctx->input_name:"", err); // Just an error code isn't much use - let's look at the source // and report something more helpful... fprint_err(" %s\n", @@ -518,27 +872,19 @@ int main(int argc, char **argv) return 1; } - if (ctx.output_name) - { - fprint_msg("pcapreport: Dumping all packets for %s:%d to %s\n", - ipv4_addr_to_string(ctx.output_dest_addr), - ctx.output_dest_port, - ctx.output_name); - } - fprint_msg("Capture made by version %u.%u local_tz_correction " "%d sigfigs %u snaplen %d network %u\n", - ctx.pcap_hdr.version_major, ctx.pcap_hdr.version_minor, - ctx.pcap_hdr.thiszone, - ctx.pcap_hdr.sigfigs, - ctx.pcap_hdr.snaplen, - ctx.pcap_hdr.network); + ctx->pcap_hdr.version_major, ctx->pcap_hdr.version_minor, + ctx->pcap_hdr.thiszone, + ctx->pcap_hdr.sigfigs, + ctx->pcap_hdr.snaplen, + ctx->pcap_hdr.network); - if (ctx.pcap_hdr.snaplen < 65535) + if (ctx->pcap_hdr.snaplen < 65535) { fprint_err("### pcapreport: WARNING snaplen is %d, not >= 65535 - " "not all data may have been captured.\n", - ctx.pcap_hdr.snaplen); + ctx->pcap_hdr.snaplen); } @@ -552,7 +898,7 @@ int main(int argc, char **argv) uint32_t len = 0; int sent_to_output = 0; - err = pcap_read_next(ctx.pcreader, &rec_hdr, &data, &len); + err = pcap_read_next(ctx->pcreader, &rec_hdr, &data, &len); switch (err) { case 0: // EOF. @@ -562,14 +908,23 @@ int main(int argc, char **argv) { byte *allocated = data; - if (ctx.verbose) + // Wireshark numbers packets from 1 so we shall do the same + if (ctx->pkt_counter++ == 0) + { + // Note time of 1st packet + ctx->time_usec = rec_hdr.ts_usec; + ctx->time_sec = rec_hdr.ts_sec; + ctx->time_start = pkt_time(&rec_hdr); + } + + if (ctx->verbose) { fprint_msg("pkt: Time = %d.%d orig_len = %d \n", rec_hdr.ts_sec, rec_hdr.ts_usec, rec_hdr.orig_len); } - if (!(ctx.pcap_hdr.network == PCAP_NETWORK_TYPE_ETHERNET)) + if (!(ctx->pcap_hdr.network == PCAP_NETWORK_TYPE_ETHERNET)) { goto dump_out; } @@ -593,7 +948,7 @@ int main(int argc, char **argv) } - if (ctx.verbose) + if (ctx->verbose) { fprint_msg("++ 802.11: src %02x:%02x:%02x:%02x:%02x:%02x " " dst %02x:%02x:%02x:%02x:%02x:%02x " @@ -626,7 +981,7 @@ int main(int argc, char **argv) goto dump_out; } - if (ctx.verbose) + if (ctx->verbose) { fprint_msg("++ IPv4: src = %s", ipv4_addr_to_string(ipv4_hdr.src_addr)); @@ -668,7 +1023,7 @@ int main(int argc, char **argv) goto dump_out; } - if (ctx.verbose) + if (ctx->verbose) { fprint_msg("++ udp: src port = %d " "dest port = %d len = %d \n", @@ -681,34 +1036,38 @@ int main(int argc, char **argv) len = out_len; if ( - (!ctx.output_dest_addr || (ipv4_hdr.dest_addr == ctx.output_dest_addr)) && - (!ctx.output_dest_port || (udp_hdr.dest_port == ctx.output_dest_port))) + (ctx->filter_dest_addr == 0 || (ipv4_hdr.dest_addr == ctx->filter_dest_addr)) && + (ctx->filter_dest_port == 0 || (udp_hdr.dest_port == ctx->filter_dest_port))) { - ++sent_to_output; + pcapreport_stream_t * const st = stream_find(ctx, ipv4_hdr.dest_addr, udp_hdr.dest_port); - if (ctx.time_report) + if (stream_ts_check(ctx, st, data, len)) { - rv =digest_times(&ctx, - &rec_hdr, - &epkt, - &ipv4_hdr, - &udp_hdr, - data, len); - if (rv) { return rv; } + ++sent_to_output; + + if (ctx->time_report || ctx->analyse) + { + rv =digest_times(ctx, + st, + &rec_hdr, + &epkt, + &ipv4_hdr, + &udp_hdr, + data, len); + if (rv) { return rv; } + } + if (st->output_name) + { + rv = write_out_packet(ctx, st, data, len); + if (rv) { return rv; } + } } - if (ctx.output_name) - { - rv = write_out_packet(&ctx, data, len); - if (rv) { return rv; } - } - - } } // Adjust dump_out: - if (ctx.dump_data || (ctx.dump_extra && !sent_to_output)) + if (ctx->dump_data || (ctx->dump_extra && !sent_to_output)) { print_data(TRUE, "data", data, len, len); } @@ -718,19 +1077,46 @@ dump_out: default: // Some other error. fprint_err( "### pcapreport: Can't read packet %d - code %d\n", - ctx.pkt_counter, err); + ctx->pkt_counter, err); ++done; break; } - ++ctx.pkt_counter; - } } - if (ctx.output_file) + if (ctx->analyse) { - fprint_msg("Closing output file.\n"); - fclose(ctx.output_file); + // Spit out pcap part of the report + // + const struct tm * const t = gmtime(&ctx->time_sec); + + fprint_msg("Pcap start time: %llu (%d-%02d-%02d %d:%02d:%02d.%06d)\n", ctx->time_start, + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec, ctx->time_usec); + fprint_msg("Pcap pkts: %u\n", ctx->pkt_counter); + fprint_msg("\n"); + } + + { + unsigned int i; + { + for (i = 0; i != 256; ++i) + { + // Kill all on this hash chain - singly linked so slightly dull + while (ctx->stream_hash[i] != NULL) + { + pcapreport_stream_t ** pst = ctx->stream_hash + i; + // Spin to last el in hash + while ((*pst)->hash_next != NULL) + pst = &(*pst)->hash_next; + // Spit out any remaining info + if (ctx->analyse) + stream_analysis(ctx, *pst); + // Kill it + stream_close(ctx, pst); + } + } + } } return 0;