/* commview.c * Routines for opening CommView NCF and NCFX file format packet captures * Copyright 2007, Stephen Fisher (see AUTHORS file) * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * Based on csids.c and nettl.c * * SPDX-License-Identifier: GPL-2.0-or-later */ /* 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 * * https://web.archive.org/web/20171022225753/http://www.tamos.com/htmlhelp/commview/logformat.htm * * if that doesn't display anything. */ #include "config.h" #include #include #include #include "wtap-int.h" #include "file_wrappers.h" #include "commview.h" #include /* * 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; guint16 year; guint8 month; guint8 day; guint8 hours; guint8 minutes; guint8 seconds; guint32 usecs; guint8 flags; /* Bit-field positions defined below */ guint8 signal_level_percent; guint8 rate; guint8 band; guint8 channel; guint8 direction; /* Or for WiFi, high order byte of * packet rate. */ gint8 signal_level_dbm; /* WiFi-only */ gint8 noise_level_dbm; /* WiFi-only */ } commview_ncf_header_t; #define COMMVIEW_NCF_HEADER_SIZE 24 /* Bit-field positions for various fields in the flags variable of the header */ #define FLAGS_MEDIUM 0x0F #define FLAGS_DECRYPTED 0x10 #define FLAGS_BROKEN 0x20 #define FLAGS_COMPRESSED 0x40 #define FLAGS_RESERVED 0x80 /* Values for the band variable of the header */ #define BAND_11A 0x01 #define BAND_11B 0x02 #define BAND_11G 0x04 #define BAND_11A_TURBO 0x08 #define BAND_SUPERG 0x10 #define BAND_PUBLIC_SAFETY 0x20 /* 4.99 GHz public safety */ #define BAND_11N_5GHZ 0x40 #define BAND_11N_2_4GHZ 0x80 static gboolean commview_ncf_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info, gint64 *data_offset); static gboolean commview_ncf_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info); static gboolean commview_ncf_read_header(commview_ncf_header_t *cv_hdr, FILE_T fh, int *err, gchar **err_info); static gboolean commview_ncf_dump(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd, int *err, gchar **err_info); 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_ncf_open(wtap *wth, int *err, gchar **err_info) { commview_ncf_header_t cv_hdr; 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; } /* If any of these fields do not match what we expect, bail out. */ if(cv_hdr.version != 0 || cv_hdr.year < 1970 || cv_hdr.year >= 2038 || 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 || cv_hdr.signal_level_percent > 100 || (cv_hdr.flags & FLAGS_RESERVED) != 0 || ((cv_hdr.flags & FLAGS_MEDIUM) != MEDIUM_ETHERNET && (cv_hdr.flags & FLAGS_MEDIUM) != MEDIUM_WIFI && (cv_hdr.flags & FLAGS_MEDIUM) != MEDIUM_TOKEN_RING)) 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_ncf_read; wth->subtype_seek_read = commview_ncf_seek_read; wth->file_type_subtype = commview_ncf_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_ncf_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info) { commview_ncf_header_t cv_hdr; struct tm tm; guint frequency; 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 * than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to * check it. */ switch(cv_hdr.flags & FLAGS_MEDIUM) { 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 = -1; /* Unknown */ rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = FALSE; rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN; switch (cv_hdr.band) { case BAND_11A: rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_channel_type = FALSE; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_turbo_type = TRUE; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.turbo_type = PHDR_802_11A_TURBO_TYPE_NORMAL; frequency = ieee80211_chan_to_mhz(cv_hdr.channel, FALSE); break; case BAND_11B: rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE; frequency = ieee80211_chan_to_mhz(cv_hdr.channel, TRUE); break; case BAND_11G: rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.has_mode = TRUE; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.mode = PHDR_802_11G_MODE_NORMAL; frequency = ieee80211_chan_to_mhz(cv_hdr.channel, TRUE); break; case BAND_11A_TURBO: rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_turbo_type = TRUE; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.turbo_type = PHDR_802_11A_TURBO_TYPE_TURBO; frequency = ieee80211_chan_to_mhz(cv_hdr.channel, FALSE); break; case BAND_SUPERG: rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.has_mode = TRUE; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.mode = PHDR_802_11G_MODE_SUPER_G; frequency = ieee80211_chan_to_mhz(cv_hdr.channel, TRUE); break; case BAND_11N_5GHZ: rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11N; frequency = ieee80211_chan_to_mhz(cv_hdr.channel, FALSE); break; case BAND_11N_2_4GHZ: rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11N; frequency = ieee80211_chan_to_mhz(cv_hdr.channel, TRUE); break; case BAND_PUBLIC_SAFETY: /* * XXX - what do we do here? What are the channel * numbers? How do we distinguish the several * different flavors of 4.9 GHz frequencies? */ frequency = 0; 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_hdr.channel; 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_hdr.rate | (cv_hdr.direction << 8); 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_hdr.signal_level_percent; /* * XXX - these are positive in captures I've seen; does * that mean that they are the negative of the actual * dBm value? (80 dBm is a bit more power than most * countries' regulatory agencies are likely to allow * any individual to have in their home. :-)) * * XXX - sometimes these are 0; assume that means that no * value is provided. */ if (cv_hdr.signal_level_dbm != 0) { 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_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) { /* * We don't know they PHY, but we do have the * data rate; try to guess it based on the * data rate and center frequency. */ if (RATE_IS_DSSS(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) { /* 11b */ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE; } else if (RATE_IS_OFDM(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) { /* 11a or 11g, depending on the band. */ if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_frequency) { if (FREQ_IS_BG(rec->rec_header.packet_header.pseudo_header.ieee_802_11.frequency)) { /* 11g */ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G; } else { /* 11a */ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A; } } } } else if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy == PHDR_802_11_PHY_11G) { if (RATE_IS_DSSS(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) { /* DSSS, so 11b. */ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B; rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE; } } break; case MEDIUM_TOKEN_RING : rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_TOKEN_RING; break; default : *err = WTAP_ERR_BAD_FILE; *err_info = ws_strdup_printf("commview: unsupported encap for NCF: %u", cv_hdr.flags & FLAGS_MEDIUM); 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->block = wtap_block_create(WTAP_BLOCK_PACKET); rec->presence_flags = WTAP_HAS_TS; rec->rec_header.packet_header.len = cv_hdr.data_len; rec->rec_header.packet_header.caplen = cv_hdr.data_len; 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_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_ncf_read_packet(wth->fh, rec, buf, err, err_info); } static gboolean 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_ncf_read_packet(wth->random_fh, rec, buf, err, err_info); } static gboolean 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)) return FALSE; if (!wtap_read_bytes(fh, &cv_hdr->source_data_len, 2, err, err_info)) return FALSE; if (!wtap_read_bytes(fh, &cv_hdr->version, 1, err, err_info)) 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->flags, 1, err, err_info)) return FALSE; if (!wtap_read_bytes(fh, &cv_hdr->signal_level_percent, 1, err, err_info)) return FALSE; if (!wtap_read_bytes(fh, &cv_hdr->rate, 1, err, err_info)) return FALSE; if (!wtap_read_bytes(fh, &cv_hdr->band, 1, err, err_info)) return FALSE; if (!wtap_read_bytes(fh, &cv_hdr->channel, 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->signal_level_dbm, 1, err, err_info)) return FALSE; 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 */ cv_hdr->data_len = GUINT16_FROM_LE(cv_hdr->data_len); cv_hdr->source_data_len = GUINT16_FROM_LE(cv_hdr->source_data_len); cv_hdr->year = GUINT16_FROM_LE(cv_hdr->year); cv_hdr->usecs = GUINT32_FROM_LE(cv_hdr->usecs); return TRUE; } /* Returns 0 if we can write out the specified encapsulation type * into a CommView format file. */ static int commview_ncf_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_TOKEN_RING : 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_ncf_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) { wdh->subtype_write = commview_ncf_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_ncf_dump(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd, int *err, gchar **err_info _U_) { commview_ncf_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 = GUINT16_TO_LE((guint16)rec->rec_header.packet_header.caplen); cv_hdr.source_data_len = GUINT16_TO_LE((guint16)rec->rec_header.packet_header.caplen); cv_hdr.version = 0; 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; } switch(rec->rec_header.packet_header.pkt_encap) { case WTAP_ENCAP_ETHERNET : cv_hdr.flags |= MEDIUM_ETHERNET; break; case WTAP_ENCAP_IEEE_802_11 : cv_hdr.flags |= MEDIUM_WIFI; break; case WTAP_ENCAP_IEEE_802_11_WITH_RADIO : cv_hdr.flags |= MEDIUM_WIFI; switch (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy) { case PHDR_802_11_PHY_11A: /* * If we don't know whether it's turbo, say it's * not. */ if (!rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_turbo_type || rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.turbo_type == PHDR_802_11A_TURBO_TYPE_NORMAL) cv_hdr.band = BAND_11A; else cv_hdr.band = BAND_11A_TURBO; break; case PHDR_802_11_PHY_11B: cv_hdr.band = BAND_11B; break; case PHDR_802_11_PHY_11G: /* * If we don't know whether it's Super G, say it's * not. */ if (!rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.has_mode) cv_hdr.band = BAND_11G; else { switch (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.mode) { case PHDR_802_11G_MODE_NORMAL: cv_hdr.band = BAND_11G; break; case PHDR_802_11G_MODE_SUPER_G: cv_hdr.band = BAND_SUPERG; break; default: cv_hdr.band = BAND_11G; break; } } break; case PHDR_802_11_PHY_11N: /* * 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.band = BAND_11N_5GHZ; } else { /* 2.4 GHz band */ cv_hdr.band = BAND_11N_2_4GHZ; } } else { /* Band is unknown. */ cv_hdr.band = 0; } break; default: /* * It's not documented how they handle 11ac, * and they don't support the older PHYs. */ cv_hdr.band = 0; break; } 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.rate = rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate ? (guint8)(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate & 0xFF) : 0; cv_hdr.direction = rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate ? (guint8)((rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate >> 8) & 0xFF) : 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.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.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; break; case WTAP_ENCAP_TOKEN_RING : cv_hdr.flags |= MEDIUM_TOKEN_RING; break; default : *err = WTAP_ERR_UNWRITABLE_ENCAP; return FALSE; } if (!wtap_dump_file_write(wdh, &cv_hdr.data_len, 2, err)) return FALSE; if (!wtap_dump_file_write(wdh, &cv_hdr.source_data_len, 2, err)) return FALSE; if (!wtap_dump_file_write(wdh, &cv_hdr.version, 1, 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.flags, 1, err)) return FALSE; if (!wtap_dump_file_write(wdh, &cv_hdr.signal_level_percent, 1, err)) return FALSE; if (!wtap_dump_file_write(wdh, &cv_hdr.rate, 1, err)) return FALSE; if (!wtap_dump_file_write(wdh, &cv_hdr.band, 1, err)) return FALSE; if (!wtap_dump_file_write(wdh, &cv_hdr.channel, 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.signal_level_dbm, 1, err)) return FALSE; if (!wtap_dump_file_write(wdh, &cv_hdr.noise_level_dbm, 1, err)) return FALSE; 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 = ws_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 = ws_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 = ws_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->block = wtap_block_create(WTAP_BLOCK_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 = ws_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 = ws_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; wdh->bytes_dumped += rec->rec_header.packet_header.caplen; return TRUE; } static const struct supported_block_type commview_blocks_supported[] = { /* * We support packet blocks, with no comments or other options. */ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } }; static const struct file_type_subtype_info commview_ncf_info = { "TamoSoft CommView NCF", "commview-ncf", "ncf", NULL, FALSE, BLOCKS_SUPPORTED(commview_blocks_supported), 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_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_ncf_file_type_subtype); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: t * End: * * vi: set shiftwidth=8 tabstop=8 noexpandtab: * :indentSize=8:tabSize=8:noTabs=false: */