wireshark/wiretap/erf.c
Anthony Coddington 7531318e05 ERF: Fix issues with Host ID mapping
packet-erf: Fix Host ID/Source ID showing for all extension header types.
Only show generated Host ID/Source ID when there is a Host ID extension header
or there was not one on the record.
Assumes there is only one Source ID if multiple Flow ID extension headers (unlikely)
and that it matches the one in the Host ID header. This is consistent with other tools.
Does support multiple Host ID extension headers though.

Fix dag_version tag short name. Was clashing with another tag due to typo.

ERF wiretap: Don't conflate Host ID 0 with implicit Host ID.

While the implicit Host ID defaults to 0, it is not the same thing as seeing
a packet with Host ID explicitly 0 in the extension header which means
explicitly unknown source.
Store the initial (unknown) implicit Host ID interface mapping in it's own
special mapping table entry rather than 0.

Noticed we can currently get duplicate interfaces in the unusual event of mixed
implicit and explicit Host ID packet extension headers for the same ID before
we discover that mapping.
Consistently abandon the implicit version for consistency with the dissector
linking behaviour and mark the interface as unmatched in the description. In
2 pass mode (including normal Wireshark file open) the abandoned interface ends
up with no packets. In the common cases (all Host ID or no Host ID on packet
records) this duplicate interface will not be created in the first place.

Change-Id: Ic5d0b2ce9aae973f1693a247cf240ef1324ff70a
Ping-Bug: 12303
Reviewed-on: https://code.wireshark.org/review/18704
Reviewed-by: Stephen Donnelly
Petri-Dish: Anders Broman <a.broman58@gmail.com>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
2016-11-08 12:09:39 +00:00

2000 lines
65 KiB
C

