From bc8bb0152ef254ccd75bb4a51502c6aadb492afa Mon Sep 17 00:00:00 2001 From: Guy Harris Date: Sun, 18 Apr 2021 20:15:23 -0700 Subject: [PATCH] commview: add support for newer NCFX file format. --- wiretap/commview.c | 831 +++++++++++++++++++++++++++++++++++++++--- wiretap/commview.h | 4 +- wiretap/file_access.c | 6 +- 3 files changed, 786 insertions(+), 55 deletions(-) diff --git a/wiretap/commview.c b/wiretap/commview.c index 2fc9381400..64a341cdff 100644 --- a/wiretap/commview.c +++ b/wiretap/commview.c @@ -1,5 +1,5 @@ /* commview.c - * Routines for opening CommView file format packet captures + * Routines for opening CommView NCF and NCFX file format packet captures * Copyright 2007, Stephen Fisher (see AUTHORS file) * * Wireshark - Network traffic analyzer @@ -11,8 +11,9 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -/* A brief description of this file format is available at: +/* A brief description of these file formats is available at: * https://www.tamos.com/htmlhelp/commview/logformat.htm + * https://www.tamos.com/htmlhelp/commwifi/logformat.htm * * Use * @@ -33,7 +34,15 @@ #include -typedef struct commview_header { +/* + * Capture medium types used in NCF and NCFX; + * Token Ring isn't used in NCFX. + */ +#define MEDIUM_ETHERNET 0 +#define MEDIUM_WIFI 1 +#define MEDIUM_TOKEN_RING 2 + +typedef struct commview_ncf_header { guint16 data_len; guint16 source_data_len; guint8 version; @@ -51,11 +60,11 @@ typedef struct commview_header { guint8 channel; guint8 direction; /* Or for WiFi, high order byte of * packet rate. */ - gint8 signal_level_dbm; - gint8 noise_level; /* In dBm (WiFi only) */ -} commview_header_t; + gint8 signal_level_dbm; /* WiFi-only */ + gint8 noise_level_dbm; /* WiFi-only */ +} commview_ncf_header_t; -#define COMMVIEW_HEADER_SIZE 24 +#define COMMVIEW_NCF_HEADER_SIZE 24 /* Bit-field positions for various fields in the flags variable of the header */ #define FLAGS_MEDIUM 0x0F @@ -74,30 +83,27 @@ typedef struct commview_header { #define BAND_11N_5GHZ 0x40 #define BAND_11N_2_4GHZ 0x80 -/* Capture mediums as defined by the commview file format */ -#define MEDIUM_ETHERNET 0 -#define MEDIUM_WIFI 1 -#define MEDIUM_TOKEN_RING 2 - -static gboolean commview_read(wtap *wth, wtap_rec *rec, Buffer *buf, +static gboolean commview_ncf_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info, gint64 *data_offset); -static gboolean commview_seek_read(wtap *wth, gint64 seek_off, +static gboolean commview_ncf_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info); -static gboolean commview_read_header(commview_header_t *cv_hdr, FILE_T fh, +static gboolean commview_ncf_read_header(commview_ncf_header_t *cv_hdr, FILE_T fh, int *err, gchar **err_info); -static gboolean commview_dump(wtap_dumper *wdh, const wtap_rec *rec, +static gboolean commview_ncf_dump(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd, int *err, gchar **err_info); -static int commview_file_type_subtype = -1; +static int commview_ncf_file_type_subtype = -1; +static int commview_ncfx_file_type_subtype = -1; void register_commview(void); -wtap_open_return_val commview_open(wtap *wth, int *err, gchar **err_info) +wtap_open_return_val +commview_ncf_open(wtap *wth, int *err, gchar **err_info) { - commview_header_t cv_hdr; + commview_ncf_header_t cv_hdr; - if(!commview_read_header(&cv_hdr, wth->fh, err, err_info)) { + if(!commview_ncf_read_header(&cv_hdr, wth->fh, err, err_info)) { if (*err != 0 && *err != WTAP_ERR_SHORT_READ) return WTAP_OPEN_ERROR; return WTAP_OPEN_NOT_MINE; @@ -123,10 +129,10 @@ wtap_open_return_val commview_open(wtap *wth, int *err, gchar **err_info) return WTAP_OPEN_ERROR; /* Set up the pointers to the handlers for this file type */ - wth->subtype_read = commview_read; - wth->subtype_seek_read = commview_seek_read; + wth->subtype_read = commview_ncf_read; + wth->subtype_seek_read = commview_ncf_seek_read; - wth->file_type_subtype = commview_file_type_subtype; + wth->file_type_subtype = commview_ncf_file_type_subtype; wth->file_encap = WTAP_ENCAP_PER_PACKET; wth->file_tsprec = WTAP_TSPREC_USEC; @@ -134,14 +140,14 @@ wtap_open_return_val commview_open(wtap *wth, int *err, gchar **err_info) } static int -commview_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf, +commview_ncf_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info) { - commview_header_t cv_hdr; + commview_ncf_header_t cv_hdr; struct tm tm; guint frequency; - if(!commview_read_header(&cv_hdr, fh, err, err_info)) + if(!commview_ncf_read_header(&cv_hdr, fh, err, err_info)) return FALSE; /* * The maximum value of cv_hdr.data_len is 65535, which is less @@ -255,8 +261,8 @@ commview_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf, rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_dbm = -cv_hdr.signal_level_dbm; rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_dbm = TRUE; } - if (cv_hdr.noise_level != 0) { - rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_dbm = -cv_hdr.noise_level; + if (cv_hdr.noise_level_dbm != 0) { + rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_dbm = -cv_hdr.noise_level_dbm; rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_noise_dbm = TRUE; } if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy == PHDR_802_11_PHY_UNKNOWN) { @@ -296,7 +302,7 @@ commview_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf, default : *err = WTAP_ERR_BAD_FILE; - *err_info = g_strdup_printf("commview: unsupported encap: %u", + *err_info = g_strdup_printf("commview: unsupported encap for NCF: %u", cv_hdr.flags & FLAGS_MEDIUM); return FALSE; } @@ -322,26 +328,26 @@ commview_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf, } static gboolean -commview_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, - gchar **err_info, gint64 *data_offset) +commview_ncf_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, + gchar **err_info, gint64 *data_offset) { *data_offset = file_tell(wth->fh); - return commview_read_packet(wth->fh, rec, buf, err, err_info); + return commview_ncf_read_packet(wth->fh, rec, buf, err, err_info); } static gboolean -commview_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, - Buffer *buf, int *err, gchar **err_info) +commview_ncf_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, + Buffer *buf, int *err, gchar **err_info) { if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) return FALSE; - return commview_read_packet(wth->random_fh, rec, buf, err, err_info); + return commview_ncf_read_packet(wth->random_fh, rec, buf, err, err_info); } static gboolean -commview_read_header(commview_header_t *cv_hdr, FILE_T fh, int *err, +commview_ncf_read_header(commview_ncf_header_t *cv_hdr, FILE_T fh, int *err, gchar **err_info) { if (!wtap_read_bytes_or_eof(fh, &cv_hdr->data_len, 2, err, err_info)) @@ -378,7 +384,7 @@ commview_read_header(commview_header_t *cv_hdr, FILE_T fh, int *err, return FALSE; if (!wtap_read_bytes(fh, &cv_hdr->signal_level_dbm, 1, err, err_info)) return FALSE; - if (!wtap_read_bytes(fh, &cv_hdr->noise_level, 1, err, err_info)) + if (!wtap_read_bytes(fh, &cv_hdr->noise_level_dbm, 1, err, err_info)) return FALSE; /* Convert multi-byte values from little endian to host endian format */ @@ -393,7 +399,7 @@ commview_read_header(commview_header_t *cv_hdr, FILE_T fh, int *err, /* Returns 0 if we can write out the specified encapsulation type * into a CommView format file. */ static int -commview_dump_can_write_encap(int encap) +commview_ncf_dump_can_write_encap(int encap) { switch (encap) { @@ -412,9 +418,9 @@ commview_dump_can_write_encap(int encap) /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on failure */ static gboolean -commview_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) +commview_ncf_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) { - wdh->subtype_write = commview_dump; + wdh->subtype_write = commview_ncf_dump; /* There is no file header to write out */ wdh->bytes_dumped = 0; @@ -424,11 +430,11 @@ commview_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) /* Write a record for a packet to a dump file. * Returns TRUE on success, FALSE on failure. */ -static gboolean commview_dump(wtap_dumper *wdh, - const wtap_rec *rec, - const guint8 *pd, int *err, gchar **err_info _U_) +static gboolean +commview_ncf_dump(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd, + int *err, gchar **err_info _U_) { - commview_header_t cv_hdr = {0}; + commview_ncf_header_t cv_hdr = {0}; struct tm *tm; /* We can only write packet records. */ @@ -573,7 +579,7 @@ static gboolean commview_dump(wtap_dumper *wdh, rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_dbm ? -rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_dbm : 0; - cv_hdr.noise_level = + cv_hdr.noise_level_dbm = rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_noise_dbm ? -rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_dbm : 0; @@ -622,9 +628,718 @@ static gboolean commview_dump(wtap_dumper *wdh, return FALSE; if (!wtap_dump_file_write(wdh, &cv_hdr.signal_level_dbm, 1, err)) return FALSE; - if (!wtap_dump_file_write(wdh, &cv_hdr.noise_level, 1, err)) + if (!wtap_dump_file_write(wdh, &cv_hdr.noise_level_dbm, 1, err)) return FALSE; - wdh->bytes_dumped += COMMVIEW_HEADER_SIZE; + wdh->bytes_dumped += COMMVIEW_NCF_HEADER_SIZE; + + if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err)) + return FALSE; + wdh->bytes_dumped += rec->rec_header.packet_header.caplen; + + return TRUE; +} + +typedef struct commview_ncfx_header { + guint32 data_len; + guint16 year; + guint8 month; + guint8 day; + guint8 hours; + guint8 minutes; + guint8 seconds; + guint32 usecs; + guint8 medium_type; + guint8 decryption_flag; + guint8 direction; + guint8 reserved1; + guint8 reserved2; +} commview_ncfx_header_t; + +#define COMMVIEW_NCFX_HEADER_SIZE 20 + +typedef struct commview_ncfx_rf_header { + guint16 header_len; /* includes extension headers */ + guint16 status_modulation; + guint16 frequency_band; + guint16 channel; + guint8 noise_level_dbm; /* abs(noise in dBm) */ + guint8 signal_level_dbm; /* abs(signal in dBm) */ + guint8 signal_level_percent; + guint8 reserved; + guint32 phy_rate; /* in 100Kbps units */ + guint32 extensions_present; +} commview_ncfx_rf_header_t; + +#define COMMVIEW_NCFX_RF_HEADER_SIZE 20 + +typedef struct commview_ncfx_mcs_header { + guint8 mcs_index; + guint8 n_streams; + guint8 channel_width; + guint8 guard_interval; +} commview_ncfx_mcs_header_t; + +#define COMMVIEW_NCFX_MCS_HEADER_SIZE 4 + +/* + * Bit-field positions for various fields in the status_modulation variable + * of the header. + */ +#define STATUS_MODULATION_BAD_FCS 0x01 +#define STATUS_MODULATION_HT_PHY 0x02 +#define STATUS_MODULATION_VHT_PHY 0x04 +#define STATUS_MODULATION_HE_PHY 0x08 +#define STATUS_MODULATION_HE_OFDMA 0x10 + +/* Values for the frequency_band variable of the header */ +#define BAND_5GHZ 0x40 +#define BAND_2_4GHZ 0x80 + +/* Presence bits */ +#define PRESENCE_MCS_HEADER 0x00000001 /* type 0, bit 0 */ + +static gboolean commview_ncfx_read(wtap *wth, wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info, gint64 *data_offset); +static gboolean commview_ncfx_seek_read(wtap *wth, gint64 seek_off, + wtap_rec *rec, Buffer *buf, int *err, gchar **err_info); +static gboolean commview_ncfx_read_header(commview_ncfx_header_t *cv_hdr, + FILE_T fh, int *err, gchar **err_info); +static gboolean commview_ncfx_read_rf_header(commview_ncfx_rf_header_t *cv_rf_hdr, + FILE_T fh, int *err, gchar **err_info); +static gboolean commview_ncfx_read_mcs_header(commview_ncfx_mcs_header_t *cv_mcs_hdr, + FILE_T fh, int *err, gchar **err_info); +static gboolean commview_ncfx_dump(wtap_dumper *wdh, const wtap_rec *rec, + const guint8 *pd, int *err, gchar **err_info); + +wtap_open_return_val +commview_ncfx_open(wtap *wth, int *err, gchar **err_info) +{ + commview_ncfx_header_t cv_hdr; + + if(!commview_ncfx_read_header(&cv_hdr, wth->fh, err, err_info)) { + if (*err != 0 && *err != WTAP_ERR_SHORT_READ) + return WTAP_OPEN_ERROR; + return WTAP_OPEN_NOT_MINE; + } + + /* If any of these fields do not match what we expect, bail out. */ + if(cv_hdr.year < 2000 || /* XXX - when was this format introduced? */ + cv_hdr.month < 1 || cv_hdr.month > 12 || + cv_hdr.day < 1 || cv_hdr.day > 31 || + cv_hdr.hours > 23 || + cv_hdr.minutes > 59 || + cv_hdr.seconds > 60) + return WTAP_OPEN_NOT_MINE; /* Not our kind of file */ + switch (cv_hdr.medium_type) { + + case MEDIUM_ETHERNET: + if (cv_hdr.direction != 0x00 && + cv_hdr.direction != 0x01 && + cv_hdr.direction != 0x02) + return WTAP_OPEN_NOT_MINE; /* Not our kind of file */ + break; + + case MEDIUM_WIFI: + if (cv_hdr.decryption_flag != 0x00 && + cv_hdr.decryption_flag != 0x01) + return WTAP_OPEN_NOT_MINE; /* Not our kind of file */ + if (cv_hdr.direction != 0x00) + return WTAP_OPEN_NOT_MINE; /* Not our kind of file */ + break; + + default: + return WTAP_OPEN_NOT_MINE; /* Not our kind of file */ + } + + /* No file header. Reset the fh to 0 so we can read the first packet */ + if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) + return WTAP_OPEN_ERROR; + + /* Set up the pointers to the handlers for this file type */ + wth->subtype_read = commview_ncfx_read; + wth->subtype_seek_read = commview_ncfx_seek_read; + + wth->file_type_subtype = commview_ncfx_file_type_subtype; + wth->file_encap = WTAP_ENCAP_PER_PACKET; + wth->file_tsprec = WTAP_TSPREC_USEC; + + return WTAP_OPEN_MINE; /* Our kind of file */ +} + +static int +commview_ncfx_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info) +{ + commview_ncfx_header_t cv_hdr; + guint32 length_remaining; + struct tm tm; + commview_ncfx_rf_header_t cv_rf_hdr; + guint frequency; + commview_ncfx_mcs_header_t cv_mcs_hdr; + + if (!commview_ncfx_read_header(&cv_hdr, fh, err, err_info)) + return FALSE; + + /* Amount of data remaining in the record, after the header */ + length_remaining = cv_hdr.data_len - COMMVIEW_NCFX_HEADER_SIZE; + + switch(cv_hdr.medium_type) { + + case MEDIUM_ETHERNET : + rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET; + rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1; /* Unknown */ + break; + + case MEDIUM_WIFI : + rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO; + memset(&rec->rec_header.packet_header.pseudo_header.ieee_802_11, 0, sizeof(rec->rec_header.packet_header.pseudo_header.ieee_802_11)); + rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = 0; /* No FCS */ + rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = (cv_hdr.decryption_flag == 0x01); + rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE; + + /* + * Make sure we have enough data left for the RF header. + */ + if (length_remaining < COMMVIEW_NCFX_RF_HEADER_SIZE) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup_printf("commview: RF header goes past the NCFX data length %u", + cv_hdr.data_len); + return FALSE; + } + length_remaining -= COMMVIEW_NCFX_RF_HEADER_SIZE; + + /* + * Read the RF header. + */ + if (!commview_ncfx_read_rf_header(&cv_rf_hdr, fh, err, err_info)) + return FALSE; + if (cv_rf_hdr.status_modulation & STATUS_MODULATION_HE_PHY) + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11AX; + else if (cv_rf_hdr.status_modulation & STATUS_MODULATION_VHT_PHY) + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11AC; + else if (cv_rf_hdr.status_modulation & STATUS_MODULATION_HT_PHY) + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11N; + else { + /* + * Unknown PHY, for now. + */ + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN; + } + switch (cv_rf_hdr.frequency_band) { + + case BAND_5GHZ: + frequency = ieee80211_chan_to_mhz(cv_rf_hdr.channel, FALSE); + if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy == PHDR_802_11_PHY_UNKNOWN) { + /* + * None of the modulation bits were set, so + * this is presumably the 11a OFDM PHY. + */ + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A; + } + break; + + case BAND_2_4GHZ: + frequency = ieee80211_chan_to_mhz(cv_rf_hdr.channel, TRUE); + if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy == PHDR_802_11_PHY_UNKNOWN) { + /* + * None of the modulation bits were set, so + * guess the PHY based on the data rate. + * + * cv_rf_hdr.phy_rate is in units of 100 + * Kbits/s. + */ + if (cv_rf_hdr.phy_rate == 10 /* 1 Mb/s */ || + cv_rf_hdr.phy_rate == 20 /* 2 Mb/s */ || + cv_rf_hdr.phy_rate == 55 /* 5.5 Mb/s */ || + cv_rf_hdr.phy_rate == 110 /* 11 Mb/s */ || + cv_rf_hdr.phy_rate == 220 /* 22 Mb/s */ || + cv_rf_hdr.phy_rate == 330 /* 33 Mb/s */) + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B; + else + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G; + } + break; + + default: + frequency = 0; + break; + } + if (frequency != 0) { + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_frequency = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.frequency = frequency; + } + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel = cv_rf_hdr.channel; + + /* + * cv_rf_hdr.phy_rate is in units of 100 Kbits/s. + * + * pseudo_header.ieee_802_11.data_rate is in units of 500 + * Kbits/s. + */ + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate = + cv_rf_hdr.phy_rate/5; + + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent = cv_rf_hdr.signal_level_percent; + + /* + * These is the absolute value of the signal and noise, + * in dBm. The value is the negative of that. + * + * XXX - sometimes these are 0; assume that means that no + * value is provided. + */ + if (cv_rf_hdr.signal_level_dbm != 0) { + rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_dbm = -cv_rf_hdr.signal_level_dbm; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_dbm = TRUE; + } + if (cv_rf_hdr.noise_level_dbm != 0) { + rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_dbm = -cv_rf_hdr.noise_level_dbm; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_noise_dbm = TRUE; + } + + if (cv_rf_hdr.extensions_present & PRESENCE_MCS_HEADER) { + /* + * Make sure we have enough data left for the + * MCS header. + */ + if (length_remaining < COMMVIEW_NCFX_MCS_HEADER_SIZE) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup_printf("commview: MCS header goes past the NCFX data length %u", + cv_hdr.data_len); + return FALSE; + } + length_remaining -= COMMVIEW_NCFX_MCS_HEADER_SIZE; + + /* + * Read the MCS header. + */ + if (!commview_ncfx_read_mcs_header(&cv_mcs_hdr, fh, + err, err_info)) + return FALSE; + switch (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy) { + + case PHDR_802_11_PHY_11N: + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.has_mcs_index = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.mcs_index = cv_mcs_hdr.mcs_index; + /* number of STBC streams? */ + switch (cv_mcs_hdr.channel_width) { + + case 0x00: + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_20_MHZ; + break; + + case 0x01: + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_40_MHZ; + break; + + default: + break; + } + /* Guard interval? */ + break; + + case PHDR_802_11_PHY_11AC: + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.mcs[0] = cv_mcs_hdr.mcs_index; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.mcs[1] = 0; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.mcs[2] = 0; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.mcs[3] = 0; + /* Remaining MCS indices? */ + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.nss[0] = cv_mcs_hdr.n_streams; + switch (cv_mcs_hdr.channel_width) { + + case 0x00: + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.has_bandwidth = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.bandwidth = PHDR_802_11_BANDWIDTH_20_MHZ; + break; + + case 0x01: + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.has_bandwidth = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.bandwidth = PHDR_802_11_BANDWIDTH_40_MHZ; + break; + + case 0x02: + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.has_bandwidth = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.bandwidth = PHDR_802_11_BANDWIDTH_80_MHZ; + break; + + default: + break; + } + /* Guard interval? */ + break; + + case PHDR_802_11_PHY_11AX: + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ax.has_mcs_index = TRUE; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ax.mcs = cv_mcs_hdr.mcs_index; + rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ax.nsts = cv_mcs_hdr.n_streams; + /* Bandwidth stuff? */ + /* Guard interval? */ + break; + + default: + break; + } + } + break; + + default : + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup_printf("commview: unsupported encap for NCFX: %u", + cv_hdr.medium_type); + return FALSE; + } + + tm.tm_year = cv_hdr.year - 1900; + tm.tm_mon = cv_hdr.month - 1; + tm.tm_mday = cv_hdr.day; + tm.tm_hour = cv_hdr.hours; + tm.tm_min = cv_hdr.minutes; + tm.tm_sec = cv_hdr.seconds; + tm.tm_isdst = -1; + + rec->rec_type = REC_TYPE_PACKET; + rec->presence_flags = WTAP_HAS_TS; + + if (length_remaining > WTAP_MAX_PACKET_SIZE_STANDARD) { + /* + * 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("commview: File has %u-byte packet, bigger than maximum of %u", + length_remaining, WTAP_MAX_PACKET_SIZE_STANDARD); + return FALSE; + } + + rec->rec_header.packet_header.len = length_remaining; + rec->rec_header.packet_header.caplen = length_remaining; + + rec->ts.secs = mktime(&tm); + rec->ts.nsecs = cv_hdr.usecs * 1000; + + return wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info); +} + +static gboolean +commview_ncfx_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, + gchar **err_info, gint64 *data_offset) +{ + *data_offset = file_tell(wth->fh); + + return commview_ncfx_read_packet(wth->fh, rec, buf, err, err_info); +} + +static gboolean +commview_ncfx_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, + Buffer *buf, int *err, gchar **err_info) +{ + if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) + return FALSE; + + return commview_ncfx_read_packet(wth->random_fh, rec, buf, err, err_info); +} + +static gboolean +commview_ncfx_read_header(commview_ncfx_header_t *cv_hdr, FILE_T fh, int *err, + gchar **err_info) +{ + if (!wtap_read_bytes_or_eof(fh, &cv_hdr->data_len, 4, err, err_info)) + return FALSE; + + /* Convert data length from little endian to host endian format */ + cv_hdr->data_len = GUINT32_FROM_LE(cv_hdr->data_len); + + /* It must be at least the length of the general header. */ + if (cv_hdr->data_len < COMMVIEW_NCFX_HEADER_SIZE) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup_printf("commview: NCFX data length %u < %u", + cv_hdr->data_len, + COMMVIEW_NCFX_HEADER_SIZE); + return FALSE; + } + + if (!wtap_read_bytes(fh, &cv_hdr->year, 2, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->month, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->day, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->hours, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->minutes, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->seconds, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->usecs, 4, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->medium_type, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->decryption_flag, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->direction, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->reserved1, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_hdr->reserved2, 1, err, err_info)) + return FALSE; + + /* Convert multi-byte values from little endian to host endian format */ + cv_hdr->year = GUINT16_FROM_LE(cv_hdr->year); + cv_hdr->usecs = GUINT32_FROM_LE(cv_hdr->usecs); + + return TRUE; +} + +static gboolean +commview_ncfx_read_rf_header(commview_ncfx_rf_header_t *cv_rf_hdr, FILE_T fh, + int *err, gchar **err_info) +{ + if (!wtap_read_bytes(fh, &cv_rf_hdr->header_len, 2, err, err_info)) + return FALSE; + + /* Convert header length from little endian to host endian format */ + cv_rf_hdr->header_len = GUINT16_FROM_LE(cv_rf_hdr->header_len); + + if (!wtap_read_bytes(fh, &cv_rf_hdr->status_modulation, 2, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_rf_hdr->frequency_band, 2, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_rf_hdr->channel, 2, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_rf_hdr->noise_level_dbm, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_rf_hdr->signal_level_dbm, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_rf_hdr->signal_level_percent, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_rf_hdr->reserved, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_rf_hdr->phy_rate, 4, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_rf_hdr->extensions_present, 4, err, err_info)) + return FALSE; + + /* Convert remaining multi-byte values from little endian to host endian format */ + cv_rf_hdr->status_modulation = GUINT16_FROM_LE(cv_rf_hdr->status_modulation); + cv_rf_hdr->frequency_band = GUINT16_FROM_LE(cv_rf_hdr->frequency_band); + cv_rf_hdr->channel = GUINT16_FROM_LE(cv_rf_hdr->channel); + cv_rf_hdr->phy_rate = GUINT32_FROM_LE(cv_rf_hdr->phy_rate); + cv_rf_hdr->extensions_present = GUINT32_FROM_LE(cv_rf_hdr->extensions_present); + + return TRUE; +} + +static gboolean +commview_ncfx_read_mcs_header(commview_ncfx_mcs_header_t *cv_mcs_hdr, FILE_T fh, + int *err, gchar **err_info) +{ + if (!wtap_read_bytes(fh, &cv_mcs_hdr->mcs_index, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_mcs_hdr->n_streams, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_mcs_hdr->channel_width, 1, err, err_info)) + return FALSE; + if (!wtap_read_bytes(fh, &cv_mcs_hdr->guard_interval, 1, err, err_info)) + return FALSE; + + return TRUE; +} + +/* Returns 0 if we can write out the specified encapsulation type + * into a CommView format file. */ +static int +commview_ncfx_dump_can_write_encap(int encap) +{ + switch (encap) { + + case WTAP_ENCAP_ETHERNET : + case WTAP_ENCAP_IEEE_802_11 : + case WTAP_ENCAP_IEEE_802_11_WITH_RADIO : + case WTAP_ENCAP_PER_PACKET : + return 0; + + default: + return WTAP_ERR_UNWRITABLE_ENCAP; + } +} + +/* Returns TRUE on success, FALSE on failure; + sets "*err" to an error code on failure */ +static gboolean +commview_ncfx_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) +{ + wdh->subtype_write = commview_ncfx_dump; + + /* There is no file header to write out */ + wdh->bytes_dumped = 0; + + return TRUE; +} + +/* Write a record for a packet to a dump file. + * Returns TRUE on success, FALSE on failure. */ +static gboolean +commview_ncfx_dump(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd, + int *err, gchar **err_info _U_) +{ + commview_ncfx_header_t cv_hdr = {0}; + struct tm *tm; + + /* We can only write packet records. */ + if (rec->rec_type != REC_TYPE_PACKET) { + *err = WTAP_ERR_UNWRITABLE_REC_TYPE; + return FALSE; + } + + /* Don't write out anything bigger than we can read. + * (The length field in packet headers is 16 bits, which + * imposes a hard limit.) */ + if (rec->rec_header.packet_header.caplen > 65535) { + *err = WTAP_ERR_PACKET_TOO_LARGE; + return FALSE; + } + + cv_hdr.data_len = GUINT32_TO_LE((guint32)rec->rec_header.packet_header.caplen); + + tm = localtime(&rec->ts.secs); + if (tm != NULL) { + cv_hdr.year = GUINT16_TO_LE(tm->tm_year + 1900); + cv_hdr.month = tm->tm_mon + 1; + cv_hdr.day = tm->tm_mday; + cv_hdr.hours = tm->tm_hour; + cv_hdr.minutes = tm->tm_min; + cv_hdr.seconds = tm->tm_sec; + cv_hdr.usecs = GUINT32_TO_LE(rec->ts.nsecs / 1000); + } else { + /* + * Second before the Epoch. + */ + cv_hdr.year = GUINT16_TO_LE(1969); + cv_hdr.month = 12; + cv_hdr.day = 31; + cv_hdr.hours = 23; + cv_hdr.minutes = 59; + cv_hdr.seconds = 59; + cv_hdr.usecs = 0; + } + cv_hdr.reserved1 = 0; + cv_hdr.reserved2 = 0; + + switch(rec->rec_header.packet_header.pkt_encap) { + + case WTAP_ENCAP_ETHERNET : + cv_hdr.medium_type = MEDIUM_ETHERNET; + cv_hdr.decryption_flag = 0x00; + cv_hdr.direction = 0x00; /* what does this mean? */ + break; + + case WTAP_ENCAP_IEEE_802_11 : + /* XXX - the claim is that the RF header is mandatory */ + cv_hdr.medium_type = MEDIUM_WIFI; + break; + + case WTAP_ENCAP_IEEE_802_11_WITH_RADIO : + cv_hdr.medium_type = MEDIUM_WIFI; + +#if 0 + switch (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy) { + + case PHDR_802_11_PHY_11N: + cv_hdr.status_modulation = STATUS_MODULATION_HT_PHY; + break; + + case PHDR_802_11_PHY_11AC: + cv_hdr.status_modulation = STATUS_MODULATION_VHT_PHY; + break; + + case PHDR_802_11_PHY_11AX: + cv_hdr.status_modulation = STATUS_MODULATION_HE_PHY; + break; + + default: + cv_hdr.status_modulation = 0; + break; + } + + /* + * Pick the band based on the frequency. + */ + if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_frequency) { + if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.frequency > 2484) { + /* 5 GHz band */ + cv_hdr.frequency_band = BAND_5GHZ; + } else { + /* 2.4 GHz band */ + cv_hdr.frequency_band = BAND_2_4GHZ; + } + } else { + /* Band is unknown. */ + cv_hdr.band = 0; + } + + cv_hdr.channel = + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel ? + rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel : + 0; + cv_hdr.noise_level_dbm = + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_noise_dbm ? + -rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_dbm : + 0; + cv_hdr.signal_level_dbm = + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_dbm ? + -rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_dbm : + 0; + cv_hdr.signal_level_percent = + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent ? + rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent : + 0; + cv_hdr.reserved = 0; + cv_hdr.phy_rate = + rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate ? + (guint32)(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate & 0xFF) : + 0; +#endif + break; + + default : + *err = WTAP_ERR_UNWRITABLE_ENCAP; + return FALSE; + } + + if (!wtap_dump_file_write(wdh, &cv_hdr.data_len, 4, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.year, 2, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.month, 1, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.day, 1, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.hours, 1, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.minutes, 1, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.seconds, 1, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.usecs, 4, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.medium_type, 1, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.decryption_flag, 1, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.direction, 1, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.reserved1, 1, err)) + return FALSE; + if (!wtap_dump_file_write(wdh, &cv_hdr.reserved2, 1, err)) + return FALSE; + wdh->bytes_dumped += COMMVIEW_NCFX_HEADER_SIZE; + + /* XXX - RF and MCS headers */ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err)) return FALSE; @@ -640,22 +1355,34 @@ static const struct supported_block_type commview_blocks_supported[] = { { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } }; -static const struct file_type_subtype_info commview_info = { - "TamoSoft CommView", "commview", "ncf", NULL, +static const struct file_type_subtype_info commview_ncf_info = { + "TamoSoft CommView NCF", "commview-ncf", "ncf", NULL, FALSE, BLOCKS_SUPPORTED(commview_blocks_supported), - commview_dump_can_write_encap, commview_dump_open, NULL + commview_ncf_dump_can_write_encap, commview_ncf_dump_open, NULL +}; + +static const struct file_type_subtype_info commview_ncfx_info = { + "TamoSoft CommView NCFX", "commview-ncfx", "ncfx", NULL, + FALSE, BLOCKS_SUPPORTED(commview_blocks_supported), + commview_ncfx_dump_can_write_encap, commview_ncfx_dump_open, NULL }; void register_commview(void) { - commview_file_type_subtype = wtap_register_file_type_subtype(&commview_info); + commview_ncf_file_type_subtype = wtap_register_file_type_subtype(&commview_ncf_info); + commview_ncfx_file_type_subtype = wtap_register_file_type_subtype(&commview_ncfx_info); /* * Register name for backwards compatibility with the * wtap_filetypes table in Lua. + * + * We don't need to register the new type, as the Wireshark + * version with which we're providing backwards compatibility + * didn't support the NCFX format. New code should fetch + * the file type/subtype with wtap_name_to_file_type_subtype(). */ wtap_register_backwards_compatibility_lua_name("COMMVIEW", - commview_file_type_subtype); + commview_ncf_file_type_subtype); } /* diff --git a/wiretap/commview.h b/wiretap/commview.h index 00ead984bc..b8d2edb6fd 100644 --- a/wiretap/commview.h +++ b/wiretap/commview.h @@ -12,7 +12,9 @@ #include #include "ws_symbol_export.h" -wtap_open_return_val commview_open(wtap *wth, int *err, gchar **err_info); +wtap_open_return_val commview_ncf_open(wtap *wth, int *err, gchar **err_info); + +wtap_open_return_val commview_ncfx_open(wtap *wth, int *err, gchar **err_info); #endif /* __COMMVIEW_H__ */ diff --git a/wiretap/file_access.c b/wiretap/file_access.c index 94842626c2..e549f99890 100644 --- a/wiretap/file_access.c +++ b/wiretap/file_access.c @@ -144,7 +144,8 @@ static const struct file_extension_info file_type_extensions_base[] = { { "Savvius *Peek", TRUE, "pkt;tpc;apc;wpz" }, { "Catapult DCT2000 trace (.out format)", TRUE, "out" }, { "Micropross mplog", TRUE, "mplog" }, - { "TamoSoft CommView", TRUE, "ncf" }, + { "TamoSoft CommView NCF", TRUE, "ncf" }, + { "TamoSoft CommView NCFX", TRUE, "ncfx" }, { "Symbian OS btsnoop", TRUE, "log" }, { "XML files (including Gammu DCT3 traces)", TRUE, "xml" }, { "macOS PacketLogger", TRUE, "pklg" }, @@ -421,7 +422,8 @@ static const struct open_info open_info_base[] = { { "TCPIPtrace (VMS)", OPEN_INFO_HEURISTIC, vms_open, "txt", NULL, NULL }, { "CoSine IPSX L2 capture", OPEN_INFO_HEURISTIC, cosine_open, "txt", NULL, NULL }, { "Bluetooth HCI dump", OPEN_INFO_HEURISTIC, hcidump_open, NULL, NULL, NULL }, - { "TamoSoft CommView", OPEN_INFO_HEURISTIC, commview_open, "ncf", NULL, NULL }, + { "TamoSoft CommView NCF", OPEN_INFO_HEURISTIC, commview_ncf_open, "ncf", NULL, NULL }, + { "TamoSoft CommView NCFX", OPEN_INFO_HEURISTIC, commview_ncfx_open, "ncfx", NULL, NULL }, { "NetScaler", OPEN_INFO_HEURISTIC, nstrace_open, "cap", NULL, NULL }, { "Android Logcat Binary format", OPEN_INFO_HEURISTIC, logcat_open, "logcat", NULL, NULL }, { "Android Logcat Text formats", OPEN_INFO_HEURISTIC, logcat_text_open, "txt", NULL, NULL },