Add ability for pcapreport to take apart .pcapng files too

master
John Cox 2013-09-03 16:48:26 +01:00
rodzic 73809607d1
commit c053a1eddd
3 zmienionych plików z 572 dodań i 81 usunięć

608
pcap.c
Wyświetl plik

@ -33,15 +33,41 @@
#include "pcap.h"
#include "misc_fns.h"
static int pcap_read_header(PCAP_reader_p ctx, pcap_hdr_t *hdr)
static inline uint32_t uint_32_ctx(const struct _pcap_io_ctx *const ctx, const void *v)
{
uint8_t hdr_val[SIZEOF_PCAP_HDR_ON_DISC];
return ctx->is_be ? uint_32_be(v) : uint_32_le(v);
}
static inline uint16_t uint_16_ctx(const struct _pcap_io_ctx *const ctx, const void *v)
{
return ctx->is_be ? uint_16_be(v) : uint_16_le(v);
}
// Hi-32, Lo-32 but native within!
static inline uint64_t uint_64_be_ctx(const struct _pcap_io_ctx *const ctx, const void *v)
{
return ((uint64_t)uint_32_ctx(ctx, v) << 32) | (uint64_t)uint_32_ctx(ctx, (const char*)v + 4);
}
static inline uint64_t uint_64_ctx(const struct _pcap_io_ctx *const ctx, const void *v)
{
return ctx->is_be ?
((uint64_t)uint_32_be(v) << 32) | (uint64_t)uint_32_be((const char*)v + 4) :
((uint64_t)uint_32_le((const char*)v + 4) << 32) | (uint64_t)uint_32_le(v);
}
static int read_block_header(const struct _pcap_io_ctx *const ctx, uint32_t *const pLength)
{
uint32_t buf[2];
int rv;
rv = fread(&hdr_val[0], SIZEOF_PCAP_HDR_ON_DISC, 1, ctx->file);
*pLength = 0;
rv = fread(buf, 8, 1, ctx->file);
if (rv != 1)
{
if (feof(ctx->file))
if (feof(ctx->file))
{
return 0;
}
@ -51,44 +77,420 @@ static int pcap_read_header(PCAP_reader_p ctx, pcap_hdr_t *hdr)
}
}
*pLength = uint_32_ctx(ctx, buf + 1);
return uint_32_ctx(ctx, buf + 0);
}
/* The magic number is 0xa1b2c3d4. If the writing
* machine was BE, the first byte will be a1 else d4
*/
if (hdr_val[0] == 0xa1)
static int read_chunk(FILE *const f, const size_t len, void **const pBuf)
{
int rv;
void *buf = malloc(len);
*pBuf = NULL;
if (buf == NULL)
{
// Big endian.
ctx->is_be = 1;
return PCAP_ERR_OUT_OF_MEMORY;
}
else if (hdr_val[0] == 0xd4)
rv = fread(buf, len, 1, f);
if (rv != 1)
{
// Little endian.
ctx->is_be = 0;
free(buf);
if (feof(f))
{
return 0;
}
else
{
return PCAP_ERR_FILE_READ;
}
}
*pBuf = buf;
return 1;
}
static int read_options(FILE *const f, const size_t len, void **const pBuf)
{
// If all we have is the final total length data - skip it
if (len <= 4)
{
fseek(f, len, SEEK_CUR);
*pBuf = NULL;
return 1;
}
return read_chunk(f, len, pBuf);
}
typedef enum pcapng_type_e
{
PCAPNG_TYPE_INVALID_BLOCK = 0,
PCAPNG_TYPE_INTERFACE_BLOCK = 1,
PCAPNG_TYPE_PACKET_BLOCK = 2,
PCAPNG_TYPE_SIMPLE_PACKET_BLOCK = 3,
PCAPNG_TYPE_NAME_RESOLUTION_BLOCK = 4,
PCAPNG_TYPE_INTERFACE_STATISTICS_BLOCK = 5,
PCAPNG_TYPE_ENHANCED_PACKET_BLOCK = 6,
PCAPNG_TYPE_SECTION_HEADER_BLOCK = 0x0a0d0d0a
} pcapng_type_t;
typedef struct pcapng_hdr_packet_s
{
uint16_t drops_count;
uint32_t interface_id;
uint32_t captured_len;
uint32_t packet_len;
uint64_t timestamp;
} pcapng_hdr_packet_t;
typedef struct pcapng_hdr_section_s
{
uint16_t major_version;
uint16_t minor_version;
uint64_t section_length;
} pcapng_hdr_section_t;
typedef struct pcapng_header_s
{
pcapng_type_t type;
uint8_t *data;
uint8_t *options;
union
{
pcapng_hdr_packet_t packet;
pcapng_hdr_interface_t iface;
pcapng_hdr_section_t section;
} hdr;
} pcapng_header_t;
// Kill header contents
static void free_block(pcapng_header_t *const hdr)
{
hdr->type = PCAPNG_TYPE_INVALID_BLOCK;
if (hdr->data != NULL)
{
free(hdr->data);
hdr->data = NULL;
}
if (hdr->options != NULL)
{
free(hdr->options);
hdr->options = NULL;
}
}
static int do_section_header(struct _pcap_io_ctx *const ctx, uint32_t length, const uint8_t *const buf,
pcapng_header_t *const hdr)
{
uint32_t magic;
int rv;
// PCAP-NG
ctx->is_ng = 1;
magic = uint_32_ctx(ctx, buf + 0);
printf("Magic = %08x, Len = %#x\n", magic, length);
if (magic == 0x1a2b3c4d)
{
// Right way up
}
else if (magic == 0x4d3c2b1a)
{
// Wrong way up
ctx->is_be = !ctx->is_be;
// Endian reverse length
length = (length >> 16) | (length << 16);
length = ((length >> 8) & 0xff00ff) | ((length << 8) & 0xff00ff00);
}
else
{
return PCAP_ERR_INVALID_MAGIC;
}
hdr->magic_number = (ctx->is_be ? uint_32_be(&hdr_val[0]) :
uint_32_le(&hdr_val[0]));
if (hdr->magic_number != 0xa1b2c3d4)
#if SIZEOF_PCAP_HDR_ON_DISC != 24
#error I am confused
#endif
// Length here includes headers
if (length < 28)
{
return PCAP_ERR_INVALID_MAGIC;
return PCAP_ERR_BAD_LENGTH;
}
hdr->version_major = (ctx->is_be ? uint_16_be(&hdr_val[4]) :
uint_16_le(&hdr_val[4]));
hdr->version_minor = (ctx->is_be ? uint_16_be(&hdr_val[6]) :
uint_16_le(&hdr_val[6]));
hdr->thiszone = (int32_t)(ctx->is_be ? uint_32_be(&hdr_val[8]) :
uint_32_le(&hdr_val[8]));
hdr->sigfigs = (ctx->is_be ? uint_32_be(&hdr_val[12]) :
uint_32_le(&hdr_val[12]));
hdr->snaplen = (ctx->is_be ? uint_32_be(&hdr_val[16]) :
uint_32_le(&hdr_val[16]));
hdr->network = (ctx->is_be ? uint_32_be(&hdr_val[20]) :
uint_32_le(&hdr_val[20]));
length -= 24;
hdr->hdr.section.major_version = uint_16_ctx(ctx, buf + 4);
hdr->hdr.section.minor_version = uint_16_ctx(ctx, buf + 6);
hdr->hdr.section.section_length = uint_64_ctx(ctx, buf + 8);
if ((rv = read_options(ctx->file, length, &hdr->options)) <= 0)
return rv;
return 1;
}
static int read_block(struct _pcap_io_ctx *const ctx, pcapng_header_t *const hdr)
{
int rv = 1;
int hdr_type;
uint32_t length;
hdr->type = PCAPNG_TYPE_INVALID_BLOCK;
hdr->data = NULL;
hdr->options = NULL;
if ((hdr_type = read_block_header(ctx, &length)) <= 0)
{
return hdr_type;
}
// If section header length may be endian confused so sort in a bit
if (hdr_type != PCAPNG_TYPE_SECTION_HEADER_BLOCK)
{
if (length > 0x100000 || length < 8)
{
return PCAP_ERR_BAD_LENGTH;
}
length -= 8;
}
switch (hdr_type)
{
case PCAPNG_TYPE_INTERFACE_BLOCK:
{
uint8_t buf[8];
if (length < 12)
return PCAP_ERR_BAD_LENGTH;
if (fread(buf, 8, 1, ctx->file) != 1)
return PCAP_ERR_FILE_READ;
hdr->hdr.iface.link_type = uint_16_ctx(ctx, buf + 0);
hdr->hdr.iface.snap_len = uint_32_ctx(ctx, buf + 4);
if ((rv = read_options(ctx->file, length - 8, &hdr->options)) <= 0)
return rv;
// Now stash - cos we need it later
// Alloc a new if (or at least check we have one)
if (ctx->if_count + 1 > ctx->if_size)
{
if (ctx->interfaces == NULL)
{
if ((ctx->interfaces = malloc(sizeof(*ctx->interfaces) * 4)) == NULL)
return PCAP_ERR_OUT_OF_MEMORY;
ctx->if_size = 4;
}
else
{
pcapng_hdr_interface_t *resized = realloc(ctx->interfaces,
sizeof(*ctx->interfaces) * ctx->if_size * 2);
if (resized == NULL)
return PCAP_ERR_OUT_OF_MEMORY;
ctx->if_size *= 2;
ctx->interfaces = resized;
}
}
ctx->interfaces[ctx->if_count++] = hdr->hdr.iface;
break;
}
case PCAPNG_TYPE_PACKET_BLOCK:
case PCAPNG_TYPE_ENHANCED_PACKET_BLOCK:
{
uint8_t buf[20];
size_t data_len;
if (length < 24)
return PCAP_ERR_BAD_LENGTH;
if (fread(buf, 20, 1, ctx->file) != 1)
return PCAP_ERR_FILE_READ;
if (hdr_type == PCAPNG_TYPE_PACKET_BLOCK)
{
hdr->hdr.packet.interface_id = uint_16_ctx(ctx, buf + 0);
hdr->hdr.packet.drops_count = uint_16_ctx(ctx, buf + 2);
}
else
{
hdr->hdr.packet.interface_id = uint_32_ctx(ctx, buf + 0);
hdr->hdr.packet.drops_count = 0;
}
hdr->hdr.packet.timestamp = uint_64_be_ctx(ctx, buf + 4);
hdr->hdr.packet.captured_len = uint_32_ctx(ctx, buf + 12);
hdr->hdr.packet.packet_len = uint_32_ctx(ctx, buf + 16);
if (hdr->hdr.packet.interface_id >= ctx->if_count)
return PCAP_ERR_BAD_INTERFACE_ID;
length -= 20;
data_len = (hdr->hdr.packet.captured_len + 3) & ~3;
if (length - 4 < data_len)
return PCAP_ERR_BAD_LENGTH;
if ((rv = read_chunk(ctx->file, data_len, &hdr->data)) <= 0)
break;
length -= data_len;
if ((rv = read_options(ctx->file, length, &hdr->options)) <= 0)
break;
break;
}
case PCAPNG_TYPE_SECTION_HEADER_BLOCK:
{
uint8_t buf[16];
// Clear out old data even if we error
// All interfaces are toast
if (ctx->interfaces != NULL)
{
free(ctx->interfaces);
ctx->interfaces = NULL;
ctx->if_count = 0;
ctx->if_size = 0;
}
if (fread(buf, 16, 1, ctx->file) != 1)
return PCAP_ERR_FILE_READ;
if ((rv = do_section_header(ctx, length, buf, hdr)) < 0)
return rv;
break;
}
default:
fseek(ctx->file, length, SEEK_CUR);
break;
}
if (rv <= 0)
{
free_block(hdr);
}
else
{
hdr->type = hdr_type;
}
return rv;
}
static int pcap_read_header(PCAP_reader_p ctx, pcap_hdr_t *hdr)
{
uint8_t hdr_val[SIZEOF_PCAP_HDR_ON_DISC];
int rv;
uint32_t magic;
// This reads an old-style header which is shorter than the shortest new-style one
rv = fread(&hdr_val[0], SIZEOF_PCAP_HDR_ON_DISC, 1, ctx->file);
if (rv != 1)
{
if (feof(ctx->file))
{
return 0;
}
else
{
return PCAP_ERR_FILE_READ;
}
}
magic = uint_32_be(hdr_val + 0);
if (magic == PCAPNG_TYPE_SECTION_HEADER_BLOCK)
{
pcapng_header_t nghdr = { 0 };
printf("NG header found\n");
// PCAP-NG
ctx->is_ng = 1;
if ((rv = do_section_header(ctx, uint_32_ctx(ctx, hdr_val + 4), hdr_val + 8, &nghdr)) <= 0)
return rv;
hdr->magic_number = 0x1a2b3c4d;
hdr->version_major = nghdr.hdr.section.major_version;
hdr->version_minor = nghdr.hdr.section.minor_version;
printf("Version: %d.%d\n", hdr->version_major, hdr->version_minor);
// Find the 1st i/f block (there must be one before the data)
for (;;)
{
free_block(&nghdr);
if ((rv = read_block(ctx, &nghdr)) <= 0)
{
return rv;
}
if (nghdr.type == PCAPNG_TYPE_INTERFACE_BLOCK)
{
hdr->snaplen = nghdr.hdr.iface.snap_len;
hdr->network = nghdr.hdr.iface.link_type;
free_block(&nghdr);
break;
}
}
}
else
{
ctx->is_ng = 0;
/* The magic number is 0xa1b2c3d4. If the writing
* machine was BE, the first byte will be a1 else d4
*/
if (magic == 0xa1b2c3d4)
{
// Big endian.
ctx->is_be = 1;
}
else if (magic == 0xd4c3b2a1)
{
// Little endian.
ctx->is_be = 0;
}
else
{
return PCAP_ERR_INVALID_MAGIC;
}
hdr->magic_number = 0xa1b2c3d4;
hdr->version_major = (ctx->is_be ? uint_16_be(&hdr_val[4]) :
uint_16_le(&hdr_val[4]));
hdr->version_minor = (ctx->is_be ? uint_16_be(&hdr_val[6]) :
uint_16_le(&hdr_val[6]));
hdr->thiszone = (int32_t)(ctx->is_be ? uint_32_be(&hdr_val[8]) :
uint_32_le(&hdr_val[8]));
hdr->sigfigs = (ctx->is_be ? uint_32_be(&hdr_val[12]) :
uint_32_le(&hdr_val[12]));
hdr->snaplen = (ctx->is_be ? uint_32_be(&hdr_val[16]) :
uint_32_le(&hdr_val[16]));
hdr->network = (ctx->is_be ? uint_32_be(&hdr_val[20]) :
uint_32_le(&hdr_val[20]));
}
return 1;
@ -112,20 +514,20 @@ static int pcap_read_pktheader(PCAP_reader_p ctx, pcaprec_hdr_t *hdr)
}
}
hdr->ts_sec = (ctx->is_be ? uint_32_be(&hdr_val[0]) :
uint_32_le(&hdr_val[0]));
hdr->ts_usec = (ctx->is_be ? uint_32_be(&hdr_val[4]) :
uint_32_le(&hdr_val[4]));
hdr->incl_len = (ctx->is_be ? uint_32_be(&hdr_val[8]) :
uint_32_le(&hdr_val[8]));
hdr->orig_len = (ctx->is_be ? uint_32_be(&hdr_val[12]) :
uint_32_le(&hdr_val[12]));
hdr->ts_sec = (ctx->is_be ? uint_32_be(&hdr_val[0]) :
uint_32_le(&hdr_val[0]));
hdr->ts_usec = (ctx->is_be ? uint_32_be(&hdr_val[4]) :
uint_32_le(&hdr_val[4]));
hdr->incl_len = (ctx->is_be ? uint_32_be(&hdr_val[8]) :
uint_32_le(&hdr_val[8]));
hdr->orig_len = (ctx->is_be ? uint_32_be(&hdr_val[12]) :
uint_32_le(&hdr_val[12]));
return 1;
}
extern int pcap_open(PCAP_reader_p *ctx_p, pcap_hdr_t *out_hdr,
const char *filename)
extern int pcap_open(PCAP_reader_p *ctx_p, pcap_hdr_t *out_hdr,
const char *filename)
{
FILE *fptr = (filename ? fopen(filename, "rb") : stdin);
PCAP_reader_p ctx;
@ -138,8 +540,8 @@ extern int pcap_open(PCAP_reader_p *ctx_p, pcap_hdr_t *out_hdr,
// Couldn't open the file.
return -1;
}
ctx = (PCAP_reader_p)malloc(SIZEOF_PCAP_READER);
if (!ctx)
ctx = (PCAP_reader_p)calloc(SIZEOF_PCAP_READER, 1);
if (!ctx)
{
fclose(fptr);
// Out of memory.
@ -163,54 +565,110 @@ extern int pcap_open(PCAP_reader_p *ctx_p, pcap_hdr_t *out_hdr,
return 0;
}
extern int pcap_read_next(PCAP_reader_p ctx, pcaprec_hdr_t *out_hdr,
uint8_t **out_data,
uint32_t *out_len)
extern int pcap_read_next(PCAP_reader_p ctx, pcaprec_hdr_t *out_hdr,
uint8_t **out_data,
uint32_t *out_len)
{
int rv;
(*out_data) = NULL; (*out_len) = 0;
rv = pcap_read_pktheader(ctx, out_hdr);
if (rv != 1) { return rv; }
// Otherwise we now know how long our packet is ..
(*out_data) = (uint8_t *)malloc(out_hdr->incl_len);
if (!(*out_data))
if (ctx->is_ng)
{
// Out of memory.
return -3;
}
(*out_len) = out_hdr->incl_len;
rv = fread((*out_data), (*out_len), 1, ctx->file);
if (rv != 1)
{
free(*out_data); (*out_data) = NULL;
*out_len = 0;
if (feof(ctx->file))
for (;;)
{
// Ah. EOF.
return 0;
}
else
{
// Error. Curses.
return rv;
pcapng_header_t nghdr;
if ((rv = read_block(ctx, &nghdr)) <= 0)
{
return rv;
}
if (nghdr.type == PCAPNG_TYPE_PACKET_BLOCK ||
nghdr.type == PCAPNG_TYPE_ENHANCED_PACKET_BLOCK)
{
*out_data = nghdr.data;
*out_len = nghdr.hdr.packet.captured_len;
out_hdr->incl_len = nghdr.hdr.packet.captured_len;
out_hdr->orig_len = nghdr.hdr.packet.packet_len;
out_hdr->ts_sec = (uint32_t)(nghdr.hdr.packet.timestamp / 1000000);
out_hdr->ts_usec = (uint32_t)(nghdr.hdr.packet.timestamp % 1000000);
// NULL out so we don't free it here!
nghdr.data = NULL;
free_block(&nghdr);
return 1;
}
free_block(&nghdr);
}
}
else if (rv == 1)
else
{
// Gotcha.
return 1;
rv = pcap_read_pktheader(ctx, out_hdr);
if (rv != 1)
{return rv; }
// Otherwise we now know how long our packet is ..
(*out_data) = (uint8_t*)malloc(out_hdr->incl_len);
if (!(*out_data))
{
// Out of memory.
return -3;
}
(*out_len) = out_hdr->incl_len;
rv = fread((*out_data), (*out_len), 1, ctx->file);
if (rv != 1)
{
free(*out_data); (*out_data) = NULL;
*out_len = 0;
if (feof(ctx->file))
{
// Ah. EOF.
return 0;
}
else
{
// Error. Curses.
return rv;
}
}
else if (rv == 1)
{
// Gotcha.
return 1;
}
}
return 0;
}
int pcap_close(PCAP_reader_p *const pctx)
{
PCAP_reader_p ctx = *pctx;
if (ctx == NULL)
return 0;
if (ctx->interfaces != NULL)
{
free(ctx->interfaces);
}
if (ctx->file != NULL)
{
fclose(ctx->file);
}
free(ctx);
return 0;
}
// Local Variables:
// tab-width: 8
// indent-tabs-mode: nil

30
pcap.h
Wyświetl plik

@ -45,6 +45,9 @@
//! Invalid magic
#define PCAP_ERR_INVALID_MAGIC (-10)
#define PCAP_ERR_BAD_LENGTH (-11);
#define PCAP_ERR_BAD_INTERFACE_ID (-12);
/*! File header */
typedef struct pcap_hdr_s
@ -104,15 +107,30 @@ typedef struct pcaprec_hdr_s
#define SIZEOF_PCAPREC_HDR_ON_DISC (4 + 4 + 4 + 4)
/*! Used to store I/O parameters for pcap I/O */
struct _pcap_io_ctx
typedef struct pcapng_hdr_interface_s
{
/*! The FILE* for this file */
FILE *file;
uint16_t link_type;
uint32_t snap_len;
} pcapng_hdr_interface_t;
/*! Used to store I/O parameters for pcap I/O */
typedef struct _pcap_io_ctx
{
// pcap or pcapng?
int is_ng;
/*! Endianness of the file */
int is_be;
};
/*! The FILE* for this file */
FILE *file;
uint32_t if_count;
uint32_t if_size;
pcapng_hdr_interface_t * interfaces;
} PCAP_reader_t;
typedef struct _pcap_io_ctx *PCAP_reader_p;
#define SIZEOF_PCAP_READER sizeof(struct _pcap_io_ctx)
@ -137,7 +155,7 @@ int pcap_read_next(PCAP_reader_p ctx_p, pcaprec_hdr_t *out_hdr,
uint32_t *out_len);
/*! Close the pcap file */
int pcap_close(PCAP_reader_p *ctx_p);
int pcap_close(PCAP_reader_p * const ctx_p);
#endif

Wyświetl plik

@ -101,6 +101,7 @@ typedef struct pcapreport_vlan_info_s
typedef struct pcapreport_rtp_info_s
{
uint16_t last_seq;
uint32_t n;
uint32_t ssrc;
int multiple_ssrc;
@ -396,6 +397,7 @@ static int digest_times(pcapreport_ctx_t * const ctx,
const uint32_t len)
{
int rv;
unsigned int rtp_seq_delta = 0;
// Deal with RTP contents - currently held with stream but could be moved to section
// especially if we do more timestamp analysis
@ -409,8 +411,19 @@ static int digest_times(pcapreport_ctx_t * const ctx,
ri->ssrc, rtp_header->ssrc);
ri->multiple_ssrc = TRUE;
}
rtp_seq_delta = ri->n == 0 ? 0 :
(rtp_header->sequence_number - (ri->last_seq + 1)) & 0xffffU;
if (rtp_seq_delta != 0)
{
fprint_msg("!%d! @%u: RTP seq delta (%u->%u) != 1\n", st->stream_no, ctx->pkt_counter,
ri->last_seq, rtp_header->sequence_number);
}
++ri->n;
ri->ssrc = rtp_header->ssrc;
ri->last_seq = rtp_header->sequence_number;
}
@ -1768,6 +1781,8 @@ dump_out:
}
}
pcap_close(&ctx->pcreader);
if (ctx->analyse)
{
// Spit out pcap part of the report