/*
* Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand.
* All rights reserved.
*
* This software and documentation has been developed by Endace Technology Ltd.
* along with the DAG PCI network capture cards. For further information please
* visit http://www.endace.com/.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of Endace Technology Ltd may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY ENDACE TECHNOLOGY LTD ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL ENDACE TECHNOLOGY LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* erf - Endace ERF (Extensible Record Format)
*
* See
*
* http://www.endace.com/support/EndaceRecordFormat.pdf
* (mirror: https://bugs.wireshark.org/bugzilla/attachment.cgi?id=4333) (bug #4484)
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <wsutil/crc32.h>
#include <wsutil/strtoi.h>
#include "wtap-int.h"
#include "file_wrappers.h"
#include "pcap-encap.h"
#include "pcapng.h"
#include "erf.h"
static gboolean erf_read_header(wtap *wth, FILE_T fh,
struct wtap_pkthdr *phdr,
erf_header_t *erf_header,
int *err,
gchar **err_info,
guint32 *bytes_read,
guint32 *packet_size);
static gboolean erf_read(wtap *wth, int *err, gchar **err_info,
gint64 *data_offset);
static gboolean erf_seek_read(wtap *wth, gint64 seek_off,
struct wtap_pkthdr *phdr, Buffer *buf,
int *err, gchar **err_info);
static void erf_close(wtap *wth);
static int populate_summary_info(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, guint32 packet_size);
static const struct {
int erf_encap_value;
int wtap_encap_value;
} erf_to_wtap_map[] = {
{ ERF_TYPE_HDLC_POS, WTAP_ENCAP_CHDLC },
{ ERF_TYPE_HDLC_POS, WTAP_ENCAP_HHDLC },
{ ERF_TYPE_HDLC_POS, WTAP_ENCAP_CHDLC_WITH_PHDR },
{ ERF_TYPE_HDLC_POS, WTAP_ENCAP_PPP },
{ ERF_TYPE_HDLC_POS, WTAP_ENCAP_FRELAY },
{ ERF_TYPE_HDLC_POS, WTAP_ENCAP_MTP2 },
{ ERF_TYPE_ETH, WTAP_ENCAP_ETHERNET },
{ 99, WTAP_ENCAP_ERF }, /*this type added so WTAP_ENCAP_ERF will work and then be treated at ERF->ERF*/
};
#define NUM_ERF_ENCAPS (sizeof erf_to_wtap_map / sizeof erf_to_wtap_map[0])
#define ERF_META_TAG_HEADERLEN 4
#define ERF_META_TAG_ALIGNED_LENGTH(taglength) ((((guint32)taglength + 0x3U) & ~0x3U) + ERF_META_TAG_HEADERLEN)
struct erf_if_info {
int if_index;
gchar *name;
gchar *descr;
int stream_num;
struct {
guint filter:1;
guint fcs_len:1;
guint snaplen:1;
} set_flags;
};
struct erf_if_mapping {
guint64 host_id;
guint8 source_id;
struct erf_if_info interfaces[4];
gchar *module_filter_str;
gint8 module_fcs_len;
guint32 module_snaplen;
int interface_metadata;
gboolean module_metadata;
};
struct erf_meta_tag {
guint16 type;
guint16 length;
guint8 *value;
};
struct erf_meta_read_state {
guint8 *tag_ptr;
guint32 remaining_len;
struct erf_if_mapping *if_map;
guint16 sectiontype;
guint16 sectionid;
guint16 parentsectiontype;
guint16 parentsectionid;
int interface_metadata;
};
static gboolean erf_if_mapping_equal(gconstpointer a, gconstpointer b)
{
const struct erf_if_mapping *if_map_a = (const struct erf_if_mapping*) a;
const struct erf_if_mapping *if_map_b = (const struct erf_if_mapping*) b;
return if_map_a->source_id == if_map_b->source_id && if_map_a->host_id == if_map_b->host_id;
}
static guint erf_if_mapping_hash(gconstpointer key)
{
const struct erf_if_mapping *if_map = (const struct erf_if_mapping*) key;
return (((guint) if_map->host_id) << 16) | if_map->source_id;
}
static void erf_if_mapping_destroy(gpointer key)
{
int i = 0;
struct erf_if_mapping *if_map = (struct erf_if_mapping*) key;
for (i = 0; i < 4; i++) {
g_free(if_map->interfaces[i].name);
g_free(if_map->interfaces[i].descr);
}
g_free(if_map->module_filter_str);
g_free(if_map);
}
static struct erf_if_mapping* erf_if_mapping_create(guint64 host_id, guint8 source_id)
{
int i = 0;
struct erf_if_mapping *if_map = NULL;
if_map = (struct erf_if_mapping*) g_malloc(sizeof(struct erf_if_mapping));
memset(if_map, 0, sizeof(struct erf_if_mapping));
if_map->host_id = host_id;
if_map->source_id = source_id;
for (i = 0; i < 4; i++) {
if_map->interfaces[i].if_index = -1;
if_map->interfaces[i].stream_num = -1;
}
if_map->module_fcs_len = -1;
if_map->module_snaplen = (guint32) -1;
/* everything else 0 by memset */
return if_map;
}
erf_t *erf_priv_create(void)
{
erf_t *erf_priv;
erf_priv = (erf_t*) g_malloc(sizeof(erf_t));
erf_priv->if_map = g_hash_table_new_full(erf_if_mapping_hash, erf_if_mapping_equal, erf_if_mapping_destroy, NULL);
erf_priv->implicit_host_id = ERF_META_HOST_ID_IMPLICIT;
erf_priv->capture_metadata = FALSE;
erf_priv->host_metadata = FALSE;
return erf_priv;
}
erf_t* erf_priv_free(erf_t* erf_priv)
{
if (erf_priv)
{
g_hash_table_destroy(erf_priv->if_map);
g_free(erf_priv);
}
return NULL;
}
static void
erf_free_data(gpointer data, gpointer user_data _U_)
{
g_free(data);
}
extern wtap_open_return_val erf_open(wtap *wth, int *err, gchar **err_info)
{
int i, n, records_for_erf_check = RECORDS_FOR_ERF_CHECK;
int valid_prev = 0;
char *s;
erf_timestamp_t prevts,ts;
erf_header_t header;
guint32 mc_hdr;
struct erf_eth_hdr eth_hdr;
guint32 packet_size;
guint16 rlen;
guint64 erf_ext_header;
guint8 type;
memset(&prevts, 0, sizeof(prevts));
/* number of records to scan before deciding if this really is ERF */
if ((s = getenv("ERF_RECORDS_TO_CHECK")) != NULL) {
if (ws_strtoi32(s, NULL, &n) && n >= 0 && n < 101) {
records_for_erf_check = n;
}
}
/*
* ERF is a little hard because there's no magic number; we look at
* the first few records and see if they look enough like ERF
* records.
*/
for (i = 0; i < records_for_erf_check; i++) { /* records_for_erf_check */
if (!wtap_read_bytes_or_eof(wth->fh,&header,sizeof(header),err,err_info)) {
if (*err == 0) {
/* EOF - all records have been successfully checked, accept the file */
break;
}
if (*err == WTAP_ERR_SHORT_READ) {
/* ERF header too short accept the file,
only if the very first records have been successfully checked */
if (i < MIN_RECORDS_FOR_ERF_CHECK) {
return WTAP_OPEN_NOT_MINE;
} else {
/* BREAK, the last record is too short, and will be ignored */
break;
}
} else {
return WTAP_OPEN_ERROR;
}
}
rlen=g_ntohs(header.rlen);
/* fail on invalid record type, invalid rlen, timestamps decreasing, or incrementing too far */
/* Test valid rlen >= 16 */
if (rlen < 16) {
return WTAP_OPEN_NOT_MINE;
}
packet_size = rlen - (guint32)sizeof(header);
if (packet_size > WTAP_MAX_PACKET_SIZE) {
/*
* Probably a corrupt capture file or a file that's not an ERF file
* but that passed earlier tests.
*/
return WTAP_OPEN_NOT_MINE;
}
/* Skip PAD records, timestamps may not be set */
if ((header.type & 0x7F) == ERF_TYPE_PAD) {
if (!wtap_read_bytes(wth->fh, NULL, packet_size, err, err_info)) {
if (*err != WTAP_ERR_SHORT_READ) {
/* A real error */
return WTAP_OPEN_ERROR;
}
/* ERF record too short, accept the file,
only if the very first records have been successfully checked */
if (i < MIN_RECORDS_FOR_ERF_CHECK) {
return WTAP_OPEN_NOT_MINE;
}
}
continue;
}
/* ERF Type 0 is reserved for ancient legacy records which are not supported, probably not ERF */
if ((header.type & 0x7F) == 0) {
return WTAP_OPEN_NOT_MINE;
}
/* fail on decreasing timestamps */
if ((ts = pletoh64(&header.ts)) < prevts) {
/* reassembled AALx records may not be in time order, also records are not in strict time order between physical interfaces, so allow 1 sec fudge */
if ( ((prevts-ts)>>32) > 1 ) {
return WTAP_OPEN_NOT_MINE;
}
}
/* Check to see if timestamp increment is > 1 week */
if ( (valid_prev) && (ts > prevts) && (((ts-prevts)>>32) > 3600*24*7) ) {
return WTAP_OPEN_NOT_MINE;
}
memcpy(&prevts, &ts, sizeof(prevts));
/* Read over the extension headers */
type = header.type;
while (type & 0x80){
if (!wtap_read_bytes(wth->fh,&erf_ext_header,sizeof(erf_ext_header),err,err_info)) {
if (*err == WTAP_ERR_SHORT_READ) {
/* Extension header missing, not an ERF file */
return WTAP_OPEN_NOT_MINE;
}
return WTAP_OPEN_ERROR;
}
packet_size -= (guint32)sizeof(erf_ext_header);
memcpy(&type, &erf_ext_header, sizeof(type));
}
/* Read over MC or ETH subheader */
switch(header.type & 0x7F) {
case ERF_TYPE_MC_HDLC:
case ERF_TYPE_MC_RAW:
case ERF_TYPE_MC_ATM:
case ERF_TYPE_MC_RAW_CHANNEL:
case ERF_TYPE_MC_AAL5:
case ERF_TYPE_MC_AAL2:
case ERF_TYPE_COLOR_MC_HDLC_POS:
case ERF_TYPE_AAL2: /* not an MC type but has a similar 'AAL2 ext' header */
if (!wtap_read_bytes(wth->fh,&mc_hdr,sizeof(mc_hdr),err,err_info)) {
if (*err == WTAP_ERR_SHORT_READ) {
/* Subheader missing, not an ERF file */
return WTAP_OPEN_NOT_MINE;
}
return WTAP_OPEN_ERROR;
}
packet_size -= (guint32)sizeof(mc_hdr);
break;
case ERF_TYPE_ETH:
case ERF_TYPE_COLOR_ETH:
case ERF_TYPE_DSM_COLOR_ETH:
case ERF_TYPE_COLOR_HASH_ETH:
if (!wtap_read_bytes(wth->fh,&eth_hdr,sizeof(eth_hdr),err,err_info)) {
if (*err == WTAP_ERR_SHORT_READ) {
/* Subheader missing, not an ERF file */
return WTAP_OPEN_NOT_MINE;
}
return WTAP_OPEN_ERROR;
}
packet_size -= (guint32)sizeof(eth_hdr);
break;
default:
break;
}
if (packet_size > WTAP_MAX_PACKET_SIZE) {
/*
* Probably a corrupt capture file or a file that's not an ERF file
* but that passed earlier tests.
*/
return WTAP_OPEN_NOT_MINE;
}
if (!wtap_read_bytes(wth->fh, NULL, packet_size, err, err_info)) {
if (*err != WTAP_ERR_SHORT_READ) {
/* A real error */
return WTAP_OPEN_ERROR;
}
/* ERF record too short, accept the file,
only if the very first records have been successfully checked */
if (i < MIN_RECORDS_FOR_ERF_CHECK) {
return WTAP_OPEN_NOT_MINE;
}
}
valid_prev = 1;
} /* records_for_erf_check */
if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) { /* rewind */
return WTAP_OPEN_ERROR;
}
/* This is an ERF file */
wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_ERF;
wth->snapshot_length = 0; /* not available in header, only in frame */
/*
* Use the encapsulation for ERF records.
*/
wth->file_encap = WTAP_ENCAP_ERF;
wth->subtype_read = erf_read;
wth->subtype_seek_read = erf_seek_read;
wth->subtype_close = erf_close;
wth->file_tsprec = WTAP_TSPREC_NSEC;
wth->priv = erf_priv_create();
return WTAP_OPEN_MINE;
}
/* Read the next packet */
static gboolean erf_read(wtap *wth, int *err, gchar **err_info,
gint64 *data_offset)
{
erf_header_t erf_header;
guint32 packet_size, bytes_read;
*data_offset = file_tell(wth->fh);
do {
if (!erf_read_header(wth, wth->fh,
&wth->phdr, &erf_header,
err, err_info, &bytes_read, &packet_size)) {
return FALSE;
}
if (!wtap_read_packet_bytes(wth->fh, wth->frame_buffer, packet_size,
err, err_info))
return FALSE;
/*
* If MetaERF, frame buffer could hold the meta erf tags. Only look until
* we have seen a description of every interface.
*/
if ((erf_header.type & 0x7F) == ERF_TYPE_META && packet_size > 0)
{
populate_summary_info((erf_t*) wth->priv, wth, &wth->phdr.pseudo_header, packet_size);
}
} while ( erf_header.type == ERF_TYPE_PAD );
return TRUE;
}
static gboolean erf_seek_read(wtap *wth, gint64 seek_off,
struct wtap_pkthdr *phdr, Buffer *buf,
int *err, gchar **err_info)
{
erf_header_t erf_header;
guint32 packet_size;
if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
return FALSE;
do {
if (!erf_read_header(wth, wth->random_fh, phdr, &erf_header,
err, err_info, NULL, &packet_size))
return FALSE;
} while ( erf_header.type == ERF_TYPE_PAD );
return wtap_read_packet_bytes(wth->random_fh, buf, packet_size,
err, err_info);
}
static gboolean erf_read_header(wtap *wth, FILE_T fh,
struct wtap_pkthdr *phdr,
erf_header_t *erf_header,
int *err,
gchar **err_info,
guint32 *bytes_read,
guint32 *packet_size)
{
union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
guint8 erf_exhdr[8];
guint64 erf_exhdr_sw;
guint8 type = 0;
guint32 mc_hdr;
guint32 aal2_hdr;
struct wtap_erf_eth_hdr eth_hdr;
guint32 skiplen = 0;
int i = 0;
int max = sizeof(pseudo_header->erf.ehdr_list)/sizeof(struct erf_ehdr);
guint64 host_id = ERF_META_HOST_ID_IMPLICIT;
guint8 source_id = 0;
guint8 if_num = 0;
gboolean host_id_found = FALSE;
if (!wtap_read_bytes_or_eof(fh, erf_header, sizeof(*erf_header), err, err_info)) {
return FALSE;
}
if (bytes_read != NULL) {
*bytes_read = sizeof(*erf_header);
}
*packet_size = g_ntohs(erf_header->rlen) - (guint32)sizeof(*erf_header);
if (*packet_size > WTAP_MAX_PACKET_SIZE) {
/*
* Probably a corrupt capture file; don't blow up trying
* to allocate space for an immensely-large packet.
*/
*err = WTAP_ERR_BAD_FILE;
*err_info = g_strdup_printf("erf: File has %u-byte packet, bigger than maximum of %u",
*packet_size, WTAP_MAX_PACKET_SIZE);
return FALSE;
}
if (*packet_size == 0) {
/* If this isn't a pad record, it's a corrupt packet; bail out */
if ((erf_header->type & 0x7F) != ERF_TYPE_PAD) {
*err = WTAP_ERR_BAD_FILE;
*err_info = g_strdup("erf: File has 0 byte packet");
return FALSE;
}
}
{
guint64 ts = pletoh64(&erf_header->ts);
/*if ((erf_header->type & 0x7f) != ERF_TYPE_META || wth->file_type_subtype != WTAP_FILE_TYPE_SUBTYPE_ERF) {*/
phdr->rec_type = REC_TYPE_PACKET;
/*
* XXX: ERF_TYPE_META records should ideally be FT_SPECIFIC for display
* purposes, but currently ft_specific_record_phdr clashes with erf_mc_phdr
* and the PCAP-NG dumper assumes it is a PCAP-NG block type. Ideally we
* would register a block handler with PCAP-NG and write out the closest
* PCAP-NG block, or a custom block/MetaERF record.
*
*/
#if 0
} else {
/*
* TODO: how to identify, distinguish and timestamp events?
* What to do about ENCAP_ERF in PCAP/PCAP-NG? Filetype dissector is
* chosen by wth->file_type_subtype?
*/
/* For now just treat all MetaERF records as reports */
phdr->rec_type = REC_TYPE_FT_SPECIFIC_REPORT;
/* XXX: phdr ft_specific_record_phdr? */
}
#endif
phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN|WTAP_HAS_INTERFACE_ID;
phdr->ts.secs = (long) (ts >> 32);
ts = ((ts & 0xffffffff) * 1000 * 1000 * 1000);
ts += (ts & 0x80000000) << 1; /* rounding */
phdr->ts.nsecs = ((int) (ts >> 32));
if (phdr->ts.nsecs >= 1000000000) {
phdr->ts.nsecs -= 1000000000;
phdr->ts.secs += 1;
}
if_num = erf_header->flags & 0x03;
}
/* Copy the ERF pseudo header */
memset(&pseudo_header->erf, 0, sizeof(pseudo_header->erf));
pseudo_header->erf.phdr.ts = pletoh64(&erf_header->ts);
pseudo_header->erf.phdr.type = erf_header->type;
pseudo_header->erf.phdr.flags = erf_header->flags;
pseudo_header->erf.phdr.rlen = g_ntohs(erf_header->rlen);
pseudo_header->erf.phdr.lctr = g_ntohs(erf_header->lctr);
pseudo_header->erf.phdr.wlen = g_ntohs(erf_header->wlen);
/* Copy the ERF extension header into the pseudo header */
type = erf_header->type;
while (type & 0x80){
if (!wtap_read_bytes(fh, &erf_exhdr, sizeof(erf_exhdr),
err, err_info))
return FALSE;
if (bytes_read != NULL)
*bytes_read += (guint32)sizeof(erf_exhdr);
*packet_size -= (guint32)sizeof(erf_exhdr);
skiplen += (guint32)sizeof(erf_exhdr);
erf_exhdr_sw = pntoh64(erf_exhdr);
if (i < max)
memcpy(&pseudo_header->erf.ehdr_list[i].ehdr, &erf_exhdr_sw, sizeof(erf_exhdr_sw));
type = erf_exhdr[0];
/*
* XXX: Only want first Source ID and Host ID, and want to preserve HID n SID 0 (see
* erf_populate_interface)
*/
switch (type & 0x7f) {
case ERF_EXT_HDR_TYPE_HOST_ID:
if (!host_id_found)
host_id = erf_exhdr_sw & ERF_EHDR_HOST_ID_MASK;
host_id_found = TRUE;
/* Fall through */
case ERF_EXT_HDR_TYPE_FLOW_ID:
if (!source_id)
source_id = (erf_exhdr_sw >> 48) & 0xff;
break;
}
i++;
}
/* XXX: erf_priv pointer needs to change if used as common function for other dissectors! */
phdr->interface_id = (guint) erf_populate_interface((erf_t*) wth->priv, wth, pseudo_header, host_id, source_id, if_num);
switch (erf_header->type & 0x7F) {
case ERF_TYPE_IPV4:
case ERF_TYPE_IPV6:
case ERF_TYPE_RAW_LINK:
case ERF_TYPE_INFINIBAND:
case ERF_TYPE_INFINIBAND_LINK:
case ERF_TYPE_META:
case ERF_TYPE_OPA_SNC:
case ERF_TYPE_OPA_9B:
#if 0
{
phdr->len = g_htons(erf_header->wlen);
phdr->caplen = g_htons(erf_header->wlen);
}
return TRUE;
#endif
break;
case ERF_TYPE_PAD:
case ERF_TYPE_HDLC_POS:
case ERF_TYPE_COLOR_HDLC_POS:
case ERF_TYPE_DSM_COLOR_HDLC_POS:
case ERF_TYPE_COLOR_HASH_POS:
case ERF_TYPE_ATM:
case ERF_TYPE_AAL5:
break;
case ERF_TYPE_ETH:
case ERF_TYPE_COLOR_ETH:
case ERF_TYPE_DSM_COLOR_ETH:
case ERF_TYPE_COLOR_HASH_ETH:
if (!wtap_read_bytes(fh, &eth_hdr, sizeof(eth_hdr), err, err_info))
return FALSE;
if (bytes_read != NULL)
*bytes_read += (guint32)sizeof(eth_hdr);
*packet_size -= (guint32)sizeof(eth_hdr);
skiplen += (guint32)sizeof(eth_hdr);
pseudo_header->erf.subhdr.eth_hdr = eth_hdr;
break;
case ERF_TYPE_MC_HDLC:
case ERF_TYPE_MC_RAW:
case ERF_TYPE_MC_ATM:
case ERF_TYPE_MC_RAW_CHANNEL:
case ERF_TYPE_MC_AAL5:
case ERF_TYPE_MC_AAL2:
case ERF_TYPE_COLOR_MC_HDLC_POS:
if (!wtap_read_bytes(fh, &mc_hdr, sizeof(mc_hdr), err, err_info))
return FALSE;
if (bytes_read != NULL)
*bytes_read += (guint32)sizeof(mc_hdr);
*packet_size -= (guint32)sizeof(mc_hdr);
skiplen += (guint32)sizeof(mc_hdr);
pseudo_header->erf.subhdr.mc_hdr = g_ntohl(mc_hdr);
break;
case ERF_TYPE_AAL2:
if (!wtap_read_bytes(fh, &aal2_hdr, sizeof(aal2_hdr), err, err_info))
return FALSE;
if (bytes_read != NULL)
*bytes_read += (guint32)sizeof(aal2_hdr);
*packet_size -= (guint32)sizeof(aal2_hdr);
skiplen += (guint32)sizeof(aal2_hdr);
pseudo_header->erf.subhdr.aal2_hdr = g_ntohl(aal2_hdr);
break;
case ERF_TYPE_IP_COUNTER:
case ERF_TYPE_TCP_FLOW_COUNTER:
/* unsupported, continue with default: */
default:
/* let the dissector dissect as unknown record type for forwards compatibility */
break;
}
{
phdr->len = g_ntohs(erf_header->wlen);
phdr->caplen = MIN( g_ntohs(erf_header->wlen),
g_ntohs(erf_header->rlen) - (guint32)sizeof(*erf_header) - skiplen );
}
if (*packet_size > WTAP_MAX_PACKET_SIZE) {
/*
* Probably a corrupt capture file; don't blow up trying
* to allocate space for an immensely-large packet.
*/
*err = WTAP_ERR_BAD_FILE;
*err_info = g_strdup_printf("erf: File has %u-byte packet, bigger than maximum of %u",
*packet_size, WTAP_MAX_PACKET_SIZE);
return FALSE;
}
return TRUE;
}
static int wtap_wtap_encap_to_erf_encap(int encap)
{
unsigned int i;
for(i = 0; i < NUM_ERF_ENCAPS; i++){
if(erf_to_wtap_map[i].wtap_encap_value == encap)
return erf_to_wtap_map[i].erf_encap_value;
}
return -1;
}
static gboolean erf_write_phdr(wtap_dumper *wdh, int encap, const union wtap_pseudo_header *pseudo_header, int * err)
{
guint8 erf_hdr[sizeof(struct erf_mc_phdr)];
guint8 erf_subhdr[sizeof(union erf_subhdr)];
guint8 ehdr[8*MAX_ERF_EHDR];
size_t size = 0;
size_t subhdr_size = 0;
int i = 0;
guint8 has_more = 0;
switch(encap){
case WTAP_ENCAP_ERF:
memset(&erf_hdr, 0, sizeof(erf_hdr));
phtolell(&erf_hdr[0], pseudo_header->erf.phdr.ts);
erf_hdr[8] = pseudo_header->erf.phdr.type;
erf_hdr[9] = pseudo_header->erf.phdr.flags;
phtons(&erf_hdr[10], pseudo_header->erf.phdr.rlen);
phtons(&erf_hdr[12], pseudo_header->erf.phdr.lctr);
phtons(&erf_hdr[14], pseudo_header->erf.phdr.wlen);
size = sizeof(struct erf_phdr);
switch(pseudo_header->erf.phdr.type & 0x7F) {
case ERF_TYPE_MC_HDLC:
case ERF_TYPE_MC_RAW:
case ERF_TYPE_MC_ATM:
case ERF_TYPE_MC_RAW_CHANNEL:
case ERF_TYPE_MC_AAL5:
case ERF_TYPE_MC_AAL2:
case ERF_TYPE_COLOR_MC_HDLC_POS:
phtonl(&erf_subhdr[0], pseudo_header->erf.subhdr.mc_hdr);
subhdr_size += (int)sizeof(struct erf_mc_hdr);
break;
case ERF_TYPE_AAL2:
phtonl(&erf_subhdr[0], pseudo_header->erf.subhdr.aal2_hdr);
subhdr_size += (int)sizeof(struct erf_aal2_hdr);
break;
case ERF_TYPE_ETH:
case ERF_TYPE_COLOR_ETH:
case ERF_TYPE_DSM_COLOR_ETH:
case ERF_TYPE_COLOR_HASH_ETH:
memcpy(&erf_subhdr[0], &pseudo_header->erf.subhdr.eth_hdr, sizeof pseudo_header->erf.subhdr.eth_hdr);
subhdr_size += (int)sizeof(struct erf_eth_hdr);
break;
default:
break;
}
break;
default:
return FALSE;
}
if (!wtap_dump_file_write(wdh, erf_hdr, size, err))
return FALSE;
wdh->bytes_dumped += size;
/*write out up to MAX_ERF_EHDR extension headers*/
has_more = pseudo_header->erf.phdr.type & 0x80;
if(has_more){ /*we have extension headers*/
do{
phtonll(ehdr+(i*8), pseudo_header->erf.ehdr_list[i].ehdr);
if(i == MAX_ERF_EHDR-1) ehdr[i*8] = ehdr[i*8] & 0x7F;
has_more = ehdr[i*8] & 0x80;
i++;
}while(has_more && i < MAX_ERF_EHDR);
if (!wtap_dump_file_write(wdh, ehdr, 8*i, err))
return FALSE;
wdh->bytes_dumped += 8*i;
}
if(!wtap_dump_file_write(wdh, erf_subhdr, subhdr_size, err))
return FALSE;
wdh->bytes_dumped += subhdr_size;
return TRUE;
}
static gboolean erf_dump(
wtap_dumper *wdh,
const struct wtap_pkthdr *phdr,
const guint8 *pd,
int *err,
gchar **err_info _U_)
{
const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
union wtap_pseudo_header other_phdr;
int encap;
gint64 alignbytes = 0;
int i;
int round_down = 0;
gboolean must_add_crc = FALSE;
guint32 crc32 = 0x00000000;
/* Don't write anything bigger than we're willing to read. */
if(phdr->caplen > WTAP_MAX_PACKET_SIZE) {
*err = WTAP_ERR_PACKET_TOO_LARGE;
return FALSE;
}
if(wdh->encap == WTAP_ENCAP_PER_PACKET){
encap = phdr->pkt_encap;
}else{
encap = wdh->encap;
}
if(encap == WTAP_ENCAP_ERF){
/* We've been handed an ERF record, so there's not much to do here. */
alignbytes = wdh->bytes_dumped + pseudo_header->erf.phdr.rlen;
if(!erf_write_phdr(wdh, encap, pseudo_header, err)) return FALSE;
if(!wtap_dump_file_write(wdh, pd, phdr->caplen, err)) return FALSE;
wdh->bytes_dumped += phdr->caplen;
/*XXX: this pads the record to its original length, which is fine in most
* cases. However with >MAX_ERF_EHDR unnecessary padding will be added, and
* if the record was truncated this will be incorrectly treated as payload.
* More than 8 extension headers is unusual though, only the first 8 are
* written out anyway and fixing properly would require major refactor.*/
while(wdh->bytes_dumped < alignbytes){
if(!wtap_dump_file_write(wdh, "", 1, err)) return FALSE;
wdh->bytes_dumped++;
}
return TRUE;
}
/* We can only convert packet records. */
if (phdr->rec_type != REC_TYPE_PACKET) {
*err = WTAP_ERR_UNWRITABLE_REC_TYPE;
return FALSE;
}
/*generate a fake header in other_phdr using data that we know*/
/*covert time erf timestamp format*/
other_phdr.erf.phdr.ts = ((guint64) phdr->ts.secs << 32) + (((guint64) phdr->ts.nsecs <<32) / 1000 / 1000 / 1000);
other_phdr.erf.phdr.type = wtap_wtap_encap_to_erf_encap(encap);
other_phdr.erf.phdr.flags = 0x4; /*vlen flag set because we're creating variable length records*/
other_phdr.erf.phdr.lctr = 0;
/*now we work out rlen, accounting for all the different headers and missing fcs(eth)*/
other_phdr.erf.phdr.rlen = phdr->caplen+16;
other_phdr.erf.phdr.wlen = phdr->len;
switch(other_phdr.erf.phdr.type){
case ERF_TYPE_ETH:
other_phdr.erf.phdr.rlen += 2; /*2 bytes for erf eth_type*/
if (pseudo_header->eth.fcs_len != 4) {
/* Either this packet doesn't include the FCS
(pseudo_header->eth.fcs_len = 0), or we don't
know whether it has an FCS (= -1). We have to
synthesize an FCS.*/
if(!(phdr->caplen < phdr->len)){ /*don't add FCS if packet has been snapped off*/
crc32 = crc32_ccitt_seed(pd, phdr->caplen, 0xFFFFFFFF);
other_phdr.erf.phdr.rlen += 4; /*4 bytes for added checksum*/
other_phdr.erf.phdr.wlen += 4;
must_add_crc = TRUE;
}
}
break;
case ERF_TYPE_HDLC_POS:
/*we assume that it's missing a FCS checksum, make one up*/
if(!(phdr->caplen < phdr->len)){ /*unless of course, the packet has been snapped off*/
crc32 = crc32_ccitt_seed(pd, phdr->caplen, 0xFFFFFFFF);
other_phdr.erf.phdr.rlen += 4; /*4 bytes for added checksum*/
other_phdr.erf.phdr.wlen += 4;
must_add_crc = TRUE; /* XXX - these never have an FCS? */
}
break;
default:
break;
}
alignbytes = (8 - (other_phdr.erf.phdr.rlen % 8)) % 8; /*calculate how much padding will be required */
if(phdr->caplen < phdr->len){ /*if packet has been snapped, we need to round down what we output*/
round_down = (8 - (guint)alignbytes) % 8;
other_phdr.erf.phdr.rlen -= round_down;
}else{
other_phdr.erf.phdr.rlen += (gint16)alignbytes;
}
if(!erf_write_phdr(wdh, WTAP_ENCAP_ERF, &other_phdr, err)) return FALSE;
if(!wtap_dump_file_write(wdh, pd, phdr->caplen - round_down, err)) return FALSE;
wdh->bytes_dumped += phdr->caplen - round_down;
/*add the 4 byte CRC if necessary*/
if(must_add_crc){
if(!wtap_dump_file_write(wdh, &crc32, 4, err)) return FALSE;
wdh->bytes_dumped += 4;
}
/*records should be 8byte aligned, so we add padding*/
if(round_down == 0){
for(i = (gint16)alignbytes; i > 0; i--){
if(!wtap_dump_file_write(wdh, "", 1, err)) return FALSE;
wdh->bytes_dumped++;
}
}
return TRUE;
}
int erf_dump_can_write_encap(int encap)
{
if(encap == WTAP_ENCAP_PER_PACKET)
return 0;
if (wtap_wtap_encap_to_erf_encap(encap) == -1)
return WTAP_ERR_UNWRITABLE_ENCAP;
return 0;
}
int erf_dump_open(wtap_dumper *wdh, int *err)
{
wdh->subtype_write = erf_dump;
switch(wdh->file_type_subtype){
case WTAP_FILE_TYPE_SUBTYPE_ERF:
wdh->tsprecision = WTAP_TSPREC_NSEC;
break;
default:
*err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
return FALSE;
break;
}
return TRUE;
}
/*
* TODO: Replace uses in pcapng and pcap with
* erf_read_header() and/or erf_populate_interface_from_header() and delete.
*/
int erf_populate_interfaces(wtap *wth)
{
wtap_block_t int_data;
wtapng_if_descr_mandatory_t* int_data_mand;
int i;
if (!wth)
return -1;
/* Preemptively create interface entries for 4 interfaces, since this is the max number in ERF */
for (i=0; i<4; i++) {
int_data = wtap_block_create(WTAP_BLOCK_IF_DESCR);
int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
int_data_mand->wtap_encap = WTAP_ENCAP_ERF;
/* int_data.time_units_per_second = (1LL<<32); ERF format resolution is 2^-32, capture resolution is unknown */
int_data_mand->time_units_per_second = 1000000000; /* XXX Since Wireshark only supports down to nanosecond resolution we have to dilute to this */
int_data_mand->link_type = wtap_wtap_encap_to_pcap_encap(WTAP_ENCAP_ERF);
int_data_mand->snap_len = 65535; /* ERF max length */
/* XXX: if_IPv4addr opt 4 Interface network address and netmask.*/
/* XXX: if_IPv6addr opt 5 Interface network address and prefix length (stored in the last byte).*/
/* XXX: if_MACaddr opt 6 Interface Hardware MAC address (48 bits).*/
/* XXX: if_EUIaddr opt 7 Interface Hardware EUI address (64 bits)*/
/* XXX: if_speed opt 8 Interface speed (in bits per second)*/
/* int_data.if_tsresol = 0xa0; ERF format resolution is 2^-32 = 0xa0, capture resolution is unknown */
wtap_block_add_uint8_option(int_data, OPT_IDB_TSRESOL, 0x09); /* XXX Since Wireshark only supports down to nanosecond resolution we have to dilute to this */
/* XXX: if_tzone 10 Time zone for GMT support (TODO: specify better). */
/* XXX if_tsoffset; opt 14 A 64 bits integer value that specifies an offset (in seconds)...*/
/* Interface statistics */
int_data_mand->num_stat_entries = 0;
int_data_mand->interface_statistics = NULL;
wtap_block_add_string_option_format(int_data, OPT_IDB_NAME, "Port %c", 'A'+i);
wtap_block_add_string_option_format(int_data, OPT_IDB_DESCR, "ERF Interface Id %d (Port %c)", i, 'A'+i);
g_array_append_val(wth->interface_data, int_data);
}
return 0;
}
int erf_get_source_from_header(union wtap_pseudo_header *pseudo_header, guint64 *host_id, guint8 *source_id)
{
guint8 type;
guint8 has_more;
guint64 hdr;
int i = 0;
gboolean host_id_found = FALSE;
if (!pseudo_header || !host_id || !source_id)
return -1;
*host_id = ERF_META_HOST_ID_IMPLICIT;
*source_id = 0;
has_more = pseudo_header->erf.phdr.type & 0x80;
while (has_more && (i < MAX_ERF_EHDR)) {
hdr = pseudo_header->erf.ehdr_list[i].ehdr;
type = (guint8) (hdr >> 56);
/*
* XXX: Only want first Source ID and Host ID, and want to preserve HID n SID 0 (see
* erf_populate_interface)
*/
switch (type & 0x7f) {
case ERF_EXT_HDR_TYPE_HOST_ID:
if (!host_id_found)
*host_id = hdr & ERF_EHDR_HOST_ID_MASK;
host_id_found = TRUE;
/* Fall through */
case ERF_EXT_HDR_TYPE_FLOW_ID:
if (*source_id == 0)
*source_id = (hdr >> 48) & 0xff;
break;
}
if (host_id_found)
break;
has_more = type & 0x80;
i += 1;
}
return 0;
}
int erf_populate_interface_from_header(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header)
{
guint64 host_id;
guint8 source_id;
guint8 if_num;
if (!pseudo_header)
return -1;
if_num = pseudo_header->erf.phdr.flags & 0x03;
erf_get_source_from_header(pseudo_header, &host_id, &source_id);
return erf_populate_interface(erf_priv, wth, pseudo_header, host_id, source_id, if_num);
}
static struct erf_if_mapping* erf_find_interface_mapping(erf_t *erf_priv, guint64 host_id, guint8 source_id)
{
struct erf_if_mapping if_map_lookup;
if (!erf_priv)
return NULL;
if_map_lookup.host_id = host_id;
if_map_lookup.source_id = source_id;
return (struct erf_if_mapping*) g_hash_table_lookup(erf_priv->if_map, &if_map_lookup);
}
static void erf_set_interface_descr(wtap_block_t block, guint option_id, guint64 host_id, guint8 source_id, guint8 if_num, const gchar *descr)
{
/* Source XXX,*/
char sourceid_buf[16];
/* Host XXXXXXXXXXXX,*/
char hostid_buf[24];
sourceid_buf[0] = '\0';
hostid_buf[0] = '\0';
/* Implicit Host ID defaults to 0 */
if (host_id == ERF_META_HOST_ID_IMPLICIT) {
host_id = 0;
}
if (host_id > 0) {
g_snprintf(hostid_buf, sizeof(hostid_buf), " Host %012" G_GINT64_MODIFIER "x,", host_id);
}
if (source_id > 0) {
g_snprintf(sourceid_buf, sizeof(sourceid_buf), " Source %u,", source_id);
}
if (descr) {
wtap_block_set_string_option_value_format(block, option_id, "%s (ERF%s%s Interface %d)", descr, hostid_buf, sourceid_buf, if_num);
} else {
wtap_block_set_string_option_value_format(block, option_id, "Port %c (ERF%s%s Interface %d)", 'A'+if_num, hostid_buf, sourceid_buf, if_num);
}
}
static int erf_update_implicit_host_id(erf_t *erf_priv, wtap *wth, guint64 implicit_host_id)
{
GHashTableIter iter;
gpointer iter_value;
GList* implicit_list = NULL;
GList* item = NULL;
wtap_block_t int_data;
struct erf_if_mapping* if_map = NULL;
struct erf_if_mapping* if_map_other = NULL;
struct erf_if_info* if_info = NULL;
gchar *oldstr = NULL;
char portstr_buf[16];
int i;
if (!erf_priv)
return -1;
erf_priv->implicit_host_id = implicit_host_id;
/*
* We need to update the descriptions of all the interfaces with no Host
* ID to the correct Host ID.
*/
g_hash_table_iter_init(&iter, erf_priv->if_map);
/* Remove the implicit mappings from the mapping table */
while (g_hash_table_iter_next(&iter, &iter_value, NULL)) {
if_map = (struct erf_if_mapping*) iter_value;
if (if_map->host_id == ERF_META_HOST_ID_IMPLICIT) {
/* Check we don't have an existing interface that matches */
if_map_other = erf_find_interface_mapping(erf_priv, implicit_host_id, if_map->source_id);
if (!if_map_other) {
/* Pull mapping for update */
/* XXX: Can't add while iterating hash table so use list instead */
g_hash_table_iter_steal(&iter);
implicit_list = g_list_append(implicit_list, if_map);
} else {
/*
* XXX: We have duplicate interfaces in this case, but not much else we
* can do since we have already dissected the earlier packets. Expected
* to be unusual as it reqires a mix of explicit and implicit Host ID
* (e.g. FlowID extension header only) packets with the same effective
* Host ID before the first ERF_TYPE_META record.
*/
/*
* Update the description of the ERF_META_HOST_ID_IMPLICIT interface(s)
* for the first records in one pass mode. In 2 pass mode (Wireshark
* initial open, TShark in 2 pass mode) we will update the interface
* mapping for the frames on the second pass. Relatively consistent
* with the dissector behaviour.
*
* TODO: Can we delete this interface on the second (or even first)
* pass? Should we try to merge in other metadata?
* Needs a wtap_block_copy() that supports overwriting and/or expose
* custom option copy and do with wtap_block_foreach_option().
*/
for (i = 0; i < 4; i++) {
if_info = &if_map->interfaces[i];
if (if_info->if_index >= 0) {
/* XXX: this is a pointer! */
int_data = g_array_index(wth->interface_data, wtap_block_t, if_info->if_index);
g_snprintf(portstr_buf, sizeof(portstr_buf), "Port %c", 'A'+i);
oldstr = if_info->name;
if_info->name = g_strconcat(oldstr ? oldstr : portstr_buf, " [unmatched implicit]", NULL);
g_free(oldstr); /* probably null, but g_free doesn't care */
oldstr = if_info->descr;
if_info->descr = g_strconcat(oldstr ? oldstr : portstr_buf, " [unmatched implicit]", NULL);
g_free(oldstr);
erf_set_interface_descr(int_data, OPT_IDB_NAME, implicit_host_id, if_map->source_id, (guint8) i, if_info->name);
erf_set_interface_descr(int_data, OPT_IDB_DESCR, implicit_host_id, if_map->source_id, (guint8) i, if_info->descr);
}
}
}
}
}
/* Re-add the non-clashing items under the real implicit Host ID */
if (implicit_list) {
item = implicit_list;
do {
if_map = (struct erf_if_mapping*) item->data;
for (i = 0; i < 4; i++) {
if_info = &if_map->interfaces[i];
if (if_info->if_index >= 0) {
/* XXX: this is a pointer! */
int_data = g_array_index(wth->interface_data, wtap_block_t, if_info->if_index);
erf_set_interface_descr(int_data, OPT_IDB_NAME, implicit_host_id, if_map->source_id, (guint8) i, if_info->name);
erf_set_interface_descr(int_data, OPT_IDB_DESCR, implicit_host_id, if_map->source_id, (guint8) i, if_info->descr);
}
}
if_map->host_id = implicit_host_id;
/* g_hash_table_add() only exists since 2.32. */
g_hash_table_replace(erf_priv->if_map, if_map, if_map);
} while ((item = g_list_next(item)));
g_list_free(implicit_list);
}
return 0;
}
int erf_populate_interface(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, guint64 host_id, guint8 source_id, guint8 if_num)
{
wtap_block_t int_data;
wtapng_if_descr_mandatory_t* int_data_mand;
struct erf_if_mapping* if_map = NULL;
if (!wth || !pseudo_header || !erf_priv || if_num > 3)
return -1;
if (host_id == ERF_META_HOST_ID_IMPLICIT) {
/* Defaults to ERF_META_HOST_ID_IMPLICIT so we can update mapping later */
host_id = erf_priv->implicit_host_id;
} else if ((pseudo_header->erf.phdr.type & 0x7f) == ERF_TYPE_META) {
/*
* XXX: We assume there is only one Implicit Host ID. As a special case a first
* Host ID extension header with Source ID 0 on a record does not change
* the implicit Host ID. We respect this even though we support only one
* Implicit Host ID.
*/
if (erf_priv->implicit_host_id == ERF_META_HOST_ID_IMPLICIT && source_id > 0) {
erf_update_implicit_host_id(erf_priv, wth, host_id);
}
}
if_map = erf_find_interface_mapping(erf_priv, host_id, source_id);
if (!if_map) {
if_map = erf_if_mapping_create(host_id, source_id);
/* g_hash_table_add() only exists since 2.32. */
g_hash_table_replace(erf_priv->if_map, if_map, if_map);
}
/* Return the existing interface if we have it */
if (if_map->interfaces[if_num].if_index >= 0) {
return if_map->interfaces[if_num].if_index;
}
int_data = wtap_block_create(WTAP_BLOCK_IF_DESCR);
int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
int_data_mand->wtap_encap = WTAP_ENCAP_ERF;
/* int_data.time_units_per_second = (1LL<<32); ERF format resolution is 2^-32, capture resolution is unknown */
int_data_mand->time_units_per_second = 1000000000; /* XXX Since Wireshark only supports down to nanosecond resolution we have to dilute to this */
int_data_mand->link_type = wtap_wtap_encap_to_pcap_encap(WTAP_ENCAP_ERF);
int_data_mand->snap_len = 65535; /* ERF max length */
/* XXX: if_IPv4addr opt 4 Interface network address and netmask.*/
/* XXX: if_IPv6addr opt 5 Interface network address and prefix length (stored in the last byte).*/
/* XXX: if_MACaddr opt 6 Interface Hardware MAC address (48 bits).*/
/* XXX: if_EUIaddr opt 7 Interface Hardware EUI address (64 bits)*/
/* XXX: if_speed opt 8 Interface speed (in bits per second)*/
/* int_data.if_tsresol = 0xa0; ERF format resolution is 2^-32 = 0xa0, capture resolution is unknown */
wtap_block_add_uint8_option(int_data, OPT_IDB_TSRESOL, 0x09); /* XXX Since Wireshark only supports down to nanosecond resolution we have to dilute to this */
/* XXX: if_tzone 10 Time zone for GMT support (TODO: specify better). */
/* XXX if_tsoffset; opt 14 A 64 bits integer value that specifies an offset (in seconds)...*/
/* Interface statistics */
int_data_mand->num_stat_entries = 0;
int_data_mand->interface_statistics = NULL;
erf_set_interface_descr(int_data, OPT_IDB_NAME, host_id, source_id, if_num, NULL);
erf_set_interface_descr(int_data, OPT_IDB_DESCR, host_id, source_id, if_num, NULL);
if_map->interfaces[if_num].if_index = (int) wth->interface_data->len;
g_array_append_val(wth->interface_data, int_data);
return if_map->interfaces[if_num].if_index;
}
static guint32 erf_meta_read_tag(struct erf_meta_tag* tag, guint8 *tag_ptr, guint32 remaining_len)
{
guint16 tagtype;
guint16 taglength;
guint32 tagtotallength;
if (!tag_ptr || !tag || remaining_len < ERF_META_TAG_HEADERLEN)
return 0;
/* tagtype (2 bytes) */
tagtype = pntoh16(&tag_ptr[0]);
/* length (2 bytes) */
taglength = pntoh16(&tag_ptr[2]);
tagtotallength = ERF_META_TAG_ALIGNED_LENGTH(taglength);
if (remaining_len < tagtotallength) {
return 0;
}
tag->type = tagtype;
tag->length = taglength;
tag->value = &tag_ptr[4];
return tagtotallength;
}
static int populate_capture_host_info(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header _U_, struct erf_meta_read_state *state)
{
struct erf_meta_tag tag = {0, 0, NULL};
wtap_block_t shb_hdr;
char* tmp;
gchar* app_name = NULL;
gchar* app_version = NULL;
gchar* model = NULL;
gchar* descr = NULL;
gchar* cpu = NULL;
gchar* modelcpu = NULL;
guint32 tagtotallength;
if (!wth || !state || !wth->shb_hdrs || (wth->shb_hdrs->len == 0))
return -1;
/* XXX: wth->shb_hdr is already created by different layer, using directly for now. */
/* XXX: Only one section header is supported at this time */
shb_hdr = g_array_index(wth->shb_hdrs, wtap_block_t, 0);
while ((tagtotallength = erf_meta_read_tag(&tag, state->tag_ptr, state->remaining_len)) && !ERF_META_IS_SECTION(tag.type)) {
switch (state->sectiontype) {
case ERF_META_SECTION_CAPTURE:
{
if (erf_priv->capture_metadata == TRUE) {
return 0;
}
switch (tag.type) {
case ERF_META_TAG_comment:
wtap_block_add_string_option(shb_hdr, OPT_COMMENT, tag.value, tag.length);
break;
}
/* Fall through */
}
case ERF_META_SECTION_HOST:
{
if (erf_priv->host_metadata == TRUE) {
return 0;
}
switch (tag.type) {
case ERF_META_TAG_model:
g_free(model);
model = g_strndup((gchar*) tag.value, tag.length);
break;
case ERF_META_TAG_cpu:
g_free(cpu);
cpu = g_strndup((gchar*) tag.value, tag.length);
break;
case ERF_META_TAG_descr:
g_free(descr);
descr = g_strndup((gchar*) tag.value, tag.length);
break;
case ERF_META_TAG_os:
wtap_block_set_string_option_value(shb_hdr, OPT_SHB_OS, tag.value, tag.length);
break;
case ERF_META_TAG_app_name:
g_free(app_name);
app_name = g_strndup((gchar*) tag.value, tag.length);
break;
case ERF_META_TAG_app_version:
g_free(app_version);
app_version = g_strndup((gchar*) tag.value, tag.length);
break;
/* TODO: dag_version? */
/* TODO: could concatenate comment(s)? */
default:
break;
}
}
break;
}
state->tag_ptr += tagtotallength;
state->remaining_len -= tagtotallength;
}
/* Post processing */
if (app_name) {
/* If no app_version will just use app_name */
tmp = g_strjoin(" ", app_name, app_version, NULL);
wtap_block_set_string_option_value(shb_hdr, OPT_SHB_USERAPPL, tmp, strlen(tmp));
g_free(tmp);
g_free(app_name);
g_free(app_version);
app_name = NULL;
app_version = NULL;
}
/* For the hardware field show description followed by (model; cpu) */
/* Build "Model; CPU" part */
if (model || cpu) {
/* g_strjoin() would be nice to use here if the API didn't stop on the first NULL... */
if (model && cpu) {
modelcpu = g_strconcat(model, "; ", cpu, NULL);
} else if (cpu) {
modelcpu = cpu;
/* avoid double-free */
cpu = NULL;
} else {
modelcpu = model;
/* avoid double-free */
model = NULL;
}
}
/* Combine into "Description (Model; CPU)" */
if (state->sectiontype == ERF_META_SECTION_HOST && descr) {
if (modelcpu) {
wtap_block_set_string_option_value_format(shb_hdr, OPT_SHB_HARDWARE, "%s (%s)", descr, modelcpu);
} else {
wtap_block_set_string_option_value(shb_hdr, OPT_SHB_HARDWARE, descr, strlen(descr));
/*descr = NULL;*/
}
} else if (modelcpu) {
wtap_block_set_string_option_value(shb_hdr, OPT_SHB_HARDWARE, modelcpu, strlen(modelcpu));
/*modelcpu = NULL;*/
}
/* Free the fields we didn't end up using */
g_free(modelcpu);
g_free(model);
g_free(descr);
g_free(cpu);
if (state->sectiontype == ERF_META_SECTION_CAPTURE) {
erf_priv->capture_metadata = TRUE;
} else {
erf_priv->host_metadata = TRUE;
}
return 1;
}
static int populate_module_info(erf_t *erf_priv _U_, wtap *wth, union wtap_pseudo_header *pseudo_header _U_, struct erf_meta_read_state *state)
{
struct erf_meta_tag tag = {0, 0, NULL};
guint32 tagtotallength;
if (!wth || !state)
return -1;
if (state->if_map->module_metadata == TRUE) {
return 0;
}
while ((tagtotallength = erf_meta_read_tag(&tag, state->tag_ptr, state->remaining_len)) && !ERF_META_IS_SECTION(tag.type)) {
switch (tag.type) {
case ERF_META_TAG_fcs_len:
if (tag.length >= 4) {
state->if_map->module_fcs_len = (gint8) pntoh32(tag.value);
}
break;
case ERF_META_TAG_snaplen:
/* XXX: this is generally per stream */
if (tag.length >= 4) {
state->if_map->module_snaplen = pntoh32(tag.value);
}
break;
case ERF_META_TAG_filter:
g_free(state->if_map->module_filter_str);
state->if_map->module_filter_str = g_strndup((gchar*) tag.value, tag.length);
break;
}
state->tag_ptr += tagtotallength;
state->remaining_len -= tagtotallength;
}
state->if_map->module_metadata = TRUE;
return 1;
}
static int populate_interface_info(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, struct erf_meta_read_state *state)
{
struct erf_meta_tag tag = {0, 0, NULL};
guint32 tagtotallength;
int interface_index = -1;
wtap_block_t int_data = NULL;
wtapng_if_descr_mandatory_t* int_data_mand = NULL;
wtapng_if_descr_filter_t if_filter;
guint32 if_num = 0;
struct erf_if_info* if_info = NULL;
memset(&if_filter, 0, sizeof(if_filter));
if (!wth || !state || !pseudo_header || !state->if_map)
return -1;
/* Section ID of interface is defined to match ERF interface id. */
if_num = state->sectionid - 1;
/*
* Get or create the interface (there can be multiple interfaces in
* a MetaERF record).
*/
if (if_num < 4) { /* Note: -1u > 4*/
if_info = &state->if_map->interfaces[if_num];
interface_index = if_info->if_index;
/* Check if the interface information is still uninitialized */
if (interface_index == -1) {
guint8 *tag_ptr_tmp = state->tag_ptr;
guint32 remaining_len_tmp = state->remaining_len;
/* First iterate tags, checking we aren't looking at a timing port */
/*
* XXX: we deliberately only do this logic here rather than the per-packet
* population function so that if somehow we do see packets for an
* 'invalid' port the interface will be created at that time.
*/
while ((tagtotallength = erf_meta_read_tag(&tag, tag_ptr_tmp, remaining_len_tmp)) && !ERF_META_IS_SECTION(tag.type)) {
if (tag.type == ERF_META_TAG_if_port_type) {
if (tag.length >= 4 && pntoh32(tag.value) == 2) {
/* This is a timing port, skip it from now on */
/* XXX: should we skip all non-capture ports instead? */
if_info->if_index = -2;
interface_index = -2;
}
} else if (tag.type == ERF_META_TAG_stream_num) {
if (tag.length >= 4) {
if_info->stream_num = (gint32) pntoh32(tag.value);
}
}
tag_ptr_tmp += tagtotallength;
remaining_len_tmp -= tagtotallength;
}
/* If the interface is valid but uninitialized, create it */
if (interface_index == -1) {
interface_index = erf_populate_interface(erf_priv, wth, pseudo_header, state->if_map->host_id, state->if_map->source_id, (guint8) if_num);
}
}
/* Get the wiretap interface metadata */
if (interface_index >= 0) {
int_data = g_array_index(wth->interface_data, wtap_block_t, interface_index);
int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
} else if (interface_index == -2) {
/* timing/unknown port */
return 0;
} else {
return -1;
}
}
/*
* Bail if already have interface metadata or no interface to associate with.
* We also don't support metadata for >4 interfaces per Host + Source
* as we only use interface ID.
*/
if (!int_data || state->if_map->interface_metadata & (1 << if_num))
return 0;
while ((tagtotallength = erf_meta_read_tag(&tag, state->tag_ptr, state->remaining_len)) && !ERF_META_IS_SECTION(tag.type)) {
switch (tag.type) {
case ERF_META_TAG_name:
/* TODO: fall back to module "dev_name Port N"? */
if (!if_info->name) {
if_info->name = g_strndup((gchar*) tag.value, tag.length);
erf_set_interface_descr(int_data, OPT_IDB_NAME, state->if_map->host_id, state->if_map->source_id, (guint8) if_num, if_info->name);
/* If we have no description, also copy to wtap if_description */
if (!if_info->descr) {
erf_set_interface_descr(int_data, OPT_IDB_DESCR, state->if_map->host_id, state->if_map->source_id, (guint8) if_num, if_info->name);
}
}
break;
case ERF_META_TAG_descr:
if (!if_info->descr) {
if_info->descr = g_strndup((gchar*) tag.value, tag.length);
erf_set_interface_descr(int_data, OPT_IDB_DESCR, state->if_map->host_id, state->if_map->source_id, (guint8) if_num, if_info->descr);
/* If we have no name, also copy to wtap if_name */
if (!if_info->name) {
erf_set_interface_descr(int_data, OPT_IDB_NAME, state->if_map->host_id, state->if_map->source_id, (guint8) if_num, if_info->descr);
}
}
break;
case ERF_META_TAG_if_speed:
if (tag.length >= 8)
wtap_block_add_uint64_option(int_data, OPT_IDB_SPEED, pntoh64(tag.value));
break;
case ERF_META_TAG_if_num:
/*
* XXX: We ignore this as Section ID must match the ERF ifid and
* that is all we care about/have space for at the moment. if_num
* is only really useful with >4 interfaces.
*/
/* TODO: might want to put this number in description */
break;
case ERF_META_TAG_fcs_len:
if (tag.length >= 4) {
wtap_block_add_uint8_option(int_data, OPT_IDB_FCSLEN, (guint8) pntoh32(tag.value));
if_info->set_flags.fcs_len = 1;
}
break;
case ERF_META_TAG_snaplen:
/* XXX: this generally per stream */
if (tag.length >= 4) {
int_data_mand->snap_len = pntoh32(tag.value);
if_info->set_flags.snaplen = 1;
}
break;
case ERF_META_TAG_comment:
wtap_block_add_string_option(int_data, OPT_COMMENT, tag.value, tag.length);
break;
case ERF_META_TAG_filter:
if_filter.if_filter_str = g_strndup((gchar*) tag.value, tag.length);
wtap_block_add_custom_option(int_data, OPT_IDB_FILTER, &if_filter, sizeof if_filter);
g_free(if_filter.if_filter_str);
if_info->set_flags.filter = 1;
break;
default:
break;
}
state->tag_ptr += tagtotallength;
state->remaining_len -= tagtotallength;
}
/* Post processing */
/*
* XXX: Assumes module defined first. It is higher in hierarchy so only set
* if not already.
*/
/*
* XXX: Missing exposed existence/type-check. No way currently to check if
* been set in the optionblock.
*/
if (state->if_map->module_filter_str && !if_info->set_flags.filter) {
/* Duplicate because might use with multiple interfaces */
if_filter.if_filter_str = state->if_map->module_filter_str;
wtap_block_add_custom_option(int_data, OPT_IDB_FILTER, &if_filter, sizeof if_filter);
/*
* Don't set flag because stream is more specific than module. Interface
* metadata bit is set so we don't look at the filter again regardless.
*/
}
if (state->if_map->module_fcs_len != -1 && !if_info->set_flags.fcs_len) {
wtap_block_add_uint8_option(int_data, OPT_IDB_FCSLEN, (guint8) state->if_map->module_fcs_len);
if_info->set_flags.fcs_len = 1;
}
if (state->if_map->module_snaplen != (guint32) -1 && !if_info->set_flags.snaplen) {
int_data_mand->snap_len = pntoh32(tag.value);
if_info->set_flags.snaplen = 1;
}
state->interface_metadata |= 1 << if_num;
return 1;
}
static int populate_stream_info(erf_t *erf_priv _U_, wtap *wth, union wtap_pseudo_header *pseudo_header, struct erf_meta_read_state *state)
{
struct erf_meta_tag tag = {0, 0, NULL};
guint32 tagtotallength;
int interface_index = -1;
wtap_block_t int_data = NULL;
wtapng_if_descr_mandatory_t* int_data_mand = NULL;
wtapng_if_descr_filter_t if_filter;
guint32 if_num = 0;
gint32 stream_num = -1;
guint8 *tag_ptr_tmp;
guint32 remaining_len_tmp;
struct erf_if_info* if_info = NULL;
memset(&if_filter, 0, sizeof(if_filter));
if (!wth || !pseudo_header || !state || !state->if_map)
return -1;
tag_ptr_tmp = state->tag_ptr;
remaining_len_tmp = state->remaining_len;
/*
* XXX: We ignore parent section ID because it doesn't represent the
* many-to-many relationship of interfaces and streams very well. The stream is
* associated with all interfaces in the record that don't have a stream_num
* that says otherwise.
*/
if (state->sectionid > 0 && state->sectionid != 0x7fff) {
/* Section ID of stream is supposed to match stream_num. */
stream_num = state->sectionid - 1;
} else {
/* First iterate tags, looking for the stream number interfaces might associate with. */
while ((tagtotallength = erf_meta_read_tag(&tag, tag_ptr_tmp, remaining_len_tmp)) && !ERF_META_IS_SECTION(tag.type)) {
if (tag.type == ERF_META_TAG_stream_num) {
if (tag.length >= 4) {
stream_num = (gint32) pntoh32(tag.value);
}
}
tag_ptr_tmp += tagtotallength;
remaining_len_tmp -= tagtotallength;
}
}
/* Otherwise assume the stream applies to all interfaces in the record */
for (if_num = 0; if_num < 4; if_num++) {
tag_ptr_tmp = state->tag_ptr;
remaining_len_tmp = state->remaining_len;
if_info = &state->if_map->interfaces[if_num];
/* Check if we should be handling this interface */
/* XXX: currently skips interfaces that are not in the record. */
if (state->if_map->interface_metadata & (1 << if_num)
|| !(state->interface_metadata & (1 << if_num))) {
continue;
}
if (if_info->stream_num != -1
&& if_info->stream_num != stream_num) {
continue;
}
interface_index = if_info->if_index;
/* Get the wiretap interface metadata */
if (interface_index >= 0) {
int_data = g_array_index(wth->interface_data, wtap_block_t, interface_index);
int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
}
if (!int_data) {
continue;
}
while ((tagtotallength = erf_meta_read_tag(&tag, tag_ptr_tmp, remaining_len_tmp)) && !ERF_META_IS_SECTION(tag.type)) {
switch (tag.type) {
case ERF_META_TAG_fcs_len:
if (tag.length >= 4) {
/* Use the largest fcslen of matching streams */
gint8 fcs_len = (gint8) pntoh32(tag.value);
guint8 old_fcs_len = 0;
switch (wtap_block_get_uint8_option_value(int_data, OPT_IDB_FCSLEN, &old_fcs_len)) {
case WTAP_OPTTYPE_SUCCESS:
/* We already have an FCS length option; update it. */
if (fcs_len > old_fcs_len || !if_info->set_flags.fcs_len) {
wtap_block_set_uint8_option_value(int_data, OPT_IDB_FCSLEN, (guint8) pntoh32(tag.value));
if_info->set_flags.fcs_len = 1;
}
break;
case WTAP_OPTTYPE_NOT_FOUND:
/* We don't have an FCS length option; add it. */
wtap_block_add_uint8_option(int_data, OPT_IDB_FCSLEN, (guint8) pntoh32(tag.value));
if_info->set_flags.fcs_len = 1;
break;
default:
/* "shouldn't happen" */
break;
}
}
break;
case ERF_META_TAG_snaplen:
if (tag.length >= 4) {
/* Use the largest snaplen of matching streams */
guint32 snaplen = pntoh32(tag.value);
if (snaplen > int_data_mand->snap_len || !if_info->set_flags.snaplen) {
int_data_mand->snap_len = pntoh32(tag.value);
if_info->set_flags.snaplen = 1;
}
}
break;
case ERF_META_TAG_filter:
/* Override only if not set */
if (!if_info->set_flags.filter) {
if_filter.if_filter_str = g_strndup((gchar*) tag.value, tag.length);
wtap_block_add_custom_option(int_data, OPT_IDB_FILTER, &if_filter, sizeof if_filter);
g_free(if_filter.if_filter_str);
if_info->set_flags.filter = 1;
}
break;
default:
break;
}
tag_ptr_tmp += tagtotallength;
remaining_len_tmp -= tagtotallength;
}
}
state->tag_ptr = tag_ptr_tmp;
state->remaining_len = remaining_len_tmp;
return 1;
}
/* Populates the capture and interface information for display on the Capture File Properties */
static int populate_summary_info(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, guint32 packet_size)
{
struct erf_meta_read_state state;
struct erf_meta_read_state *state_post = NULL;
guint64 host_id;
guint8 source_id;
GList *post_list = NULL;
GList *item = NULL;
struct erf_meta_tag tag = {0, 0, NULL};
guint32 tagtotallength;
if (!erf_priv || !wth || !pseudo_header)
return -1;
memset(&state, 0, sizeof(struct erf_meta_read_state));
erf_get_source_from_header(pseudo_header, &host_id, &source_id);
if (host_id == 0) {
host_id = erf_priv->implicit_host_id;
}
state.if_map = erf_find_interface_mapping(erf_priv, host_id, source_id);
if (!state.if_map) {
state.if_map = erf_if_mapping_create(host_id, source_id);
/* g_hash_table_add() only exists since 2.32. */
g_hash_table_replace(erf_priv->if_map, state.if_map, state.if_map);
}
/*
* Skip the record if we already have enough metadata (seen one section for
* each type for the source).
*/
if ((state.if_map->interface_metadata & 0x03)
&& erf_priv->host_metadata && erf_priv->capture_metadata) {
return 0;
}
state.tag_ptr = wth->frame_buffer->data;
state.remaining_len = packet_size;
/* Read until see next section tag */
while ((tagtotallength = erf_meta_read_tag(&tag, state.tag_ptr, state.remaining_len))) {
/*
* Skip until we get to the next section tag (which could be the current tag
* after an empty section or successful parsing).
*/
if (!ERF_META_IS_SECTION(tag.type)) {
/* adjust offset */
state.tag_ptr += tagtotallength;
state.remaining_len -= tagtotallength;
continue;
}
/*
* We are now looking at the next section (and would have exited the loop
* if we reached the end).
*/
/* Update parent section. Implicit grouping is by a change in section except Interface and Stream. */
if (tag.type != state.sectiontype) {
if ((tag.type == ERF_META_SECTION_STREAM && state.sectiontype == ERF_META_SECTION_INTERFACE) ||
(tag.type == ERF_META_SECTION_INTERFACE && state.sectiontype == ERF_META_SECTION_STREAM)) {
/* do nothing */
} else {
state.parentsectiontype = state.sectiontype;
state.parentsectionid = state.sectionid;
}
}
/* Update with new sectiontype */
state.sectiontype = tag.type;
if (tag.length >= 4) {
state.sectionid = pntoh16(tag.value);
} else {
state.sectionid = 0;
}
/* Adjust offset to that of first tag in section */
state.tag_ptr += tagtotallength;
state.remaining_len -= tagtotallength;
if ((tagtotallength = erf_meta_read_tag(&tag, state.tag_ptr, state.remaining_len))) {
/*
* Process parent section tag if present (which must be the first tag in
* the section).
*/
if (tag.type == ERF_META_TAG_parent_section && tag.length >= 4) {
state.parentsectiontype = pntoh16(tag.value);
state.parentsectionid = pntoh16(&tag.value[2]);
}
}
/* Skip empty sections (includes if above read fails) */
if (ERF_META_IS_SECTION(tag.type)) {
continue;
}
/*
* Skip sections that don't apply to the general set of records
* (extension point for per-packet/event metadata).
*/
if (state.sectionid & 0x8000) {
continue;
}
/*
* Start at first tag in section, makes loop
* simpler in called functions too. Also makes iterating after failure
* much simpler.
*/
switch (state.sectiontype) {
case ERF_META_SECTION_CAPTURE:
case ERF_META_SECTION_HOST:
/* TODO: use return code */
populate_capture_host_info(erf_priv, wth, pseudo_header, &state);
break;
case ERF_META_SECTION_MODULE:
populate_module_info(erf_priv, wth, pseudo_header, &state);
break;
case ERF_META_SECTION_INTERFACE:
populate_interface_info(erf_priv, wth, pseudo_header, &state);
break;
case ERF_META_SECTION_STREAM:
/*
* XXX: Treat streams specially in case the stream information appears
* before the interface information, as we associate them to interface
* data.
*/
post_list = g_list_append(post_list, g_memdup(&state, sizeof(struct erf_meta_read_state)));
break;
case ERF_META_SECTION_SOURCE:
case ERF_META_SECTION_DNS:
default:
/* TODO: Not yet implemented */
break;
}
}
/* Process streams last */
if (post_list) {
item = post_list;
do {
state_post = (struct erf_meta_read_state*) item->data;
switch (state_post->sectiontype) {
case ERF_META_SECTION_STREAM:
populate_stream_info(erf_priv, wth, pseudo_header, state_post);
break;
}
} while ((item = g_list_next(item)));
/* g_list_free_full() only exists since 2.28. */
g_list_foreach(post_list, erf_free_data, NULL);
g_list_free(post_list);
}
/*
* Update known metadata so we only examine the first set of metadata. Need to
* do this here so can have interface and stream in same record.
*/
state.if_map->interface_metadata |= state.interface_metadata;
return 0;
}
static void erf_close(wtap *wth)
{
erf_t* erf_priv = (erf_t*)wth->priv;
erf_priv_free(erf_priv);
/* XXX: Prevent double free by wtap_close() */
wth->priv = NULL;
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local Variables:
* c-basic-offset: 2
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=2 tabstop=8 expandtab:
* :indentSize=2:tabSize=8:noTabs=true:
*/