2008-09-05 16:09:58 +00:00
|
|
|
|
/*
|
|
|
|
|
* Report on a pcap (.pcap) file.
|
|
|
|
|
*
|
|
|
|
|
* <rrw@kynesim.co.uk> 2008-09-05
|
|
|
|
|
*
|
|
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
|
* Version: MPL 1.1
|
|
|
|
|
*
|
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
|
*
|
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
|
* License.
|
|
|
|
|
*
|
|
|
|
|
* The Original Code is the MPEG TS, PS and ES tools.
|
|
|
|
|
*
|
|
|
|
|
* The Initial Developer of the Original Code is Amino Communications Ltd.
|
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2008
|
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s):
|
2008-09-05 16:11:56 +00:00
|
|
|
|
* Richard Watts, Kynesim <rrw@kynesim.co.uk>
|
2008-09-05 16:09:58 +00:00
|
|
|
|
*
|
|
|
|
|
* ***** END LICENSE BLOCK *****
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
2009-10-26 11:56:18 +00:00
|
|
|
|
#include <limits.h>
|
2008-09-05 16:09:58 +00:00
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <limits.h>
|
2009-10-26 11:56:18 +00:00
|
|
|
|
#include <time.h>
|
2008-09-05 16:09:58 +00:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#else // _WIN32
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
#endif // _WIN32
|
|
|
|
|
|
|
|
|
|
#include "compat.h"
|
|
|
|
|
#include "pcap.h"
|
|
|
|
|
#include "ethernet.h"
|
|
|
|
|
#include "ipv4.h"
|
|
|
|
|
#include "version.h"
|
|
|
|
|
#include "misc_fns.h"
|
2008-09-05 18:06:52 +00:00
|
|
|
|
#include "ts_fns.h"
|
2009-11-27 15:07:44 +00:00
|
|
|
|
#include "fmtx.h"
|
2008-09-05 16:09:58 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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;
|
2009-11-27 15:07:44 +00:00
|
|
|
|
uint32_t pkt_final;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
uint64_t time_start; // 90kHz
|
2009-11-27 15:07:44 +00:00
|
|
|
|
uint64_t time_last; // time @ last PCR
|
|
|
|
|
uint64_t time_final;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
uint64_t pcr_start; // 90kHz
|
|
|
|
|
uint64_t pcr_last;
|
2009-11-27 15:07:44 +00:00
|
|
|
|
uint64_t ts_byte_start;
|
|
|
|
|
uint64_t ts_byte_final;
|
2011-07-18 09:44:01 +00:00
|
|
|
|
int32_t rtp_skew_min;
|
|
|
|
|
int32_t rtp_skew_max;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
};
|
|
|
|
|
|
2011-07-06 11:30:23 +00:00
|
|
|
|
typedef struct pcapreport_vlan_info_s
|
|
|
|
|
{
|
|
|
|
|
uint16_t vid;
|
|
|
|
|
uint16_t cfimap;
|
|
|
|
|
uint16_t pcpmap;
|
|
|
|
|
} pcapreport_vlan_info_t;
|
|
|
|
|
|
2011-07-18 09:44:01 +00:00
|
|
|
|
typedef struct pcapreport_rtp_info_s
|
|
|
|
|
{
|
|
|
|
|
uint32_t n;
|
|
|
|
|
uint32_t ssrc;
|
|
|
|
|
int multiple_ssrc;
|
|
|
|
|
} pcapreport_rtp_info_t;
|
|
|
|
|
|
|
|
|
|
// RTP info (if any) in a packet
|
|
|
|
|
typedef struct rtp_header_s
|
|
|
|
|
{
|
|
|
|
|
int is_rtp;
|
|
|
|
|
int marker;
|
|
|
|
|
uint8_t payload_type;
|
|
|
|
|
uint16_t sequence_number;
|
|
|
|
|
uint32_t timestamp;
|
|
|
|
|
uint32_t ssrc;
|
|
|
|
|
uint32_t header_len;
|
|
|
|
|
uint32_t pad_len;
|
|
|
|
|
// CSRC ignored
|
|
|
|
|
// Extension ignored
|
|
|
|
|
} rtp_header_t;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
struct pcapreport_stream_struct {
|
|
|
|
|
pcapreport_stream_t * hash_next;
|
|
|
|
|
|
2008-09-05 16:09:58 +00:00
|
|
|
|
char *output_name;
|
|
|
|
|
FILE *output_file;
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t output_dest_addr;
|
|
|
|
|
uint32_t output_dest_port;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-10-26 16:21:42 +00:00
|
|
|
|
FILE * csv_file;
|
|
|
|
|
const char * csv_name;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
int stream_no;
|
|
|
|
|
int force; // We have an explicit filter - try harder
|
|
|
|
|
int ts_good; // Not a boolean -ve is bad, +ve is good
|
2010-04-29 16:29:39 +00:00
|
|
|
|
int seen_good; // Includes those seen_dodgy
|
2009-10-26 11:56:18 +00:00
|
|
|
|
int seen_bad;
|
2010-04-29 16:29:39 +00:00
|
|
|
|
int seen_dodgy; // Count of packets that we aren't completely happy with but have declared good
|
2009-10-26 11:56:18 +00:00
|
|
|
|
int multiple_pcr_pids;
|
|
|
|
|
|
2008-09-05 18:06:52 +00:00
|
|
|
|
TS_reader_p ts_r;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
uint32_t pcr_pid;
|
|
|
|
|
|
2008-09-05 18:06:52 +00:00
|
|
|
|
// The temporary read buffer used by our ts reader.
|
2008-09-09 19:01:50 +00:00
|
|
|
|
byte *tmp_buf;
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t tmp_len;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
|
|
|
|
// ts packet counter for error reporting.
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t ts_counter;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-27 17:29:04 +00:00
|
|
|
|
// Count overlength packets
|
|
|
|
|
uint32_t pkts_overlength;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
/*! How far do we need to skew (in 90kHz units) to signal a discontinuity? */
|
|
|
|
|
int64_t skew_discontinuity_threshold;
|
|
|
|
|
|
|
|
|
|
int64_t last_time_offset;
|
|
|
|
|
|
2009-11-27 15:07:44 +00:00
|
|
|
|
uint64_t ts_bytes;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
pcapreport_section_t * section_first;
|
|
|
|
|
pcapreport_section_t * section_last;
|
|
|
|
|
|
2011-07-06 11:30:23 +00:00
|
|
|
|
int vlan_count;
|
|
|
|
|
pcapreport_vlan_info_t vlans[ETHERNET_VLANS_MAX];
|
2011-07-18 09:44:01 +00:00
|
|
|
|
pcapreport_rtp_info_t rtp_info;
|
2011-07-06 11:30:23 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
jitter_env_t jitter;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2009-11-27 15:07:44 +00:00
|
|
|
|
typedef struct pcapreport_fragment_struct
|
|
|
|
|
{
|
|
|
|
|
int in_use;
|
|
|
|
|
uint16_t ident;
|
|
|
|
|
uint16_t current_len;
|
|
|
|
|
byte pkt[65536];
|
|
|
|
|
} pcapreport_fragment_t;
|
|
|
|
|
|
|
|
|
|
typedef struct pcapreport_reassembly_struct
|
|
|
|
|
{
|
|
|
|
|
pcapreport_fragment_t frag;
|
|
|
|
|
} pcapreport_reassembly_t;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
typedef struct pcapreport_ctx_struct
|
|
|
|
|
{
|
|
|
|
|
int use_stdin;
|
|
|
|
|
char *input_name;
|
2009-10-26 16:21:42 +00:00
|
|
|
|
const char * base_name;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
int had_input_name;
|
2009-10-26 16:21:42 +00:00
|
|
|
|
int extract_data;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
int dump_data;
|
|
|
|
|
int dump_extra;
|
|
|
|
|
int time_report;
|
|
|
|
|
int verbose;
|
|
|
|
|
int analyse;
|
2009-10-26 16:21:42 +00:00
|
|
|
|
int extract;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
int stream_count;
|
2009-10-26 16:21:42 +00:00
|
|
|
|
int csv_gen;
|
2010-04-29 16:29:39 +00:00
|
|
|
|
int good_ts_only;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
PCAP_reader_p pcreader;
|
|
|
|
|
pcap_hdr_t pcap_hdr;
|
|
|
|
|
|
2009-11-27 15:07:44 +00:00
|
|
|
|
unsigned int tfmt;
|
|
|
|
|
|
2008-09-05 18:06:52 +00:00
|
|
|
|
// packet counter.
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t pkt_counter;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
uint32_t filter_dest_addr;
|
|
|
|
|
uint32_t filter_dest_port;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
const char * output_name_base;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
int64_t opt_skew_discontinuity_threshold;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
uint64_t time_start; // 90kHz
|
|
|
|
|
uint32_t time_usec;
|
|
|
|
|
time_t time_sec;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
pcapreport_stream_t * stream_hash[256];
|
2009-11-27 15:07:44 +00:00
|
|
|
|
pcapreport_reassembly_t reassembly_env;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
} pcapreport_ctx_t;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
static unsigned int
|
|
|
|
|
jitter_value(const jitter_env_t * const je)
|
|
|
|
|
{
|
|
|
|
|
return je->max_val - je->min_val;
|
|
|
|
|
}
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2011-07-18 09:44:01 +00:00
|
|
|
|
// Init "obvious" non-zero stuff
|
|
|
|
|
tsect->rtp_skew_max = -0x7fffffff;
|
|
|
|
|
tsect->rtp_skew_min = 0x7fffffff;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
return tsect;
|
|
|
|
|
}
|
2008-09-05 16:09:58 +00:00
|
|
|
|
|
2008-09-05 18:06:52 +00:00
|
|
|
|
// Discontinuity threshold is 6s.
|
|
|
|
|
#define SKEW_DISCONTINUITY_THRESHOLD (6*90000)
|
|
|
|
|
|
2008-09-09 19:01:50 +00:00
|
|
|
|
static int digest_times_read(void *handle, byte *out_buf, size_t len)
|
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
memcpy(out_buf, st->tmp_buf, nr_bytes);
|
|
|
|
|
memmove(st->tmp_buf, &st->tmp_buf[nr_bytes],
|
2008-09-09 19:01:50 +00:00
|
|
|
|
new_tmp_len);
|
2009-10-26 11:56:18 +00:00
|
|
|
|
st->tmp_len = new_tmp_len;
|
2009-05-04 21:21:17 +00:00
|
|
|
|
// fprint_msg(">> read %d bytes from intermediate buffer. \n", nr_bytes);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
|
|
|
|
return nr_bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int digest_times_seek(void *handle, offset_t val)
|
|
|
|
|
{
|
|
|
|
|
// Cannot seek in a ts stream.
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-07-18 09:44:01 +00:00
|
|
|
|
static int digest_times(pcapreport_ctx_t * const ctx,
|
2009-10-26 11:56:18 +00:00
|
|
|
|
pcapreport_stream_t * const st,
|
2011-07-18 09:44:01 +00:00
|
|
|
|
const pcaprec_hdr_t * const pcap_pkt_hdr,
|
|
|
|
|
const ethernet_packet_t * const epkt,
|
|
|
|
|
const ipv4_header_t * const ipv4_header,
|
|
|
|
|
const ipv4_udp_header_t * const udp_header,
|
|
|
|
|
const rtp_header_t * const rtp_header,
|
|
|
|
|
const byte * const data,
|
2008-10-18 15:04:34 +00:00
|
|
|
|
const uint32_t len)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
int rv;
|
2009-11-27 15:07:44 +00:00
|
|
|
|
const uint64_t ts_byte_start = st->ts_bytes;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2011-07-18 09:44:01 +00:00
|
|
|
|
// Deal with RTP contents - currently held with stream but could be moved to section
|
|
|
|
|
// especially is we do more timestamp analysis
|
|
|
|
|
if (rtp_header->is_rtp)
|
|
|
|
|
{
|
|
|
|
|
pcapreport_rtp_info_t * const ri = &st->rtp_info;
|
|
|
|
|
|
|
|
|
|
if (ri->ssrc != rtp_header->ssrc && ri->n != 0 && !ri->multiple_ssrc)
|
|
|
|
|
{
|
|
|
|
|
fprint_msg("!%d! Multiple SSRCs detected: SSRCs: %u,%u,...\n", st->stream_no,
|
|
|
|
|
ri->ssrc, rtp_header->ssrc);
|
|
|
|
|
ri->multiple_ssrc = TRUE;
|
|
|
|
|
}
|
|
|
|
|
++ri->n;
|
|
|
|
|
ri->ssrc = rtp_header->ssrc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (st->ts_r == NULL)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
rv = build_TS_reader_with_fns(st,
|
2008-09-09 19:01:50 +00:00
|
|
|
|
digest_times_read,
|
|
|
|
|
digest_times_seek,
|
2009-10-26 11:56:18 +00:00
|
|
|
|
&st->ts_r);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
if (rv)
|
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
print_err( "### pcapreport: Cannot create ts reader.\n");
|
2008-09-09 19:01:50 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add all our data to the pool.
|
2009-10-27 17:29:04 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned int pkts = len / 188;
|
|
|
|
|
unsigned int pktlen = pkts * 188;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-27 17:29:04 +00:00
|
|
|
|
if (pktlen != len)
|
|
|
|
|
++st->pkts_overlength;
|
|
|
|
|
|
|
|
|
|
st->tmp_buf = (byte *)realloc(st->tmp_buf, st->tmp_len + pktlen);
|
|
|
|
|
memcpy(&st->tmp_buf[st->tmp_len], data, pktlen);
|
|
|
|
|
st->tmp_len += pktlen;
|
2009-11-27 15:07:44 +00:00
|
|
|
|
st->ts_bytes += pktlen;
|
2009-10-27 17:29:04 +00:00
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
|
|
|
|
// Now read out all the ts packets we can.
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
byte *pkt;
|
|
|
|
|
int rv;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
rv = read_next_TS_packet(st->ts_r, &pkt);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
if (rv == EOF)
|
|
|
|
|
{
|
|
|
|
|
// Got to EOF - return for more data
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Right. Split it ..
|
|
|
|
|
{
|
2009-11-27 15:07:44 +00:00
|
|
|
|
const uint64_t t_pcr = pkt_time(pcap_pkt_hdr);
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t pid;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
int pusi;
|
|
|
|
|
byte *adapt;
|
|
|
|
|
int adapt_len;
|
|
|
|
|
byte *payload;
|
|
|
|
|
int payload_len;
|
|
|
|
|
|
|
|
|
|
rv = split_TS_packet(pkt, &pid, &pusi, &adapt, &adapt_len,
|
|
|
|
|
&payload, &payload_len);
|
|
|
|
|
if (rv)
|
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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,
|
2009-05-04 21:21:17 +00:00
|
|
|
|
pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//int cc;
|
|
|
|
|
|
|
|
|
|
// PCR ?
|
|
|
|
|
if (adapt && adapt_len)
|
|
|
|
|
{
|
|
|
|
|
int has_pcr;
|
|
|
|
|
uint64_t pcr;
|
|
|
|
|
int64_t pcr_time_offset;
|
|
|
|
|
|
|
|
|
|
get_PCR_from_adaptation_field(adapt, adapt_len, &has_pcr,
|
|
|
|
|
&pcr);
|
2009-10-26 11:56:18 +00:00
|
|
|
|
|
2008-09-09 19:01:50 +00:00
|
|
|
|
if (has_pcr)
|
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
int64_t skew;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (ctx->time_report)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (st->pcr_pid == 0)
|
|
|
|
|
st->pcr_pid = pid;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (pid != st->pcr_pid)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
// *** If this happens often then fix to track each Pid
|
|
|
|
|
if (!st->multiple_pcr_pids)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
fprint_msg("!%d! Multiple pids detected: pids: %d,%d,...\n",
|
|
|
|
|
st->stream_no, st->pcr_pid, pid);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2009-10-26 11:56:18 +00:00
|
|
|
|
st->multiple_pcr_pids = TRUE;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
// PCR pops out in 27MHz units. Let's do all our comparisons
|
|
|
|
|
// in 90kHz.
|
|
|
|
|
pcr /= 300;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
// fprint_msg("pcr = %lld t_pcr = %lld diff = %lld\n",
|
|
|
|
|
// pcr, t_pcr, t_pcr - pcr);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
pcr_time_offset = ((int64_t)t_pcr - (int64_t)pcr);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-27 15:07:44 +00:00
|
|
|
|
tsect->pkt_final =
|
2009-10-26 11:56:18 +00:00
|
|
|
|
tsect->pkt_start = ctx->pkt_counter;
|
|
|
|
|
tsect->pcr_last =
|
|
|
|
|
tsect->pcr_start = pcr;
|
|
|
|
|
tsect->time_last =
|
|
|
|
|
tsect->time_start = t_pcr;
|
2009-11-27 15:07:44 +00:00
|
|
|
|
tsect->ts_byte_start =
|
|
|
|
|
tsect->ts_byte_final = ts_byte_start;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2011-07-18 09:44:01 +00:00
|
|
|
|
if (rtp_header->is_rtp)
|
|
|
|
|
{
|
|
|
|
|
// We have both PCR & RTP times - look for min & max
|
|
|
|
|
int32_t rtp_skew = (int32_t)(rtp_header->timestamp - (uint32_t)(t_pcr & 0xffffffffU));
|
|
|
|
|
if (tsect->rtp_skew_max < rtp_skew)
|
|
|
|
|
tsect->rtp_skew_max = rtp_skew;
|
|
|
|
|
if (tsect->rtp_skew_min > rtp_skew)
|
|
|
|
|
tsect->rtp_skew_min = rtp_skew;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 16:21:42 +00:00
|
|
|
|
if (st->csv_name != NULL) // We should be outputting to file
|
|
|
|
|
{
|
|
|
|
|
if (st->csv_file == NULL)
|
|
|
|
|
{
|
|
|
|
|
if ((st->csv_file = fopen(st->csv_name, "wt")) == NULL)
|
|
|
|
|
{
|
|
|
|
|
fprint_err("### pcapreport: Cannot open %s .\n",
|
|
|
|
|
st->csv_name);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
fprintf(st->csv_file, "\"PKT\",\"Time\",\"PCR\",\"Skew\",\"Jitter\"\n");
|
|
|
|
|
}
|
2011-04-15 15:10:29 +00:00
|
|
|
|
fprintf(st->csv_file, "%d," LLU_FORMAT "," LLU_FORMAT "," LLD_FORMAT ",%u\n", ctx->pkt_counter,
|
|
|
|
|
t_pcr - ctx->time_start, pcr, skew, cur_jitter);
|
2009-10-26 16:21:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
// Remember where we are for posterity
|
|
|
|
|
tsect->pcr_last = pcr;
|
|
|
|
|
tsect->time_last = t_pcr;
|
|
|
|
|
|
|
|
|
|
st->last_time_offset = pcr_time_offset;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-18 09:44:01 +00:00
|
|
|
|
// Actions at end of TS packet
|
2009-11-27 15:07:44 +00:00
|
|
|
|
{
|
|
|
|
|
pcapreport_section_t * const tsect = st->section_last;
|
|
|
|
|
if (tsect != NULL)
|
|
|
|
|
{
|
|
|
|
|
tsect->time_final = t_pcr;
|
|
|
|
|
tsect->ts_byte_final = st->ts_bytes;
|
|
|
|
|
tsect->pkt_final = ctx->pkt_counter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
++st->ts_counter;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
static int write_out_packet(pcapreport_ctx_t * const ctx,
|
|
|
|
|
pcapreport_stream_t * const st,
|
2008-09-09 19:01:50 +00:00
|
|
|
|
const byte *data,
|
2008-10-18 15:04:34 +00:00
|
|
|
|
const uint32_t len)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
int rv;
|
2009-10-27 17:29:04 +00:00
|
|
|
|
unsigned int pkts = len / 188;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (st->output_name)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (!st->output_file)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-27 17:29:04 +00:00
|
|
|
|
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);
|
2009-10-26 11:56:18 +00:00
|
|
|
|
st->output_file = fopen(st->output_name, "wb");
|
|
|
|
|
if (!st->output_file)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_err("### pcapreport: Cannot open %s .\n",
|
2009-10-26 11:56:18 +00:00
|
|
|
|
st->output_name);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2008-09-09 19:01:50 +00:00
|
|
|
|
if (ctx->verbose)
|
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_msg("++ Dumping %d bytes to output file.\n", len);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2009-10-27 17:29:04 +00:00
|
|
|
|
rv = fwrite(data, 188, pkts, st->output_file);
|
|
|
|
|
if (rv != pkts)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_err( "### pcapreport: Couldn't write %d bytes"
|
|
|
|
|
" to %s (error = %d).\n",
|
2009-10-26 11:56:18 +00:00
|
|
|
|
len, st->output_name,
|
|
|
|
|
ferror(st->output_file));
|
2008-09-09 19:01:50 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2011-07-06 11:30:23 +00:00
|
|
|
|
static int
|
2011-07-18 09:44:01 +00:00
|
|
|
|
stream_ts_check(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const st,
|
2009-10-26 11:56:18 +00:00
|
|
|
|
const byte *data,
|
|
|
|
|
const uint32_t len)
|
|
|
|
|
{
|
|
|
|
|
const byte * ptr;
|
2010-04-29 16:29:39 +00:00
|
|
|
|
int good = 0;
|
|
|
|
|
int bad = 0;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
|
|
|
|
|
if (st->force)
|
|
|
|
|
st->ts_good = 10;
|
|
|
|
|
|
|
|
|
|
if (len % 188 != 0)
|
2010-04-29 16:29:39 +00:00
|
|
|
|
++bad;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
else
|
2010-04-29 16:29:39 +00:00
|
|
|
|
++good;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
|
|
|
|
|
for (ptr = data; ptr < data + len; ptr += 188)
|
|
|
|
|
{
|
|
|
|
|
if (*ptr != 0x47)
|
2010-04-29 16:29:39 +00:00
|
|
|
|
++bad;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
else
|
2010-04-29 16:29:39 +00:00
|
|
|
|
++good;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-04-29 16:29:39 +00:00
|
|
|
|
st->ts_good += good - bad;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (st->ts_good > 10)
|
|
|
|
|
st->ts_good = 10;
|
|
|
|
|
if (st->ts_good < -10)
|
|
|
|
|
st->ts_good = -10;
|
|
|
|
|
|
2010-04-29 16:29:39 +00:00
|
|
|
|
if (st->ts_good <= 0 || (bad != 0 && ctx->good_ts_only))
|
2009-10-26 11:56:18 +00:00
|
|
|
|
{
|
|
|
|
|
++st->seen_bad;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-29 16:29:39 +00:00
|
|
|
|
if (bad != 0)
|
|
|
|
|
++st->seen_dodgy;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
++st->seen_good;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-18 09:44:01 +00:00
|
|
|
|
// RTP - RFC 3550
|
|
|
|
|
// RTP payload types - RFC 3551
|
|
|
|
|
// M2TS - RFC 2250
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
stream_rtp_check(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const st,
|
|
|
|
|
const byte * const data,
|
|
|
|
|
const uint32_t len,
|
|
|
|
|
rtp_header_t * const rh)
|
|
|
|
|
{
|
|
|
|
|
uint32_t offset;
|
|
|
|
|
uint32_t padlen = 0;
|
|
|
|
|
|
|
|
|
|
// Flatten output
|
|
|
|
|
memset(rh, 0, sizeof(*rh));
|
|
|
|
|
|
|
|
|
|
// Must contain at least the header!
|
|
|
|
|
if (len < 12)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
// Check version - must be 2
|
|
|
|
|
// Incidentally this will reject 0x47 which is good :-)
|
|
|
|
|
if ((data[0] & 0xc0) != 0x80)
|
|
|
|
|
return FALSE;
|
|
|
|
|
// We only deal with TS in RTP so check for that alone
|
|
|
|
|
if ((data[1] & 0x7f) != 33) // PT bits
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
// ??Check sequence??
|
|
|
|
|
|
|
|
|
|
// offset = start of extension or payload
|
|
|
|
|
offset = 12 + (data[0] & 0xf) * 4;
|
|
|
|
|
|
|
|
|
|
// Check for padding
|
|
|
|
|
if ((data[0] & 0x20) != 0) // P bit
|
|
|
|
|
{
|
|
|
|
|
padlen = data[len - 1];
|
|
|
|
|
// Padlen of zero makes no sense as padding len includes this byte
|
|
|
|
|
if (padlen == 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for extension
|
|
|
|
|
if ((data[0] & 0x10) != 0) // X bit
|
|
|
|
|
{
|
|
|
|
|
if (offset + 4 + padlen > len)
|
|
|
|
|
return FALSE;
|
|
|
|
|
// Skip extension header
|
|
|
|
|
offset += 4 + uint_16_be(data + offset + 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// trivial check for TS in payload
|
|
|
|
|
if (offset + 188 + padlen > len || data[offset] != 0x47)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
rh->is_rtp = TRUE;
|
|
|
|
|
rh->marker = ((data[1] & 0x80) != 0);
|
|
|
|
|
rh->payload_type = data[0] & 0x7f;
|
|
|
|
|
rh->sequence_number = uint_16_be(data + 2);
|
|
|
|
|
rh->timestamp = uint_32_be(data + 4);
|
|
|
|
|
rh->ssrc = uint_32_be(data + 8);
|
|
|
|
|
rh->header_len = offset;
|
|
|
|
|
rh->pad_len = padlen;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-06 11:30:23 +00:00
|
|
|
|
static void
|
|
|
|
|
stream_merge_vlan_info(pcapreport_stream_t * const st, const ethernet_packet_t * const epkt)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < epkt->vlan_count; ++i)
|
|
|
|
|
{
|
|
|
|
|
st->vlans[i].cfimap |= (1 << epkt->vlans[i].cfi);
|
|
|
|
|
st->vlans[i].pcpmap |= (1 << epkt->vlans[i].pcp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
vlan_name(const char * prefix, const pcapreport_stream_t * const st, char * const buf)
|
2009-10-26 11:56:18 +00:00
|
|
|
|
{
|
2011-07-06 11:30:23 +00:00
|
|
|
|
if (st->vlan_count == 0)
|
|
|
|
|
{
|
|
|
|
|
buf[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
size_t n = strlen(prefix);
|
|
|
|
|
char * p = buf;
|
|
|
|
|
memcpy(p, prefix, n);
|
|
|
|
|
p += n;
|
|
|
|
|
for (i = 0; i < st->vlan_count; ++i)
|
|
|
|
|
{
|
|
|
|
|
const pcapreport_vlan_info_t * const vi = st->vlans + i;
|
|
|
|
|
if (i != 0)
|
|
|
|
|
*p++ = '.';
|
|
|
|
|
p += sprintf(p, "%d", vi->vid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static pcapreport_stream_t *
|
|
|
|
|
stream_create(pcapreport_ctx_t * const ctx,
|
|
|
|
|
const ethernet_packet_t * const epkt, uint32_t const dest_addr, const uint32_t dest_port)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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;
|
2011-07-06 11:30:23 +00:00
|
|
|
|
st->vlan_count = epkt->vlan_count;
|
|
|
|
|
for (i = 0; i < epkt->vlan_count; ++i)
|
|
|
|
|
{
|
|
|
|
|
st->vlans[i].vid = epkt->vlans[i].vid;
|
|
|
|
|
// Maps are zero - will be filled in by merge_vlan_info
|
|
|
|
|
}
|
2009-10-26 11:56:18 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2009-10-26 16:21:42 +00:00
|
|
|
|
if (ctx->extract)
|
2009-10-26 11:56:18 +00:00
|
|
|
|
{
|
2009-10-26 16:21:42 +00:00
|
|
|
|
const char * const base_name = ctx->output_name_base != NULL ? ctx->output_name_base : ctx->base_name;
|
|
|
|
|
size_t len = strlen(base_name);
|
2011-07-06 11:30:23 +00:00
|
|
|
|
char pbuf[32];
|
|
|
|
|
|
2011-07-06 12:03:51 +00:00
|
|
|
|
st->output_name = malloc(len + 64);
|
2009-10-26 16:21:42 +00:00
|
|
|
|
memcpy(st->output_name, base_name, len + 1);
|
2009-10-26 11:56:18 +00:00
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
{
|
2011-07-06 11:30:23 +00:00
|
|
|
|
sprintf(st->output_name + len, "%s_%u.%u.%u.%u_%u.ts",
|
|
|
|
|
vlan_name("_V", st, pbuf),
|
2009-10-26 11:56:18 +00:00
|
|
|
|
dest_addr >> 24, (dest_addr >> 16) & 0xff,
|
|
|
|
|
(dest_addr >> 8) & 0xff, dest_addr & 0xff,
|
|
|
|
|
dest_port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 16:21:42 +00:00
|
|
|
|
if (ctx->csv_gen)
|
|
|
|
|
{
|
|
|
|
|
const size_t len = strlen(ctx->base_name);
|
2011-07-06 12:03:51 +00:00
|
|
|
|
char * const name = malloc(len + 64);
|
|
|
|
|
char pbuf[32];
|
|
|
|
|
|
2009-10-26 16:21:42 +00:00
|
|
|
|
memcpy(name, ctx->base_name, len + 1);
|
|
|
|
|
|
|
|
|
|
if (ctx->filter_dest_addr == 0 || ctx->filter_dest_port == 0)
|
|
|
|
|
{
|
2011-07-06 12:03:51 +00:00
|
|
|
|
sprintf(name + len, "%s_%u.%u.%u.%u_%u.csv",
|
|
|
|
|
vlan_name("_V", st, pbuf),
|
2009-10-26 16:21:42 +00:00
|
|
|
|
dest_addr >> 24, (dest_addr >> 16) & 0xff,
|
|
|
|
|
(dest_addr >> 8) & 0xff, dest_addr & 0xff,
|
|
|
|
|
dest_port);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
strcpy(name + len, ".csv");
|
|
|
|
|
st->csv_name = name;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
return st;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-06 11:30:23 +00:00
|
|
|
|
static char *
|
|
|
|
|
map_to_string(unsigned int n, char * const buf)
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
char * p = buf;
|
|
|
|
|
int first = TRUE;
|
|
|
|
|
|
|
|
|
|
while (n != 0)
|
|
|
|
|
{
|
|
|
|
|
if ((n & 1) != 0)
|
|
|
|
|
{
|
|
|
|
|
if (!first)
|
|
|
|
|
*p++ = ',';
|
|
|
|
|
p += sprintf(p, "%d", i);
|
|
|
|
|
first = FALSE;
|
|
|
|
|
}
|
|
|
|
|
n >>= 1;
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2011-07-18 09:44:01 +00:00
|
|
|
|
stream_analysis(const pcapreport_ctx_t * const ctx, const pcapreport_stream_t * const st)
|
2009-10-26 11:56:18 +00:00
|
|
|
|
{
|
|
|
|
|
uint32_t dest_addr = st->output_dest_addr;
|
2011-07-06 11:30:23 +00:00
|
|
|
|
char pbuf[32];
|
2009-11-27 15:07:44 +00:00
|
|
|
|
|
|
|
|
|
if (ctx->verbose < 1 && st->seen_good == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2011-07-06 11:30:23 +00:00
|
|
|
|
fprint_msg("Stream %d: Dest:%s %u.%u.%u.%u:%u\n",
|
2009-10-26 11:56:18 +00:00
|
|
|
|
st->stream_no,
|
2011-07-06 11:30:23 +00:00
|
|
|
|
vlan_name(" VLAN:", st, pbuf),
|
2009-10-26 11:56:18 +00:00
|
|
|
|
dest_addr >> 24, (dest_addr >> 16) & 0xff,
|
|
|
|
|
(dest_addr >> 8) & 0xff, dest_addr & 0xff,
|
|
|
|
|
st->output_dest_port);
|
2011-07-06 11:30:23 +00:00
|
|
|
|
if (st->vlan_count != 0)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < st->vlan_count; ++i)
|
|
|
|
|
{
|
|
|
|
|
const pcapreport_vlan_info_t * const vi = st->vlans + i;
|
|
|
|
|
char pbuf1[64], pbuf2[64];
|
|
|
|
|
|
|
|
|
|
fprint_msg(" VLAN %d: cfi:[%s], pcp[%s]\n", vi->vid,
|
|
|
|
|
map_to_string(vi->cfimap, pbuf1), map_to_string(vi->pcpmap, pbuf2));
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-18 09:44:01 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2010-04-29 16:29:39 +00:00
|
|
|
|
fprint_msg(" Pkts: Good=%d, Dodgy=%d, Bad=%d, Overlength=%u\n",
|
|
|
|
|
st->seen_good - st->seen_dodgy, st->seen_dodgy, st->seen_bad, st->pkts_overlength);
|
2011-07-18 09:44:01 +00:00
|
|
|
|
|
|
|
|
|
if (st->rtp_info.n != 0)
|
|
|
|
|
{
|
|
|
|
|
const pcapreport_rtp_info_t * const ri = &st->rtp_info;
|
|
|
|
|
fprint_msg(" RTP TS packets: %d, SSRC: %u%s\n", ri->n, ri->ssrc, ri->multiple_ssrc ? "..." : "");
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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;
|
2009-11-27 15:07:44 +00:00
|
|
|
|
int64_t time_len2 = tsect->time_final - tsect->time_start;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
int64_t pcr_len = tsect->pcr_last - tsect->pcr_start;
|
|
|
|
|
int64_t drift = time_len - pcr_len;
|
2009-11-27 15:07:44 +00:00
|
|
|
|
fprint_msg(" Section %d:\n", tsect->section_no);
|
|
|
|
|
fprint_msg(" Pkts: %u->%u\n", tsect->pkt_start, tsect->pkt_final);
|
|
|
|
|
fprint_msg(" Bytes: %llu (%llu bits/sec)\n", tsect->ts_byte_final - tsect->ts_byte_start,
|
|
|
|
|
time_len2 == 0LL ? 0LL : (tsect->ts_byte_final - tsect->ts_byte_start) * 8ULL * 90000ULL / time_len2);
|
|
|
|
|
fprint_msg(" Time (Total): %s->%s (%s)\n",
|
|
|
|
|
fmtx_timestamp(tsect->time_start - time_offset, ctx->tfmt),
|
|
|
|
|
fmtx_timestamp(tsect->time_final - time_offset, ctx->tfmt),
|
|
|
|
|
fmtx_timestamp(time_len2, ctx->tfmt));
|
|
|
|
|
fprint_msg(" Time (PCRs): %s->%s (%s)\n",
|
|
|
|
|
fmtx_timestamp(tsect->time_start - time_offset, ctx->tfmt),
|
|
|
|
|
fmtx_timestamp(tsect->time_last - time_offset, ctx->tfmt),
|
|
|
|
|
fmtx_timestamp(time_len, ctx->tfmt));
|
|
|
|
|
fprint_msg(" PCR: %s->%s (%s)\n",
|
|
|
|
|
fmtx_timestamp(tsect->pcr_start, ctx->tfmt),
|
|
|
|
|
fmtx_timestamp(tsect->pcr_last, ctx->tfmt),
|
|
|
|
|
fmtx_timestamp(pcr_len, ctx->tfmt));
|
|
|
|
|
fprint_msg(" Drift: diff=%s; rate=%s/min; 1s per %llds\n",
|
|
|
|
|
fmtx_timestamp(time_len - pcr_len, ctx->tfmt),
|
|
|
|
|
fmtx_timestamp(time_len == 0 ? 0LL : drift * 60LL * 90000LL / time_len, ctx->tfmt),
|
2009-10-26 11:56:18 +00:00
|
|
|
|
drift == 0 ? 0LL : time_len / drift);
|
2009-11-27 15:07:44 +00:00
|
|
|
|
fprint_msg(" Max jitter: %s\n", fmtx_timestamp(tsect->jitter_max, ctx->tfmt));
|
2011-07-18 09:44:01 +00:00
|
|
|
|
if (st->rtp_info.n != 0)
|
|
|
|
|
{
|
|
|
|
|
fprint_msg(" PCR/RTP skew: %s->%s (%s)\n",
|
|
|
|
|
fmtx_timestamp(tsect->rtp_skew_min, ctx->tfmt),
|
|
|
|
|
fmtx_timestamp(tsect->rtp_skew_max, ctx->tfmt),
|
|
|
|
|
fmtx_timestamp(tsect->rtp_skew_max - tsect->rtp_skew_min, ctx->tfmt));
|
|
|
|
|
}
|
2009-10-26 11:56:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-06 11:30:23 +00:00
|
|
|
|
static int
|
|
|
|
|
stream_vlan_match(const pcapreport_stream_t * const st, const ethernet_packet_t * const epkt)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (epkt->vlan_count != st->vlan_count)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < epkt->vlan_count; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (epkt->vlans[i].vid != st->vlans[i].vid)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
pcapreport_stream_t *
|
2011-07-06 11:30:23 +00:00
|
|
|
|
stream_find(pcapreport_ctx_t * const ctx, const ethernet_packet_t * const epkt, uint32_t const dest_addr, const uint32_t dest_port)
|
2009-10-26 11:56:18 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2011-07-06 11:30:23 +00:00
|
|
|
|
if (st->output_dest_addr == dest_addr && st->output_dest_port == dest_port &&
|
|
|
|
|
stream_vlan_match(st, epkt))
|
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
return st;
|
2011-07-06 11:30:23 +00:00
|
|
|
|
}
|
2009-10-26 11:56:18 +00:00
|
|
|
|
pst = &st->hash_next;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-06 11:30:23 +00:00
|
|
|
|
if ((st = stream_create(ctx, epkt, dest_addr, dest_port)) == NULL)
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2009-10-26 16:21:42 +00:00
|
|
|
|
{
|
|
|
|
|
// Free off all our section data
|
|
|
|
|
pcapreport_section_t * p = st->section_first;
|
|
|
|
|
while (p != NULL)
|
|
|
|
|
{
|
|
|
|
|
pcapreport_section_t * np = p->next;
|
|
|
|
|
free(p);
|
|
|
|
|
p = np;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (st->output_file != NULL)
|
2010-04-29 16:29:39 +00:00
|
|
|
|
{
|
|
|
|
|
if (st->seen_dodgy != 0)
|
|
|
|
|
{
|
|
|
|
|
fprint_msg(">%d> WARNING: %d dodgy packet%s written to: %s\n",
|
|
|
|
|
st->stream_no,
|
|
|
|
|
st->seen_dodgy, st->seen_dodgy == 1 ? "" : "s", st->output_name);
|
|
|
|
|
}
|
|
|
|
|
if (st->seen_bad != 0)
|
|
|
|
|
{
|
|
|
|
|
fprint_msg(">%d> WARNING: %d bad packet%s excluded from: %s\n",
|
|
|
|
|
st->stream_no,
|
|
|
|
|
st->seen_bad, st->seen_bad == 1 ? "" : "s", st->output_name);
|
|
|
|
|
}
|
2009-10-26 11:56:18 +00:00
|
|
|
|
fclose(st->output_file);
|
2010-04-29 16:29:39 +00:00
|
|
|
|
}
|
2009-10-26 16:21:42 +00:00
|
|
|
|
if (st->csv_file != NULL)
|
|
|
|
|
fclose(st->csv_file);
|
|
|
|
|
if (st->csv_name != NULL)
|
|
|
|
|
free((void *)st->csv_name);
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (st->output_name != NULL)
|
|
|
|
|
free(st->output_name);
|
|
|
|
|
free(st);
|
|
|
|
|
}
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-11-27 15:07:44 +00:00
|
|
|
|
static int
|
2011-04-15 15:10:29 +00:00
|
|
|
|
ip_reassemble(pcapreport_reassembly_t * const reas, const ipv4_header_t * const ip, byte * const in_data,
|
|
|
|
|
byte ** const out_pdata, uint32_t * const out_plen)
|
2009-11-27 15:07:44 +00:00
|
|
|
|
{
|
|
|
|
|
uint32_t frag_len = ip->length - ip->hdr_length * 4;
|
|
|
|
|
uint32_t frag_offset = ip->frag_offset * 8; // bytes
|
|
|
|
|
int frag_final = (ip->flags & 1) == 0;
|
|
|
|
|
|
|
|
|
|
// Discard unless we succeed
|
2011-04-15 15:10:29 +00:00
|
|
|
|
*out_pdata = (void *)NULL;
|
2009-11-27 15:07:44 +00:00
|
|
|
|
*out_plen = 0;
|
|
|
|
|
|
|
|
|
|
if (frag_final && frag_offset == 0)
|
|
|
|
|
{
|
|
|
|
|
// Normal case - no fragmentation
|
|
|
|
|
*out_pdata = in_data;
|
|
|
|
|
*out_plen = frag_len;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((frag_len & 7) != 0 && !frag_final)
|
|
|
|
|
{
|
|
|
|
|
// Only final fragment may have length that is not a multiple of 8
|
|
|
|
|
fprint_err("### Non-final fragment with bad length: %d\n", frag_len);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (frag_len + frag_offset >= 0x10000)
|
|
|
|
|
{
|
|
|
|
|
// I can't find this explicitly prohibited in RFC791 but it can't be good
|
|
|
|
|
// and the limit should probably be a little less if we were being pedantic
|
|
|
|
|
fprint_err("### Fragment end >= 64k: %d+%d\n", frag_offset, frag_len);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Very limited reassembly
|
|
|
|
|
{
|
|
|
|
|
pcapreport_fragment_t * frag = &reas->frag;
|
|
|
|
|
|
|
|
|
|
if (frag->in_use && frag->ident != ip->ident)
|
|
|
|
|
{
|
|
|
|
|
fprint_err("### Multi-packet fragment reassembly NIF - previous packet discarded\n");
|
|
|
|
|
frag->in_use = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If previously idle then reset stuff
|
|
|
|
|
if (!frag->in_use)
|
|
|
|
|
{
|
|
|
|
|
frag->in_use = 1;
|
|
|
|
|
frag->current_len = 0;
|
|
|
|
|
frag->ident = ip->ident;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (frag->current_len != frag_offset)
|
|
|
|
|
{
|
|
|
|
|
fprint_err("### Reordering fragment reassembly NIF - packet discarded\n");
|
|
|
|
|
frag->in_use = 0;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frag->current_len = frag_offset + frag_len;
|
|
|
|
|
|
|
|
|
|
memcpy(frag->pkt + ip->frag_offset * 8, in_data, frag_len);
|
|
|
|
|
|
|
|
|
|
if (!frag_final)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
*out_pdata = frag->pkt;
|
|
|
|
|
*out_plen = frag->current_len;
|
|
|
|
|
frag->in_use = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ip_reassembly_init(pcapreport_reassembly_t * const reas)
|
|
|
|
|
{
|
|
|
|
|
memset(reas, 0, sizeof(*reas));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-09-05 16:09:58 +00:00
|
|
|
|
static void print_usage()
|
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
print_msg(
|
|
|
|
|
"Usage: pcapreport [switches] <infile>\n"
|
|
|
|
|
"\n"
|
|
|
|
|
);
|
2008-11-11 19:45:13 +00:00
|
|
|
|
REPORT_VERSION("pcapreport");
|
2009-05-04 21:21:17 +00:00
|
|
|
|
print_msg(
|
2008-09-09 19:01:50 +00:00
|
|
|
|
"\n"
|
2009-10-26 11:56:18 +00:00
|
|
|
|
"Report on a pcap capture file.\n"
|
2008-09-09 19:01:50 +00:00
|
|
|
|
"\n"
|
2009-11-27 15:07:44 +00:00
|
|
|
|
" -h This help\n"
|
|
|
|
|
" -h detail More detail on what some terms used by pcapreport mean\n"
|
2009-10-26 16:21:42 +00:00
|
|
|
|
" --name <file>\n"
|
|
|
|
|
" -n <file> Set the default base name for output files; by default\n"
|
|
|
|
|
" this will be the input name without any .pcap suffix\n"
|
|
|
|
|
" -x, --extract Extract TS(s) to files of the default name\n"
|
|
|
|
|
" -c, --csvgen Create a .csv file for each stream containing timing info\n"
|
|
|
|
|
" -output <file>\n"
|
2009-10-26 11:56:18 +00:00
|
|
|
|
" -o <file>, Dump selected UDP payloads to output file(s)\n"
|
|
|
|
|
" Uses given filename if <ip>:<port> specified,\n"
|
|
|
|
|
" otherwise appends <ip>_<port> to filename per TS\n"
|
2009-10-26 16:21:42 +00:00
|
|
|
|
" Is much the same as -x -n <name>\n"
|
2009-10-26 11:56:18 +00:00
|
|
|
|
" -a Analyse. Produces summary info on every TS in the pcap\n"
|
|
|
|
|
" -d <dest ip>:<port>\n"
|
|
|
|
|
" -d <dest ip> Select data with the given destination IP and port.\n"
|
|
|
|
|
" If the <port> is not specified, it defaults to 0\n"
|
|
|
|
|
" (see below).\n"
|
2010-04-29 16:29:39 +00:00
|
|
|
|
" -g, --good-ts-only Only extract/analyse packets that seem entirely good.\n"
|
|
|
|
|
" By default there is a bit of slack in determining if a\n"
|
|
|
|
|
" packet is good and some dodgy packets are let through\n"
|
|
|
|
|
" This switch ensures that all packets pass simple testing\n"
|
2009-11-27 15:07:44 +00:00
|
|
|
|
" -tfmt 32|90|ms|hms Set time format in report [default = 90kHz units]\n"
|
2009-10-26 11:56:18 +00:00
|
|
|
|
" -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"
|
2008-11-11 19:45:13 +00:00
|
|
|
|
" -skew-discontinuity-threshold <number>\n"
|
2009-10-26 11:56:18 +00:00
|
|
|
|
" -skew <number> Gives the skew discontinuity threshold in 90kHz units.\n"
|
2008-09-09 19:01:50 +00:00
|
|
|
|
"\n"
|
2009-10-26 11:56:18 +00:00
|
|
|
|
" -err stdout Write error messages to standard output (the default)\n"
|
|
|
|
|
" -err stderr Write error messages to standard error (Unix traditional)\n"
|
2009-06-16 20:27:25 +00:00
|
|
|
|
"\n"
|
2009-10-26 11:56:18 +00:00
|
|
|
|
"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"
|
2008-09-09 19:01:50 +00:00
|
|
|
|
"\n"
|
2009-10-26 11:56:18 +00:00
|
|
|
|
"Network packet numbers start at 1 (like wireshark)\n"
|
|
|
|
|
"TS packet numbers start at 0.\n"
|
2008-09-09 19:01:50 +00:00
|
|
|
|
"\n"
|
2009-10-26 11:56:18 +00:00
|
|
|
|
"Positive skew means that we received too low a PCR for this timestamp.\n"
|
2008-09-09 19:01:50 +00:00
|
|
|
|
"\n"
|
|
|
|
|
);
|
2008-09-05 16:09:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-11-27 15:07:44 +00:00
|
|
|
|
static char manpage[] =
|
|
|
|
|
"Times (packet and PCR)\n"
|
|
|
|
|
"----------------------\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"The times associated with packets and PCR are held internally in 90kHz units\n"
|
|
|
|
|
"and are displayed in those units by default\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Stream\n"
|
|
|
|
|
"------\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"A set of packets to the same IP & Port. TS streams are detected by looking\n"
|
|
|
|
|
"for 0x47s at appropriate places in the packets\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Section\n"
|
|
|
|
|
"-------\n"
|
|
|
|
|
"A part of a stream which appears to have a continuous TS embedded in it. If\n"
|
|
|
|
|
"the PCR jumps then a new section should be started (though this will not\n"
|
|
|
|
|
"generate a separate .ts file if the extraction option is in effect, nor will\n"
|
|
|
|
|
"it generate a new .csv file.)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"As it stands pcapreport will only report on a single PCR pid within a TS. If\n"
|
|
|
|
|
"multiple pids with PCRs are detected then this will be reported but the other\n"
|
|
|
|
|
"PCRs will be ignored\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Skew\n"
|
|
|
|
|
"----\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"This is the difference between the time in the pcap for a UDP packet and any\n"
|
|
|
|
|
"PCR found in the TS contained within that packet. The accuracy of this figure\n"
|
|
|
|
|
"obviously depends on how good the clock was in the capture process. Skew is\n"
|
|
|
|
|
"arbitrarily set to zero at the start of a section. A skew of >6s is assumed\n"
|
|
|
|
|
"to be a discontinuity and will start a new section.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Drift\n"
|
|
|
|
|
"-----\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"This is skew over time and (assuming that the playout process is good)\n"
|
|
|
|
|
"represents the difference in speed between the transmitters clock and the\n"
|
|
|
|
|
"receivers clock. The algorithm for determining this isn't very sophisticated\n"
|
|
|
|
|
"so if you have a large maximum jitter or a short sample this should be taken\n"
|
|
|
|
|
"with a pinch of salt. Beware also that PC clocks (like the one in the m/c\n"
|
|
|
|
|
"doing the tcpdump) are not always amongst the most stable or accurate; however\n"
|
|
|
|
|
"they should be good enough to detect gross errors\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Jitter\n"
|
|
|
|
|
"------\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"This is measured as the difference between the maximum and minimum skews over\n"
|
|
|
|
|
"a 10sec (max 1024 samples) period. This should be long enough to capture a\n"
|
|
|
|
|
"good baseline but short enough that drift has a negligible effect\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Max Jitter\n"
|
|
|
|
|
"----------\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"The maximum value of jitter (see above) found in a section\n"
|
|
|
|
|
"";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char *onechararg[26] =
|
|
|
|
|
{
|
|
|
|
|
"analyse", // a
|
|
|
|
|
"", // b
|
|
|
|
|
"csvgen", // c
|
|
|
|
|
"destip", // d
|
|
|
|
|
"", // e
|
|
|
|
|
"", // f
|
2010-04-29 16:29:39 +00:00
|
|
|
|
"good-ts-only", // g
|
2009-11-27 15:07:44 +00:00
|
|
|
|
"help", // h
|
|
|
|
|
"", // i
|
|
|
|
|
"", // j
|
|
|
|
|
"", // k
|
|
|
|
|
"", // l
|
|
|
|
|
"", // m
|
|
|
|
|
"name", // n
|
|
|
|
|
"output", // o
|
|
|
|
|
"", // p
|
|
|
|
|
"", // q
|
|
|
|
|
"", // r
|
|
|
|
|
"", // s
|
|
|
|
|
"times", // t
|
|
|
|
|
"", // u
|
|
|
|
|
"verbose", // v
|
|
|
|
|
"", // w
|
|
|
|
|
"extract", // x
|
|
|
|
|
"", // y
|
|
|
|
|
"" // z
|
|
|
|
|
};
|
|
|
|
|
|
2008-09-05 16:09:58 +00:00
|
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
int err = 0;
|
|
|
|
|
int ii = 1;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
pcapreport_ctx_t sctx = {0};
|
|
|
|
|
pcapreport_ctx_t * const ctx = &sctx;
|
2008-09-05 16:09:58 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
ctx->opt_skew_discontinuity_threshold = SKEW_DISCONTINUITY_THRESHOLD;
|
2009-11-27 15:07:44 +00:00
|
|
|
|
ctx->tfmt = FMTX_TS_DISPLAY_90kHz_RAW;
|
|
|
|
|
|
|
|
|
|
ip_reassembly_init(&ctx->reassembly_env);
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2008-09-05 16:09:58 +00:00
|
|
|
|
if (argc < 2)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
print_usage();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-09-05 16:09:58 +00:00
|
|
|
|
|
|
|
|
|
while (ii < argc)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
if (argv[ii][0] == '-')
|
|
|
|
|
{
|
2009-11-27 15:07:44 +00:00
|
|
|
|
// remove double dashes
|
|
|
|
|
const char c = argv[ii][1];
|
|
|
|
|
const char * const arg = c >= 'a' && c <= 'z' && argv[ii][2] == 0 ? onechararg[c - 'a'] :
|
|
|
|
|
argv[ii][1] == '-' ? argv[ii] + 2 : argv[ii] + 1;
|
|
|
|
|
|
|
|
|
|
if (strcmp("help", arg) == 0)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-11-27 15:07:44 +00:00
|
|
|
|
if (ii + 1 < argc && strcmp("detail", argv[ii + 1]) == 0)
|
|
|
|
|
{
|
|
|
|
|
fwrite(manpage, sizeof(manpage), 1, stdout);
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
print_usage();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (!strcmp("err",arg))
|
2009-06-16 20:27:25 +00:00
|
|
|
|
{
|
|
|
|
|
CHECKARG("pcapreport",ii);
|
|
|
|
|
if (!strcmp(argv[ii+1],"stderr"))
|
|
|
|
|
redirect_output_stderr();
|
|
|
|
|
else if (!strcmp(argv[ii+1],"stdout"))
|
|
|
|
|
redirect_output_stdout();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fprint_err("### pcapreport: "
|
|
|
|
|
"Unrecognised option '%s' to -err (not 'stdout' or"
|
|
|
|
|
" 'stderr')\n",argv[ii+1]);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
ii++;
|
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (!strcmp("output", arg))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2008-11-11 19:45:13 +00:00
|
|
|
|
CHECKARG("pcapreport",ii);
|
2009-10-26 11:56:18 +00:00
|
|
|
|
ctx->output_name_base = argv[++ii];
|
2009-10-26 16:21:42 +00:00
|
|
|
|
ctx->extract_data = TRUE;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (!strcmp("times", arg))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
++ctx->time_report;
|
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (!strcmp("analyse", arg))
|
2009-10-26 11:56:18 +00:00
|
|
|
|
{
|
|
|
|
|
ctx->analyse = TRUE;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (!strcmp("verbose", arg))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
++ctx->verbose;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (!strcmp("destip", arg))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
char *hostname;
|
2008-11-11 19:45:13 +00:00
|
|
|
|
int port = 0;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2008-11-11 19:45:13 +00:00
|
|
|
|
CHECKARG("pcapreport",ii);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
err = host_value("pcapreport", argv[ii], argv[ii+1], &hostname, &port);
|
|
|
|
|
if (err) return 1;
|
|
|
|
|
++ii;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
ctx->filter_dest_port = port;
|
|
|
|
|
if (ipv4_string_to_addr(&ctx->filter_dest_addr, hostname))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_err( "### pcapreport: '%s' is not a host IP address (names are not allowed!)\n",
|
|
|
|
|
hostname);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (!strcmp("dump-data", arg) || !strcmp("D", arg))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
++ctx->dump_data;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (!strcmp("extra-dump", arg) || !strcmp("E", arg))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
++ctx->dump_extra;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (!strcmp("skew-discontinuity-threshold", arg) ||
|
|
|
|
|
!strcmp("skew", arg))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
int val;
|
2008-11-11 19:45:13 +00:00
|
|
|
|
CHECKARG("pcapreport",ii);
|
|
|
|
|
err = int_value("pcapreport", argv[ii], argv[ii+1], TRUE, 0, &val);
|
|
|
|
|
if (err) return 1;
|
2009-10-26 11:56:18 +00:00
|
|
|
|
ctx->opt_skew_discontinuity_threshold = val;
|
2008-11-18 15:49:35 +00:00
|
|
|
|
++ii;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (strcmp("name", arg) == 0)
|
2009-10-26 16:21:42 +00:00
|
|
|
|
{
|
|
|
|
|
CHECKARG("pcapreport",ii);
|
|
|
|
|
ctx->base_name = strdup(argv[++ii]); // So we know it is always malloced
|
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (strcmp("extract", arg) == 0)
|
2009-10-26 16:21:42 +00:00
|
|
|
|
{
|
|
|
|
|
ctx->extract = TRUE;
|
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (strcmp("csvgen", arg) == 0)
|
2009-10-26 16:21:42 +00:00
|
|
|
|
{
|
|
|
|
|
ctx->csv_gen = TRUE;
|
|
|
|
|
}
|
2010-04-29 16:29:39 +00:00
|
|
|
|
else if (strcmp("good-ts-only", arg) == 0)
|
|
|
|
|
{
|
|
|
|
|
ctx->good_ts_only = TRUE;
|
|
|
|
|
}
|
2009-11-27 15:07:44 +00:00
|
|
|
|
else if (strcmp("tfmt", arg) == 0)
|
|
|
|
|
{
|
|
|
|
|
int tfmt;
|
|
|
|
|
CHECKARG("pcapreport",ii);
|
|
|
|
|
if ((tfmt = fmtx_str_to_timestamp_flags(argv[ii + 1])) < 0)
|
|
|
|
|
{
|
|
|
|
|
fprint_err("### Bad timeformat: %s\n", argv[ii + 1]);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
ctx->tfmt = tfmt;
|
|
|
|
|
++ii;
|
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_err( "### pcapreport: "
|
|
|
|
|
"Unrecognised command line switch '%s'\n", argv[ii]);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2008-09-05 16:09:58 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (ctx->had_input_name)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_err( "### pcapreport: Unexpected '%s'\n", argv[ii]);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
2008-09-05 16:09:58 +00:00
|
|
|
|
else
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
ctx->input_name = argv[ii];
|
|
|
|
|
ctx->had_input_name = TRUE;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2008-09-05 16:09:58 +00:00
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
++ii;
|
|
|
|
|
}
|
2008-09-05 16:09:58 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (!ctx->had_input_name)
|
2008-11-11 19:45:13 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
print_err("### pcapreport: No input file specified\n");
|
2008-11-11 19:45:13 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 16:21:42 +00:00
|
|
|
|
if (ctx->base_name == NULL)
|
|
|
|
|
{
|
|
|
|
|
// If we have no default name then use the input name as a base after
|
|
|
|
|
// stripping off any .pcap
|
|
|
|
|
const char * input_name = ctx->input_name == NULL ? "pcap" : ctx->input_name;
|
|
|
|
|
char * buf = strdup(ctx->input_name);
|
|
|
|
|
size_t len = strlen(input_name);
|
|
|
|
|
if (len > 5 && strcmp(".pcap", buf + len - 5) == 0)
|
|
|
|
|
buf[len - 5] = 0;
|
|
|
|
|
ctx->base_name = buf;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
fprint_msg("%s\n",ctx->input_name);
|
2008-11-11 19:45:13 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
err = pcap_open(&ctx->pcreader, &ctx->pcap_hdr, ctx->input_name);
|
2008-09-05 16:09:58 +00:00
|
|
|
|
if (err)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_err("### pcapreport: Unable to open input file %s for reading "
|
|
|
|
|
"PCAP (code %d)\n",
|
2009-10-26 11:56:18 +00:00
|
|
|
|
ctx->had_input_name?ctx->input_name:"<stdin>", err);
|
2008-11-11 19:45:13 +00:00
|
|
|
|
// Just an error code isn't much use - let's look at the source
|
|
|
|
|
// and report something more helpful...
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_err(" %s\n",
|
|
|
|
|
(err==-1?"Unable to open file":
|
|
|
|
|
err==-2?"Unable to allocate PCAP reader datastructure":
|
|
|
|
|
err==-4?"Unable to read PCAP header - is it a PCAP file?":
|
|
|
|
|
"<unrecogised error code>"));
|
2008-11-11 19:45:13 +00:00
|
|
|
|
return 1;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2008-09-05 16:09:58 +00:00
|
|
|
|
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_msg("Capture made by version %u.%u local_tz_correction "
|
|
|
|
|
"%d sigfigs %u snaplen %d network %u\n",
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (ctx->pcap_hdr.snaplen < 65535)
|
2008-09-05 16:09:58 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_err("### pcapreport: WARNING snaplen is %d, not >= 65535 - "
|
|
|
|
|
"not all data may have been captured.\n",
|
2009-10-26 11:56:18 +00:00
|
|
|
|
ctx->pcap_hdr.snaplen);
|
2008-09-05 16:09:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
int done = 0;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2008-09-09 19:01:50 +00:00
|
|
|
|
while (!done)
|
2008-09-05 18:06:52 +00:00
|
|
|
|
{
|
2008-09-09 19:01:50 +00:00
|
|
|
|
pcaprec_hdr_t rec_hdr;
|
|
|
|
|
byte *data = NULL;
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t len = 0;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
int sent_to_output = 0;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
err = pcap_read_next(ctx->pcreader, &rec_hdr, &data, &len);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
switch (err)
|
2008-09-05 18:06:52 +00:00
|
|
|
|
{
|
2008-09-09 19:01:50 +00:00
|
|
|
|
case 0: // EOF.
|
|
|
|
|
++done;
|
|
|
|
|
break;
|
|
|
|
|
case 1: // Got a packet.
|
|
|
|
|
{
|
|
|
|
|
byte *allocated = data;
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
// 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)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_msg("pkt: Time = %d.%d orig_len = %d \n",
|
|
|
|
|
rec_hdr.ts_sec, rec_hdr.ts_usec,
|
|
|
|
|
rec_hdr.orig_len);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (!(ctx->pcap_hdr.network == PCAP_NETWORK_TYPE_ETHERNET))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
goto dump_out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
ethernet_packet_t epkt;
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t out_st, out_len;
|
2008-09-09 19:01:50 +00:00
|
|
|
|
int rv;
|
|
|
|
|
ipv4_header_t ipv4_hdr;
|
|
|
|
|
ipv4_udp_header_t udp_hdr;
|
|
|
|
|
|
|
|
|
|
rv = ethernet_packet_from_pcap(&rec_hdr,
|
|
|
|
|
data, len,
|
|
|
|
|
&epkt,
|
|
|
|
|
&out_st,
|
|
|
|
|
&out_len);
|
|
|
|
|
|
|
|
|
|
if (rv)
|
|
|
|
|
{
|
|
|
|
|
goto dump_out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (ctx->verbose)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_msg("++ 802.11: src %02x:%02x:%02x:%02x:%02x:%02x "
|
|
|
|
|
" dst %02x:%02x:%02x:%02x:%02x:%02x "
|
|
|
|
|
"typeorlen 0x%04x\n",
|
|
|
|
|
epkt.src_addr[0], epkt.src_addr[1],
|
|
|
|
|
epkt.src_addr[2], epkt.src_addr[3],
|
|
|
|
|
epkt.src_addr[4], epkt.src_addr[5],
|
|
|
|
|
epkt.dst_addr[0], epkt.dst_addr[1],
|
|
|
|
|
epkt.dst_addr[2], epkt.dst_addr[3],
|
|
|
|
|
epkt.dst_addr[4], epkt.dst_addr[5],
|
|
|
|
|
epkt.typeorlen);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = &data[out_st];
|
|
|
|
|
len = out_len;
|
|
|
|
|
|
|
|
|
|
// Is it IP?
|
|
|
|
|
if (epkt.typeorlen != 0x800)
|
|
|
|
|
{
|
|
|
|
|
goto dump_out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rv = ipv4_from_payload(data, len,
|
|
|
|
|
&ipv4_hdr,
|
|
|
|
|
&out_st,
|
|
|
|
|
&out_len);
|
|
|
|
|
if (rv)
|
|
|
|
|
{
|
|
|
|
|
goto dump_out;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (ctx->verbose)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_msg("++ IPv4: src = %s",
|
|
|
|
|
ipv4_addr_to_string(ipv4_hdr.src_addr));
|
|
|
|
|
fprint_msg(" dest = %s \n",
|
|
|
|
|
ipv4_addr_to_string(ipv4_hdr.dest_addr));
|
|
|
|
|
|
|
|
|
|
fprint_msg("++ IPv4: version = 0x%x hdr_length = 0x%x"
|
|
|
|
|
" serv_type = 0x%08x length = 0x%04x\n"
|
|
|
|
|
"++ IPv4: ident = 0x%04x flags = 0x%02x"
|
|
|
|
|
" frag_offset = 0x%04x ttl = %d\n"
|
|
|
|
|
"++ IPv4: proto = %d csum = 0x%04x\n",
|
|
|
|
|
ipv4_hdr.version,
|
|
|
|
|
ipv4_hdr.hdr_length,
|
|
|
|
|
ipv4_hdr.serv_type,
|
|
|
|
|
ipv4_hdr.length,
|
|
|
|
|
ipv4_hdr.ident,
|
|
|
|
|
ipv4_hdr.flags,
|
|
|
|
|
ipv4_hdr.frag_offset,
|
|
|
|
|
ipv4_hdr.ttl,
|
|
|
|
|
ipv4_hdr.proto,
|
|
|
|
|
ipv4_hdr.csum);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = &data[out_st];
|
|
|
|
|
len = out_len;
|
|
|
|
|
|
2011-04-15 15:10:29 +00:00
|
|
|
|
if (ip_reassemble(&ctx->reassembly_env, &ipv4_hdr, data, &data, &len) != 0)
|
2009-11-27 15:07:44 +00:00
|
|
|
|
{
|
|
|
|
|
goto dump_out;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-09 19:01:50 +00:00
|
|
|
|
if (!(IPV4_HDR_IS_UDP(&ipv4_hdr)))
|
|
|
|
|
{
|
|
|
|
|
goto dump_out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rv = ipv4_udp_from_payload(data, len,
|
|
|
|
|
&udp_hdr,
|
|
|
|
|
&out_st,
|
|
|
|
|
&out_len);
|
|
|
|
|
if (rv)
|
|
|
|
|
{
|
|
|
|
|
goto dump_out;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (ctx->verbose)
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_msg("++ udp: src port = %d "
|
|
|
|
|
"dest port = %d len = %d \n",
|
|
|
|
|
udp_hdr.source_port,
|
|
|
|
|
udp_hdr.dest_port,
|
|
|
|
|
udp_hdr.length);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = &data[out_st];
|
|
|
|
|
len = out_len;
|
|
|
|
|
|
|
|
|
|
if (
|
2009-10-26 11:56:18 +00:00
|
|
|
|
(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)))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2011-07-06 11:30:23 +00:00
|
|
|
|
pcapreport_stream_t * const st = stream_find(ctx, &epkt, ipv4_hdr.dest_addr, udp_hdr.dest_port);
|
2011-07-18 09:44:01 +00:00
|
|
|
|
rtp_header_t rtp_hdr;
|
2011-07-06 11:30:23 +00:00
|
|
|
|
|
|
|
|
|
stream_merge_vlan_info(st, &epkt);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
2011-07-18 09:44:01 +00:00
|
|
|
|
if (stream_rtp_check(ctx, st, data, len, &rtp_hdr))
|
|
|
|
|
{
|
|
|
|
|
data += rtp_hdr.header_len;
|
|
|
|
|
len -= rtp_hdr.header_len + rtp_hdr.pad_len;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (stream_ts_check(ctx, st, data, len))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
++sent_to_output;
|
|
|
|
|
|
2009-10-26 16:21:42 +00:00
|
|
|
|
if (ctx->time_report || ctx->analyse || ctx->csv_gen)
|
2009-10-26 11:56:18 +00:00
|
|
|
|
{
|
2011-07-18 09:44:01 +00:00
|
|
|
|
rv = digest_times(ctx,
|
2009-10-26 11:56:18 +00:00
|
|
|
|
st,
|
|
|
|
|
&rec_hdr,
|
|
|
|
|
&epkt,
|
|
|
|
|
&ipv4_hdr,
|
2011-07-18 09:44:01 +00:00
|
|
|
|
&udp_hdr,
|
|
|
|
|
&rtp_hdr,
|
2009-10-26 11:56:18 +00:00
|
|
|
|
data, len);
|
|
|
|
|
if (rv) { return rv; }
|
|
|
|
|
}
|
2009-10-26 16:21:42 +00:00
|
|
|
|
if (ctx->extract)
|
2009-10-26 11:56:18 +00:00
|
|
|
|
{
|
|
|
|
|
rv = write_out_packet(ctx, st, data, len);
|
|
|
|
|
if (rv) { return rv; }
|
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Adjust
|
|
|
|
|
dump_out:
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (ctx->dump_data || (ctx->dump_extra && !sent_to_output))
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-05-03 15:57:22 +00:00
|
|
|
|
print_data(TRUE, "data", data, len, len);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
free(allocated); allocated = data = NULL;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// Some other error.
|
2009-05-04 21:21:17 +00:00
|
|
|
|
fprint_err( "### pcapreport: Can't read packet %d - code %d\n",
|
2009-10-26 11:56:18 +00:00
|
|
|
|
ctx->pkt_counter, err);
|
2008-09-09 19:01:50 +00:00
|
|
|
|
++done;
|
|
|
|
|
break;
|
2008-09-05 18:06:52 +00:00
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
2009-10-26 11:56:18 +00:00
|
|
|
|
if (ctx->analyse)
|
|
|
|
|
{
|
|
|
|
|
// 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");
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-09 19:01:50 +00:00
|
|
|
|
{
|
2009-10-26 11:56:18 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
}
|
2008-09-05 18:06:52 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-09-09 19:01:50 +00:00
|
|
|
|
|
|
|
|
|
// Local Variables:
|
|
|
|
|
// tab-width: 8
|
|
|
|
|
// indent-tabs-mode: nil
|
|
|
|
|
// c-basic-offset: 2
|
|
|
|
|
// End:
|
|
|
|
|
// vim: set tabstop=8 shiftwidth=2 expandtab:
|