diff options
author | Gerald Combs <gerald@wireshark.org> | 2015-08-18 11:46:12 -0700 |
---|---|---|
committer | Gerald Combs <gerald@wireshark.org> | 2015-08-20 23:12:37 +0000 |
commit | 2ed3d91b45332a2cbb4114b61f18c80fb28a6840 (patch) | |
tree | 1c37bec81ce931d54d8e0a3f59812dcdfe9c505f | |
parent | 0f9bbbc6f7146747841760102a74efb96161ad99 (diff) |
Add the WLAN statistics dialog.
Instead of splitting the stats into two lists as with the GTK+ UI, add
everything to an expandable tree. This allows viewing nodes on more than
one network.
Rename the top-level Bluetooth menu item to Wireless and put the WLAN
stats dialog there.
The Qt UI matches SSIDs (WlanNetworkTreeWidgetItem::isMatch) a bit
differently than the GTK+ UI. Try to make the logic as plain as possible
since we'll likely have to update it in the future.
The addition of a custom BSSID address types means that we can't assume
that everything is AT_ETHER. Add routines for checking for broadcast
BSSIDs and comparing only the data portions of addresses.
Move PercentBarDelegate into its own module. Use it in
WlanStatisticsDialog.
Change-Id: Ie4214eb00671a890871380c4a07213ebfb7585c6
Reviewed-on: https://code.wireshark.org/review/10171
Reviewed-by: Gerald Combs <gerald@wireshark.org>
-rw-r--r-- | epan/address.h | 19 | ||||
-rw-r--r-- | epan/dissectors/packet-ieee80211.c | 16 | ||||
-rw-r--r-- | epan/dissectors/packet-ieee80211.h | 17 | ||||
-rw-r--r-- | epan/wslua/taps | 2 | ||||
-rw-r--r-- | ui/gtk/wlan_stat_dlg.c | 24 | ||||
-rw-r--r-- | ui/qt/CMakeLists.txt | 4 | ||||
-rw-r--r-- | ui/qt/Makefile.common | 15 | ||||
-rw-r--r-- | ui/qt/Wireshark.pro | 9 | ||||
-rw-r--r-- | ui/qt/main_window.h | 3 | ||||
-rw-r--r-- | ui/qt/main_window.ui | 18 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 16 | ||||
-rw-r--r-- | ui/qt/multicast_statistics_dialog.cpp | 7 | ||||
-rw-r--r-- | ui/qt/percent_bar_delegate.cpp | 94 | ||||
-rw-r--r-- | ui/qt/percent_bar_delegate.h | 54 | ||||
-rw-r--r-- | ui/qt/protocol_hierarchy_dialog.cpp | 55 | ||||
-rw-r--r-- | ui/qt/protocol_hierarchy_dialog.h | 23 | ||||
-rw-r--r-- | ui/qt/wlan_statistics_dialog.cpp | 679 | ||||
-rw-r--r-- | ui/qt/wlan_statistics_dialog.h | 66 |
18 files changed, 1007 insertions, 114 deletions
diff --git a/epan/address.h b/epan/address.h index c48cc7020d..5855d22641 100644 --- a/epan/address.h +++ b/epan/address.h @@ -143,6 +143,25 @@ addresses_equal(const address *addr1, const address *addr2) { } #define ADDRESSES_EQUAL(addr1, addr2) addresses_equal((addr1), (addr2)) +/** Check the data of two addresses for equality. + * + * Given two addresses, return "true" if they have the same length and, + * their data is equal, "false" otherwise. + * The address types are ignored. This can be used to compare custom + * address types defined with address_type_dissector_register. + * + * @param addr1 [in] The first address to compare. + * @param addr2 [in] The second address to compare. + * @return TRUE if the adresses are equal, FALSE otherwise. + */ +static inline gboolean +addresses_data_equal(const address *addr1, const address *addr2) { + if ( addr1->len == addr2->len + && memcmp(addr1->data, addr2->data, addr1->len) == 0 + ) return TRUE; + return FALSE; +} + /** Copy an address, allocating a new buffer for the address data. * * @param to [in,out] The destination address. diff --git a/epan/dissectors/packet-ieee80211.c b/epan/dissectors/packet-ieee80211.c index 53e782109a..13f36e620e 100644 --- a/epan/dissectors/packet-ieee80211.c +++ b/epan/dissectors/packet-ieee80211.c @@ -5177,6 +5177,11 @@ static const enum_val_t wlan_ignore_wep_options[] = { static int wlan_address_type = -1; static int wlan_bssid_address_type = -1; +static address bssid_broadcast; +gboolean is_broadcast_bssid(const address *bssid) { + return addresses_equal(&bssid_broadcast, bssid); +} + static dissector_handle_t ieee80211_handle; static dissector_handle_t llc_handle; static dissector_handle_t ipx_handle; @@ -5294,7 +5299,7 @@ static int wlan_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip) { conv_hash_t *hash = (conv_hash_t*) pct; - const wlan_hdr *whdr=(const wlan_hdr *)vip; + const wlan_hdr_t *whdr=(const wlan_hdr_t *)vip; add_conversation_table_data(hash, &whdr->src, &whdr->dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->rel_ts, &pinfo->fd->abs_ts, &wlan_ct_dissector_info, PT_NONE); @@ -5315,7 +5320,7 @@ static int wlan_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip) { conv_hash_t *hash = (conv_hash_t*) pit; - const wlan_hdr *whdr=(const wlan_hdr *)vip; + const wlan_hdr_t *whdr=(const wlan_hdr_t *)vip; /* Take two "add" passes per packet, adding for each direction, ensures that all packets are counted properly (even if address is sending to itself) @@ -16558,14 +16563,14 @@ dissect_ieee80211_common (tvbuff_t *tvb, packet_info *pinfo, gint meshctl_len = 0; guint8 mesh_flags; guint16 meshoff = 0; - static wlan_hdr whdrs[4]; + static wlan_hdr_t whdrs[4]; gboolean retransmitted; gboolean isDMG = (tree == NULL) ? FALSE : proto_tree_traverse_post_order(proto_tree_get_root(tree), is_80211ad, NULL); volatile encap_t encap_type; proto_tree *volatile hdr_tree = NULL; tvbuff_t *volatile next_tvb = NULL; - wlan_hdr *volatile whdr; + wlan_hdr_t *volatile whdr; AIRPDCAP_KEY_ITEM used_key; @@ -26923,6 +26928,8 @@ proto_register_ieee80211 (void) module_t *wlan_module; + const unsigned char bssid_broadcast_data[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + memset(&wlan_stats, 0, sizeof wlan_stats); proto_aggregate = proto_register_protocol("IEEE 802.11 wireless LAN aggregate frame", @@ -26963,6 +26970,7 @@ proto_register_ieee80211 (void) ether_len, ether_name_resolution_str, ether_name_resolution_len); wlan_bssid_address_type = address_type_dissector_register("AT_ETHER_BSSID", "WLAN BSSID Address", ether_to_str, ether_str_len, wlan_bssid_col_filter_str, ether_len, ether_name_resolution_str, ether_name_resolution_len); + set_address(&bssid_broadcast, wlan_bssid_address_type, 6, bssid_broadcast_data); /* Register configuration options */ wlan_module = prefs_register_protocol(proto_wlan, init_wepkeys); diff --git a/epan/dissectors/packet-ieee80211.h b/epan/dissectors/packet-ieee80211.h index df63bf2e77..93fa7fcf89 100644 --- a/epan/dissectors/packet-ieee80211.h +++ b/epan/dissectors/packet-ieee80211.h @@ -27,6 +27,10 @@ #include "ws_symbol_export.h" +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + WS_DLL_PUBLIC void capture_ieee80211 (const guchar *, int, int, packet_counts *); void capture_ieee80211_datapad (const guchar *, int, int, packet_counts *); @@ -65,6 +69,13 @@ WS_DLL_PUBLIC const float ieee80211_float_htrates[MAX_MCS_INDEX+1][2][2]; WS_DLL_PUBLIC value_string_ext ieee80211_supported_rates_vals_ext; +WS_DLL_PUBLIC +gboolean is_broadcast_bssid(const address *bssid); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + /* * Extract the protocol version from the frame control field */ @@ -225,12 +236,12 @@ WS_DLL_PUBLIC value_string_ext ieee80211_supported_rates_vals_ext; */ #define EXTENSION_DMG_BEACON 0x30 /* Extension DMG beacon */ -struct _wlan_stats { +typedef struct _wlan_stats { guint8 channel; guint8 ssid_len; guchar ssid[MAX_SSID_LEN]; gchar protection[MAX_PROTECT_LEN]; -}; +} wlan_stats_t; typedef struct _wlan_hdr { address bssid; @@ -238,7 +249,7 @@ typedef struct _wlan_hdr { address dst; guint16 type; struct _wlan_stats stats; -} wlan_hdr; +} wlan_hdr_t; #define WLANCAP_MAGIC_COOKIE_BASE 0x80211000 #define WLANCAP_MAGIC_COOKIE_V1 0x80211001 diff --git a/epan/wslua/taps b/epan/wslua/taps index 56f4242a5b..eee8176bcf 100644 --- a/epan/wslua/taps +++ b/epan/wslua/taps @@ -76,5 +76,5 @@ smb2 ../dissectors/packet-smb2.h smb2_info_t tcp ../dissectors/packet-tcp.h tcp_info_t #teredo ../dissectors/packet-teredo.h teredo_info_t #tr ../dissectors/packet-tr.h tr_info_t -wlan ../dissectors/packet-ieee80211.h wlan_hdr +wlan ../dissectors/packet-ieee80211.h wlan_hdr_t #wsp ../dissectors/packet-wsp.h wsp_info_t diff --git a/ui/gtk/wlan_stat_dlg.c b/ui/gtk/wlan_stat_dlg.c index 672c672813..c6e16451e5 100644 --- a/ui/gtk/wlan_stat_dlg.c +++ b/ui/gtk/wlan_stat_dlg.c @@ -122,7 +122,6 @@ typedef struct wlan_ep { static GtkWidget *wlanstat_dlg_w = NULL; static GtkWidget *wlanstat_pane = NULL; static GtkWidget *wlanstat_name_lb = NULL; -static address broadcast; /* used to keep track of the statistics for an entire program interface */ typedef struct _wlan_stat_t { @@ -358,10 +357,10 @@ wlanstat_packet_details (wlan_ep_t *te, guint32 type, const address *addr, gbool } } +#if 0 static gboolean is_broadcast(const address *addr) { -#if 0 gboolean cmp_addr; char* addr_str = address_to_display(NULL, addr); @@ -370,9 +369,9 @@ is_broadcast(const address *addr) /* doesn't work if MAC resolution is disable */ return cmp_addr; -#endif return ADDRESSES_EQUAL(&broadcast, addr); } +#endif static gboolean ssid_equal(const struct _wlan_stats *st1, const struct _wlan_stats *st2 ) @@ -380,7 +379,7 @@ ssid_equal(const struct _wlan_stats *st1, const struct _wlan_stats *st2 ) return ((st1->ssid_len <= sizeof(st1->ssid)) && st1->ssid_len == st2->ssid_len) && (memcmp(st1->ssid, st2->ssid, st1->ssid_len) == 0); } -static int +static gboolean wlanstat_packet (void *phs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *phi) { wlanstat_t *hs = (wlanstat_t *)phs; @@ -389,19 +388,19 @@ wlanstat_packet (void *phs, packet_info *pinfo, epan_dissect_t *edt _U_, const v guint16 frame_type = si->type & 0xff0; if (!hs) - return (0); + return FALSE; if (!((frame_type == 0x0) || (frame_type == 0x20) || (frame_type == 0x30)) || ((frame_type == 0x20) && DATA_FRAME_IS_NULL(si->type))) { /* Not a management or non null data or extension frame; let's skip it */ - return (0); + return FALSE; } hs->number_of_packets++; if (!hs->ep_list) { hs->ep_list = alloc_wlan_ep (si, pinfo); te = hs->ep_list; - te->is_broadcast = is_broadcast(&si->bssid); + te->is_broadcast = is_broadcast_bssid(&si->bssid); } else { for (tmp = hs->ep_list; tmp; tmp = tmp->next) { if ((((si->type == MGT_PROBE_REQ) && ( @@ -417,7 +416,7 @@ wlanstat_packet (void *phs, packet_info *pinfo, epan_dissect_t *edt _U_, const v if (!te) { te = alloc_wlan_ep (si, pinfo); - te->is_broadcast = is_broadcast(&si->bssid); + te->is_broadcast = is_broadcast_bssid(&si->bssid); te->next = hs->ep_list; hs->ep_list = te; } @@ -488,7 +487,7 @@ wlanstat_packet (void *phs, packet_info *pinfo, epan_dissect_t *edt _U_, const v wlanstat_packet_details (te, si->type, &si->src, TRUE); /* Register source */ wlanstat_packet_details (te, si->type, &si->dst, FALSE); /* Register destination */ - return (1); + return TRUE; } static void @@ -508,8 +507,8 @@ wlanstat_draw_details(wlanstat_t *hs, wlan_ep_t *wlan_ep, gboolean clear) hs->num_details = 0; for (tmp = wlan_ep->details; tmp; tmp=tmp->next) { - broadcast_flag = is_broadcast(&tmp->addr); - basestation_flag = !broadcast_flag && !CMP_ADDRESS(&tmp->addr, &wlan_ep->bssid); + broadcast_flag = is_broadcast_bssid(&tmp->addr); + basestation_flag = !broadcast_flag && addresses_data_equal(&tmp->addr, &wlan_ep->bssid); if ((wlan_ep->number_of_packets - wlan_ep->type[MGT_BEACON]) > 0) { f = (float)(((float)tmp->number_of_packets * 100.0) / (wlan_ep->number_of_packets - wlan_ep->type[MGT_BEACON])); @@ -1989,9 +1988,6 @@ wlanstat_launch (GtkAction *action _U_, gpointer user_data _U_) void register_tap_listener_wlanstat (void) { - static const unsigned char src[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - - SET_ADDRESS(&broadcast, AT_ETHER, 6, src); } /* diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index b43922fedb..b19093f7c0 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -92,6 +92,7 @@ set(WIRESHARK_QT_HEADERS packet_list.h packet_list_model.h packet_range_group_box.h + percent_bar_delegate.h preference_editor_frame.h preferences_dialog.h print_dialog.h @@ -132,6 +133,7 @@ set(WIRESHARK_QT_HEADERS wireless_frame.h wireshark_application.h wireshark_dialog.h + wlan_statistics_dialog.h ) if(HAVE_PCAP_REMOTE) @@ -224,6 +226,7 @@ set(WIRESHARK_QT_SRC packet_list_model.cpp packet_list_record.cpp packet_range_group_box.cpp + percent_bar_delegate.cpp preference_editor_frame.cpp preferences_dialog.cpp print_dialog.cpp @@ -292,6 +295,7 @@ set(WIRESHARK_QT_TAP_SRC sctp_all_assocs_dialog.cpp sctp_assoc_analyse_dialog.cpp stats_tree_dialog.cpp + wlan_statistics_dialog.cpp ) set(WIRESHARK_QT_FILES diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index 142b511444..d6232989ea 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -99,7 +99,8 @@ NODIST_GENERATED_HEADER_FILES = \ ui_traffic_table_dialog.h \ ui_uat_dialog.h \ ui_voip_calls_dialog.h \ - ui_wireless_frame.h + ui_wireless_frame.h \ + ui_wlan_statistics_dialog.h # Generated C source files that we want in the distribution. GENERATED_C_FILES = \ @@ -206,6 +207,7 @@ MOC_HDRS = \ packet_list.h \ packet_list_model.h \ packet_range_group_box.h \ + percent_bar_delegate.h \ preference_editor_frame.h \ preferences_dialog.h \ print_dialog.h \ @@ -248,8 +250,8 @@ MOC_HDRS = \ voip_calls_dialog.h \ wireless_frame.h \ wireshark_application.h \ - wireshark_dialog.h - + wireshark_dialog.h \ + wlan_statistics_dialog.h # # .ui files. @@ -324,7 +326,8 @@ UI_FILES = \ traffic_table_dialog.ui \ uat_dialog.ui \ voip_calls_dialog.ui \ - wireless_frame.ui + wireless_frame.ui \ + wlan_statistics_dialog.ui # # The .moc.cpp files generated from them. @@ -444,6 +447,7 @@ WIRESHARK_QT_SRC = \ packet_list_model.cpp \ packet_list_record.cpp \ packet_range_group_box.cpp \ + percent_bar_delegate.cpp \ preference_editor_frame.cpp \ preferences_dialog.cpp \ print_dialog.cpp \ @@ -495,7 +499,8 @@ WIRESHARK_QT_TAP_SRC = \ rtp_stream_dialog.cpp \ sctp_all_assocs_dialog.cpp \ sctp_assoc_analyse_dialog.cpp \ - stats_tree_dialog.cpp + stats_tree_dialog.cpp \ + wlan_statistics_dialog.cpp noinst_HEADERS = \ $(MOC_HDRS) \ diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro index 627a66e8e4..8e1445cf1f 100644 --- a/ui/qt/Wireshark.pro +++ b/ui/qt/Wireshark.pro @@ -279,7 +279,6 @@ FORMS += \ voip_calls_dialog.ui \ wireless_frame.ui - HEADERS += $$HEADERS_WS_C \ about_dialog.h \ accordion_frame.h \ @@ -329,6 +328,7 @@ HEADERS += $$HEADERS_WS_C \ packet_comment_dialog.h \ packet_dialog.h \ packet_format_group_box.h \ + percent_bar_delegate.h \ preference_editor_frame.h \ preferences_dialog.h \ print_dialog.h \ @@ -358,7 +358,8 @@ HEADERS += $$HEADERS_WS_C \ traffic_table_dialog.h \ uat_dialog.h \ voip_calls_dialog.h \ - wireless_frame.h + wireless_frame.h \ + wlan_statistics_dialog.h win32 { OBJECTS_WS_C = $$SOURCES_WS_C @@ -642,7 +643,7 @@ HEADERS += \ time_shift_dialog.h \ wireshark_application.h \ wireshark_dialog.h \ - + wlan_statistics_dialog.h SOURCES += \ about_dialog.cpp \ @@ -718,6 +719,7 @@ SOURCES += \ packet_list_model.cpp \ packet_list_record.cpp \ packet_range_group_box.cpp \ + percent_bar_delegate.cpp \ preference_editor_frame.cpp \ preferences_dialog.cpp \ print_dialog.cpp \ @@ -763,4 +765,5 @@ SOURCES += \ wireless_frame.cpp \ wireshark_application.cpp \ wireshark_dialog.cpp \ + wlan_statistics_dialog.cpp \ ../../wireshark-qt.cpp diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index ad17ef355d..380c9ced41 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -480,6 +480,9 @@ private slots: void statCommandMulticastStatistics(const char *arg, void *); void on_actionStatisticsUdpMulticastStreams_triggered(); + void statCommandWlanStatistics(const char *arg, void *); + void on_actionWirelessWlanStatistics_triggered(); + void openStatisticsTreeDialog(const gchar *abbr); void on_actionStatistics29WestTopics_Advertisements_by_Topic_triggered(); void on_actionStatistics29WestTopics_Advertisements_by_Source_triggered(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 75c1971388..cec6122cc4 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -576,13 +576,15 @@ <addaction name="actionEditConfigurationProfiles"/> <addaction name="actionEditPreferences"/> </widget> - <widget class="QMenu" name="menuBluetooth"> + <widget class="QMenu" name="menuWireless"> <property name="title"> - <string>&Bluetooth</string> + <string>&Wireless</string> </property> <addaction name="actionATT_Server_Attributes"/> <addaction name="actionDevices"/> <addaction name="actionHCI_Summary"/> + <addaction name="separator"/> + <addaction name="actionWirelessWlanStatistics"/> </widget> <widget class="QMenu" name="menuTools"> <property name="title"> @@ -598,8 +600,8 @@ <addaction name="menuAnalyze"/> <addaction name="menuStatistics"/> <addaction name="menuTelephony"/> + <addaction name="menuWireless"/> <addaction name="menuTools"/> - <addaction name="menuBluetooth"/> <addaction name="menuHelp"/> </widget> <widget class="QToolBar" name="mainToolBar"> @@ -2385,7 +2387,7 @@ </action> <action name="actionATT_Server_Attributes"> <property name="text"> - <string>ATT Server Attributes</string> + <string>Bluetooth ATT Server Attributes</string> </property> </action> <action name="actionDevices"> @@ -2646,6 +2648,14 @@ <string>Show UTP multicast stream statistics.</string> </property> </action> + <action name="actionWirelessWlanStatistics"> + <property name="text"> + <string>WLAN Traffic</string> + </property> + <property name="toolTip"> + <string>Show IEEE 802.11 wireless LAN statistics.</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <customwidgets> diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 25b6030e8f..05d0d550e1 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -98,6 +98,7 @@ #if HAVE_EXTCAP #include "extcap_options_dialog.h" #endif +#include "filter_action.h" #include "filter_dialog.h" #include "funnel_statistics.h" #include "gsm_map_summary_dialog.h" @@ -130,7 +131,7 @@ #include "multicast_statistics_dialog.h" #include "voip_calls_dialog.h" #include "wireshark_application.h" -#include "filter_action.h" +#include "wlan_statistics_dialog.h" #include <QClipboard> #include <QFileInfo> @@ -2571,6 +2572,19 @@ void MainWindow::on_actionSCTPFilterThisAssociation_triggered() } } +void MainWindow::statCommandWlanStatistics(const char *arg, void *) +{ + WlanStatisticsDialog *wlan_stats_dlg = new WlanStatisticsDialog(*this, capture_file_, arg); + connect(wlan_stats_dlg, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)), + this, SLOT(filterAction(QString&,FilterAction::Action,FilterAction::ActionType))); + wlan_stats_dlg->show(); +} + +void MainWindow::on_actionWirelessWlanStatistics_triggered() +{ + statCommandWlanStatistics(NULL, NULL); +} + void MainWindow::statCommandExpertInfo(const char *, void *) { ExpertInfoDialog *expert_dialog = new ExpertInfoDialog(*this, capture_file_); diff --git a/ui/qt/multicast_statistics_dialog.cpp b/ui/qt/multicast_statistics_dialog.cpp index 4b4da27446..c818ce4e3f 100644 --- a/ui/qt/multicast_statistics_dialog.cpp +++ b/ui/qt/multicast_statistics_dialog.cpp @@ -306,10 +306,6 @@ void MulticastStatisticsDialog::tapDraw(void *mti_ptr) ms_ti->updateStreamInfo(stream_info); cur_row++; } - - for (int col = 0; col < ms_dlg->statsTreeWidget()->columnCount() - 1; col++) { - ms_dlg->statsTreeWidget()->resizeColumnToContents(col); - } } const QString MulticastStatisticsDialog::filterExpression() @@ -442,6 +438,9 @@ void MulticastStatisticsDialog::fillTree() tapDraw(tapinfo_); foreach (QWidget *w, disable_widgets) w->setEnabled(true); + for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) { + statsTreeWidget()->resizeColumnToContents(col); + } updateWidgets(); } diff --git a/ui/qt/percent_bar_delegate.cpp b/ui/qt/percent_bar_delegate.cpp new file mode 100644 index 0000000000..06ee6f422c --- /dev/null +++ b/ui/qt/percent_bar_delegate.cpp @@ -0,0 +1,94 @@ +/* percent_bar_delegate.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "percent_bar_delegate.h" + +#include "color_utils.h" + +#include <QApplication> +#include <QPainter> + +static const int bar_em_width_ = 8; +static const double bar_blend_ = 0.15; + +void PercentBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionViewItemV4 optv4 = option; + QStyledItemDelegate::initStyleOption(&optv4, index); + double value = index.data(Qt::UserRole).toDouble(); + + QStyledItemDelegate::paint(painter, option, index); + + painter->save(); + + if (QApplication::style()->objectName().contains("vista")) { + // QWindowsVistaStyle::drawControl does this internally. Unfortunately there + // doesn't appear to be a more general way to do this. + optv4.palette.setColor(QPalette::All, QPalette::HighlightedText, + optv4.palette.color(QPalette::Active, QPalette::Text)); + } + + QColor bar_color = ColorUtils::alphaBlend(optv4.palette.windowText(), + optv4.palette.window(), bar_blend_); + QPalette::ColorGroup cg = optv4.state & QStyle::State_Enabled + ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(optv4.state & QStyle::State_Active)) + cg = QPalette::Inactive; + if (optv4.state & QStyle::State_Selected) { + painter->setPen(optv4.palette.color(cg, QPalette::HighlightedText)); + bar_color = ColorUtils::alphaBlend(optv4.palette.color(cg, QPalette::Window), + optv4.palette.color(cg, QPalette::Highlight), + bar_blend_); + } else { + painter->setPen(optv4.palette.color(cg, QPalette::Text)); + } + + QRect pct_rect = option.rect; + pct_rect.adjust(1, 1, -1, -1); + pct_rect.setWidth(((pct_rect.width() * value) / 100.0) + 0.5); + painter->fillRect(pct_rect, bar_color); + + QString pct_str = QString::number(value, 'f', 1); + painter->drawText(option.rect, Qt::AlignCenter, pct_str); + + painter->restore(); +} + +QSize PercentBarDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + return QSize(option.fontMetrics.height() * bar_em_width_, + QStyledItemDelegate::sizeHint(option, index).height()); +} + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/percent_bar_delegate.h b/ui/qt/percent_bar_delegate.h new file mode 100644 index 0000000000..4bd2473e86 --- /dev/null +++ b/ui/qt/percent_bar_delegate.h @@ -0,0 +1,54 @@ +/* percent_bar_delegate.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef PERCENTBARDELEGATE_H +#define PERCENTBARDELEGATE_H + +#include <QStyledItemDelegate> + +class PercentBarDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + PercentBarDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) { } +protected: + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const; + +}; + +#endif // PERCENTBARDELEGATE_H + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/protocol_hierarchy_dialog.cpp b/ui/qt/protocol_hierarchy_dialog.cpp index 61f65de72e..05a498003d 100644 --- a/ui/qt/protocol_hierarchy_dialog.cpp +++ b/ui/qt/protocol_hierarchy_dialog.cpp @@ -27,12 +27,10 @@ #include "ui/proto_hier_stats.h" #include "ui/utf8_entities.h" -#include "color_utils.h" #include "qt_ui_utils.h" #include "wireshark_application.h" #include <QClipboard> -#include <QPainter> #include <QPushButton> #include <QTextStream> #include <QTreeWidgetItemIterator> @@ -58,59 +56,6 @@ const int end_packets_col_ = 6; const int end_bytes_col_ = 7; const int end_bandwidth_col_ = 8; -const int bar_em_width_ = 8; -const double bar_blend_ = 0.15; -void PercentBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItemV4 optv4 = option; - QStyledItemDelegate::initStyleOption(&optv4, index); - double value = index.data(Qt::UserRole).toDouble(); - - QStyledItemDelegate::paint(painter, option, index); - - painter->save(); - - if (QApplication::style()->objectName().contains("vista")) { - // QWindowsVistaStyle::drawControl does this internally. Unfortunately there - // doesn't appear to be a more general way to do this. - optv4.palette.setColor(QPalette::All, QPalette::HighlightedText, - optv4.palette.color(QPalette::Active, QPalette::Text)); - } - - QColor bar_color = ColorUtils::alphaBlend(optv4.palette.windowText(), - optv4.palette.window(), bar_blend_); - QPalette::ColorGroup cg = optv4.state & QStyle::State_Enabled - ? QPalette::Normal : QPalette::Disabled; - if (cg == QPalette::Normal && !(optv4.state & QStyle::State_Active)) - cg = QPalette::Inactive; - if (optv4.state & QStyle::State_Selected) { - painter->setPen(optv4.palette.color(cg, QPalette::HighlightedText)); - bar_color = ColorUtils::alphaBlend(optv4.palette.color(cg, QPalette::Window), - optv4.palette.color(cg, QPalette::Highlight), - bar_blend_); - } else { - painter->setPen(optv4.palette.color(cg, QPalette::Text)); - } - - QRect pct_rect = option.rect; - pct_rect.adjust(1, 1, -1, -1); - pct_rect.setWidth(((pct_rect.width() * value) / 100.0) + 0.5); - painter->fillRect(pct_rect, bar_color); - - QString pct_str = QString::number(value, 'f', 1); - painter->drawText(option.rect, Qt::AlignCenter, pct_str); - - painter->restore(); -} - -QSize PercentBarDelegate::sizeHint(const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - return QSize(option.fontMetrics.height() * bar_em_width_, - QStyledItemDelegate::sizeHint(option, index).height()); -} - Q_DECLARE_METATYPE(ph_stats_t*) class ProtocolHierarchyTreeWidgetItem : public QTreeWidgetItem diff --git a/ui/qt/protocol_hierarchy_dialog.h b/ui/qt/protocol_hierarchy_dialog.h index 1c0fb47c59..b3963cd4df 100644 --- a/ui/qt/protocol_hierarchy_dialog.h +++ b/ui/qt/protocol_hierarchy_dialog.h @@ -22,11 +22,12 @@ #ifndef PROTOCOL_HIERARCHY_DIALOG_H #define PROTOCOL_HIERARCHY_DIALOG_H +#include <QMenu> + #include "filter_action.h" +#include "percent_bar_delegate.h" #include "wireshark_dialog.h" -#include <QMenu> - class QPushButton; class QTreeWidgetItem; @@ -34,24 +35,6 @@ namespace Ui { class ProtocolHierarchyDialog; } -#include <QStyledItemDelegate> - -class PercentBarDelegate : public QStyledItemDelegate -{ -public: - PercentBarDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) { } -// void clear(); -// void addRelatedFrame(int frame_num); -// void setConversationSpan(int first_frame, int last_frame); -protected: - void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const; - QSize sizeHint(const QStyleOptionViewItem &option, - const QModelIndex &index) const; - -private: -}; - class ProtocolHierarchyDialog : public WiresharkDialog { Q_OBJECT diff --git a/ui/qt/wlan_statistics_dialog.cpp b/ui/qt/wlan_statistics_dialog.cpp new file mode 100644 index 0000000000..181001a6ed --- /dev/null +++ b/ui/qt/wlan_statistics_dialog.cpp @@ -0,0 +1,679 @@ +/* wlan_statistics_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "wlan_statistics_dialog.h" + +#include <epan/packet.h> +#include <epan/strutil.h> +#include <epan/tap.h> + +#include <epan/dissectors/packet-ieee80211.h> + +#include <QMessageBox> +#include <QTreeWidget> +#include <QTreeWidgetItem> + +#include "percent_bar_delegate.h" +#include "qt_ui_utils.h" +#include "wireshark_application.h" + +// To do: +// - Add the name resolution checkbox +// - Add the "Only show defined networks" checkbox + +enum { + col_bssid_, + col_channel_, + col_ssid_, + col_pct_packets_, + col_beacons_, + col_data_packets_, + col_probe_reqs_, + col_probe_resps_, + col_auths_, + col_deauths_, + col_others_, + col_protection_ +}; + +enum { + wlan_network_row_type_ = 1000, + wlan_station_row_type_ +}; + +class WlanStationTreeWidgetItem : public QTreeWidgetItem +{ +public: + WlanStationTreeWidgetItem(address *addr) : + QTreeWidgetItem (wlan_station_row_type_), + packets_(0), + sent_(0), + received_(0), + probe_req_(0), + probe_resp_(0), + auth_(0), + deauth_(0), + other_(0) + { + copy_address(&addr_, addr); + setText(col_bssid_, address_to_qstring(&addr_)); + } + bool isMatch(address *addr) { + return (addresses_equal(&addr_, addr)) == TRUE; + } + void update(wlan_hdr_t *wlan_hdr) { + bool is_sender = addresses_equal(&addr_, &wlan_hdr->src) == TRUE; + + // XXX Should we count received probes and auths? This is what the + // GTK+ UI does, but it seems odd. + switch (wlan_hdr->type) { + case MGT_PROBE_REQ: + probe_req_++; + break; + case MGT_PROBE_RESP: + probe_resp_++; + break; + case MGT_BEACON: + // Skip + break; + case MGT_AUTHENTICATION: + auth_++; + break; + case MGT_DEAUTHENTICATION: + deauth_++; + break; + case DATA: + case DATA_CF_ACK: + case DATA_CF_POLL: + case DATA_CF_ACK_POLL: + case DATA_QOS_DATA: + case DATA_QOS_DATA_CF_ACK: + case DATA_QOS_DATA_CF_POLL: + case DATA_QOS_DATA_CF_ACK_POLL: + if (is_sender) { + sent_++; + } else { + received_++; + } + break; + default: + other_++; + break; + } + if (wlan_hdr->type != MGT_BEACON) packets_++; + } + void draw(address *bssid, int num_packets) { + setData(col_pct_packets_, Qt::UserRole, QVariant::fromValue<double>(packets_ * 100.0 / num_packets)); + setText(col_beacons_, QString::number(sent_)); + setText(col_data_packets_, QString::number(received_)); + setText(col_probe_reqs_, QString::number(probe_req_)); + setText(col_probe_resps_, QString::number(probe_resp_)); + setText(col_auths_, QString::number(auth_)); + setText(col_deauths_, QString::number(deauth_)); + setText(col_others_, QString::number(other_)); + + if (!is_broadcast_bssid(bssid) && addresses_data_equal(&addr_, bssid)) { + setText(col_protection_, QObject::tr("Base station")); + } + } + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != wlan_station_row_type_) return QTreeWidgetItem::operator< (other); + const WlanStationTreeWidgetItem *other_row = static_cast<const WlanStationTreeWidgetItem *>(&other); + + switch (treeWidget()->sortColumn()) { + case col_bssid_: + return cmp_address(&addr_, &other_row->addr_) < 0; + case col_pct_packets_: + return packets_ < other_row->packets_; + case col_beacons_: + return sent_ < other_row->sent_; + case col_data_packets_: + return received_ < other_row->received_; + case col_probe_reqs_: + return probe_req_ < other_row->probe_req_; + case col_probe_resps_: + return probe_resp_ < other_row->probe_resp_; + case col_auths_: + return auth_ < other_row->auth_; + case col_deauths_: + return deauth_ < other_row->deauth_; + case col_others_: + return other_ < other_row->other_; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + QList<QVariant> rowData() { + return QList<QVariant>() + << address_to_qstring(&addr_) + << data(col_pct_packets_, Qt::UserRole).toDouble() + << sent_ << received_ << probe_req_ << probe_resp_ + << auth_ << deauth_ << other_ << text(col_protection_); + } + const QString filterExpression() { + QString filter_expr = QString("wlan.addr==%1") + .arg(address_to_qstring(&addr_)); + return filter_expr; + } + +private: + address addr_; + int packets_; + int sent_; + int received_; + int probe_req_; + int probe_resp_; + int auth_; + int deauth_; + int other_; +}; + +class WlanNetworkTreeWidgetItem : public QTreeWidgetItem +{ +public: + WlanNetworkTreeWidgetItem(QTreeWidget *parent, wlan_hdr_t *wlan_hdr) : + QTreeWidgetItem (parent, wlan_network_row_type_), + beacon_(0), + data_packet_(0), + probe_req_(0), + probe_resp_(0), + auth_(0), + deauth_(0), + other_(0), + packets_(0) + { + updateBssid(wlan_hdr); + channel_ = wlan_hdr->stats.channel; + ssid_ = QByteArray::fromRawData((const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len); + QString ssid_text; + + if (wlan_hdr->stats.ssid_len == 0) { + ssid_text = QObject::tr("<Broadcast>"); + } else if (wlan_hdr->stats.ssid_len == 1 && wlan_hdr->stats.ssid[0] == 0) { + ssid_text = QObject::tr("<Hidden>"); + } else { + ssid_text = format_text(wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len); + } + + setText(col_ssid_, ssid_text); + } + + bool isMatch(wlan_hdr_t *wlan_hdr) { + bool is_bssid_match = false; + bool is_ssid_match = false; + bool update_bssid = false; + bool update_ssid = false; + // We want (but might not have) a unicast BSSID and a named SSID. Try + // to match the current packet and update our information if possible. + + if (addresses_equal(&bssid_, &wlan_hdr->bssid)) { + is_bssid_match = true; + } + + if ((wlan_hdr->stats.ssid_len > 0) && (wlan_hdr->stats.ssid[0] != 0)) { + QByteArray hdr_ssid = QByteArray::fromRawData((const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len); + if (ssid_ == hdr_ssid) { + is_ssid_match = true; + } + } + + if (is_bssid_match && is_ssid_match) return true; + + // Probe requests. + if (wlan_hdr->type == MGT_PROBE_REQ) { + // Probes with visible SSIDs. Unicast or broadcast. + if (is_ssid_match) { + if (is_broadcast_ && !is_broadcast_bssid(&wlan_hdr->bssid)) { + update_bssid = true; + } + // Probes with hidden SSIDs. Unicast. + } else if ((wlan_hdr->stats.ssid_len == 1) && (wlan_hdr->stats.ssid[0] == 0)) { + if (!is_broadcast_ && addresses_equal(&bssid_, &wlan_hdr->bssid)) { + is_bssid_match = true; + update_ssid = true; + } + // Probes with no SSID. Broadcast. + } else if (ssid_.isEmpty() && wlan_hdr->stats.ssid_len < 1) { + if (is_broadcast_ && is_broadcast_bssid(&wlan_hdr->bssid)) { + return true; + } + } + // Non-probe requests (responses, beacons, etc) + } else { + if (is_ssid_match) { + if (is_broadcast_ && !is_broadcast_bssid(&wlan_hdr->bssid)) { + update_bssid = true; + } + } else if (wlan_hdr->stats.ssid_len < 1) { + // No SSID. + is_ssid_match = true; + } + if (is_bssid_match) { + if ((ssid_.isEmpty() || ssid_[0] == '\0') && (wlan_hdr->stats.ssid_len > 0) && (wlan_hdr->stats.ssid[0] != 0) ) { + update_ssid = true; + } + } + } + + if (update_bssid) { + updateBssid(wlan_hdr); + is_bssid_match = true; + } + + if (update_ssid) { + ssid_ = QByteArray::fromRawData((const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len); + setText(col_ssid_, format_text(wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len)); + is_ssid_match = true; + } + + return is_bssid_match && is_ssid_match; + } + + void update(wlan_hdr_t *wlan_hdr) { + if (channel_ == 0 && wlan_hdr->stats.channel != 0) { + channel_ = wlan_hdr->stats.channel; + } + if (text(col_protection_).isEmpty() && wlan_hdr->stats.protection[0] != 0) { + setText(col_protection_, wlan_hdr->stats.protection); + } + switch (wlan_hdr->type) { + case MGT_PROBE_REQ: + probe_req_++; + break; + case MGT_PROBE_RESP: + probe_resp_++; + break; + case MGT_BEACON: + beacon_++; + break; + case MGT_AUTHENTICATION: + auth_++; + break; + case MGT_DEAUTHENTICATION: + deauth_++; + break; + case DATA: + case DATA_CF_ACK: + case DATA_CF_POLL: + case DATA_CF_ACK_POLL: + case DATA_QOS_DATA: + case DATA_QOS_DATA_CF_ACK: + case DATA_QOS_DATA_CF_POLL: + case DATA_QOS_DATA_CF_ACK_POLL: + data_packet_++; + break; + default: + other_++; + break; + } + packets_++; + + WlanStationTreeWidgetItem* sender_ws_ti = NULL; + WlanStationTreeWidgetItem* receiver_ws_ti = NULL; + foreach (QTreeWidgetItem *cur_ti, stations_) { + WlanStationTreeWidgetItem *cur_ws_ti = dynamic_cast<WlanStationTreeWidgetItem *>(cur_ti); + if (cur_ws_ti->isMatch(&wlan_hdr->src)) sender_ws_ti = cur_ws_ti; + if (cur_ws_ti->isMatch(&wlan_hdr->dst)) receiver_ws_ti = cur_ws_ti; + if (sender_ws_ti && receiver_ws_ti) break; + } + if (!sender_ws_ti) { + sender_ws_ti = new WlanStationTreeWidgetItem(&wlan_hdr->src); + stations_ << sender_ws_ti; + } + if (!receiver_ws_ti) { + receiver_ws_ti = new WlanStationTreeWidgetItem(&wlan_hdr->dst); + stations_ << receiver_ws_ti; + } + sender_ws_ti->update(wlan_hdr); + receiver_ws_ti->update(wlan_hdr); + } + + void draw(int num_packets) { + if (channel_ > 0) setText(col_channel_, QString::number(channel_)); + setData(col_pct_packets_, Qt::UserRole, QVariant::fromValue<double>(packets_ * 100.0 / num_packets)); + setText(col_beacons_, QString::number(beacon_)); + setText(col_data_packets_, QString::number(data_packet_)); + setText(col_probe_reqs_, QString::number(probe_req_)); + setText(col_probe_resps_, QString::number(probe_resp_)); + setText(col_auths_, QString::number(auth_)); + setText(col_deauths_, QString::number(deauth_)); + setText(col_others_, QString::number(other_)); + } + + void addStations() { + foreach (QTreeWidgetItem *cur_ti, stations_) { + WlanStationTreeWidgetItem *cur_ws_ti = dynamic_cast<WlanStationTreeWidgetItem *>(cur_ti); + cur_ws_ti->draw(&bssid_, packets_ - beacon_); + for (int col = 0; col < treeWidget()->columnCount(); col++) { + cur_ws_ti->setTextAlignment(col, treeWidget()->headerItem()->textAlignment(col)); + } + } + + addChildren(stations_); + stations_.clear(); + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != wlan_network_row_type_) return QTreeWidgetItem::operator< (other); + const WlanNetworkTreeWidgetItem *other_row = static_cast<const WlanNetworkTreeWidgetItem *>(&other); + + switch (treeWidget()->sortColumn()) { + case col_bssid_: + return cmp_address(&bssid_, &other_row->bssid_) < 0; + case col_channel_: + return channel_ < other_row->channel_; + case col_ssid_: + return ssid_ < other_row->ssid_; + case col_pct_packets_: + return packets_ < other_row->packets_; + case col_beacons_: + return beacon_ < other_row->beacon_; + case col_data_packets_: + return data_packet_ < other_row->data_packet_; + case col_probe_reqs_: + return probe_req_ < other_row->probe_req_; + case col_probe_resps_: + return probe_resp_ < other_row->probe_resp_; + case col_auths_: + return auth_ < other_row->auth_; + case col_deauths_: + return deauth_ < other_row->deauth_; + case col_others_: + return other_ < other_row->other_; + case col_protection_: + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + QList<QVariant> rowData() { + return QList<QVariant>() + << address_to_qstring(&bssid_) << channel_ << text(col_ssid_) + << data(col_pct_packets_, Qt::UserRole).toDouble() + << beacon_ << data_packet_ << probe_req_ << probe_resp_ + << auth_ << deauth_ << other_ << text(col_protection_); + } + + const QString filterExpression() { + QString filter_expr = QString("(wlan.bssid==%1") + .arg(address_to_qstring(&bssid_)); + if (!ssid_.isEmpty() && ssid_[0] != '\0') { + filter_expr += QString(" || wlan_mgt.ssid==\"%1\"") + .arg(ssid_.constData()); + } + filter_expr += ")"; + return filter_expr; + } + +private: + address bssid_; + bool is_broadcast_; + int channel_; + QByteArray ssid_; + int beacon_; + int data_packet_; + int probe_req_; + int probe_resp_; + int auth_; + int deauth_; + int other_; + int packets_; + + // Adding items one at a time is slow. Gather up the stations in a list + // and add them all at once later. + QList<QTreeWidgetItem *>stations_; + + void updateBssid(wlan_hdr_t *wlan_hdr) { + copy_address(&bssid_, &wlan_hdr->bssid); + is_broadcast_ = is_broadcast_bssid(&bssid_) == TRUE; + setText(col_bssid_, address_to_qstring(&bssid_)); + } +}; + +static const QString network_col_0_title_ = QObject::tr("BSSID"); +static const QString network_col_4_title_ = QObject::tr("Beacons"); +static const QString network_col_5_title_ = QObject::tr("Data Pkts"); +static const QString network_col_11_title_ = QObject::tr("Protection"); + +static const QString node_col_0_title_ = QObject::tr("Address"); +static const QString node_col_4_title_ = QObject::tr("Pkts Sent"); +static const QString node_col_5_title_ = QObject::tr("Pkts Received"); +static const QString node_col_11_title_ = QObject::tr("Comment"); + +WlanStatisticsDialog::WlanStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter = NULL) : + TapParameterDialog(parent, cf, HELP_STATS_WLAN_TRAFFIC_DIALOG) +{ + setWindowSubtitle(tr("Wireless LAN Statistics")); + + // XXX Use recent settings instead + resize(parent.width() * 4 / 5, parent.height() * 3 / 4); + + QStringList header_labels = QStringList() + << "" << tr("Channel") << tr("SSID") << tr("Percent Packets") << "" << "" + << tr("Probe Reqs") << tr("Probe Resp") << tr("Auths") + << tr("Deauths") << tr("Other"); + statsTreeWidget()->setHeaderLabels(header_labels); + updateHeaderLabels(); + statsTreeWidget()->setItemDelegateForColumn(col_pct_packets_, new PercentBarDelegate()); + statsTreeWidget()->sortByColumn(col_bssid_, Qt::AscendingOrder); + + // resizeColumnToContents doesn't work well here, so set sizes manually. + int one_em = fontMetrics().height(); + for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) { + switch (col) { + case col_bssid_: + statsTreeWidget()->setColumnWidth(col, one_em * 11); + break; + case col_ssid_: + statsTreeWidget()->setColumnWidth(col, one_em * 8); + break; + case col_pct_packets_: + case col_protection_: + statsTreeWidget()->setColumnWidth(col, one_em * 6); + break; + default: + // The rest are numeric + statsTreeWidget()->setColumnWidth(col, one_em * 4); + statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight); + break; + } + } + + addFilterActions(); + + connect(statsTreeWidget(), SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), + this, SLOT(updateHeaderLabels())); +} + +WlanStatisticsDialog::~WlanStatisticsDialog() +{ +} + +void WlanStatisticsDialog::tapReset(void *ws_dlg_ptr) +{ + WlanStatisticsDialog *ws_dlg = static_cast<WlanStatisticsDialog *>(ws_dlg_ptr); + if (!ws_dlg) return; + + ws_dlg->statsTreeWidget()->clear(); + ws_dlg->packet_count_ = 0; +} + +gboolean WlanStatisticsDialog::tapPacket(void *ws_dlg_ptr, _packet_info *, epan_dissect *, const void *wlan_hdr_ptr) +{ + WlanStatisticsDialog *ws_dlg = static_cast<WlanStatisticsDialog *>(ws_dlg_ptr); + wlan_hdr_t *wlan_hdr = (wlan_hdr_t *)wlan_hdr_ptr; + if (!ws_dlg || !wlan_hdr) return FALSE; + + guint16 frame_type = wlan_hdr->type & 0xff0; + if (!((frame_type == 0x0) || (frame_type == 0x20) || (frame_type == 0x30)) + || ((frame_type == 0x20) && DATA_FRAME_IS_NULL(wlan_hdr->type))) { + /* Not a management or non null data or extension frame; let's skip it */ + return FALSE; + } + + ws_dlg->packet_count_++; + + WlanNetworkTreeWidgetItem *wn_ti = NULL; + for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { + QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); + if (ti->type() != wlan_network_row_type_) continue; + WlanNetworkTreeWidgetItem *cur_wn_ti = static_cast<WlanNetworkTreeWidgetItem*>(ti); + + if (cur_wn_ti->isMatch(wlan_hdr)) { + wn_ti = cur_wn_ti; + break; + } + } + + if (!wn_ti) { + wn_ti = new WlanNetworkTreeWidgetItem(ws_dlg->statsTreeWidget(), wlan_hdr); + for (int col = 0; col < ws_dlg->statsTreeWidget()->columnCount(); col++) { + wn_ti->setTextAlignment(col, ws_dlg->statsTreeWidget()->headerItem()->textAlignment(col)); + } + } + + wn_ti->update(wlan_hdr); + return TRUE; +} + +void WlanStatisticsDialog::tapDraw(void *ws_dlg_ptr) +{ + WlanStatisticsDialog *ws_dlg = static_cast<WlanStatisticsDialog *>(ws_dlg_ptr); + if (!ws_dlg) return; + + for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { + QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); + if (ti->type() != wlan_network_row_type_) continue; + + WlanNetworkTreeWidgetItem *wn_ti = static_cast<WlanNetworkTreeWidgetItem*>(ti); + wn_ti->draw(ws_dlg->packet_count_); + } +} + +const QString WlanStatisticsDialog::filterExpression() +{ + QString filter_expr; + if (statsTreeWidget()->selectedItems().count() > 0) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + + if (ti->type() == wlan_network_row_type_) { + WlanNetworkTreeWidgetItem *wn_ti = static_cast<WlanNetworkTreeWidgetItem*>(ti); + filter_expr = wn_ti->filterExpression(); + } else if (ti->type() == wlan_station_row_type_) { + WlanStationTreeWidgetItem *ws_ti = static_cast<WlanStationTreeWidgetItem*>(ti); + filter_expr = ws_ti->filterExpression(); + } + } + return filter_expr; +} + +void WlanStatisticsDialog::fillTree() +{ + GString *error_string = register_tap_listener("wlan", + this, + NULL, + TL_REQUIRES_NOTHING, + tapReset, + tapPacket, + tapDraw); + if (error_string) { + QMessageBox::warning(this, tr("Endpoint expert failed to register tap listener"), + error_string->str); + g_string_free(error_string, TRUE); + reject(); + return; + } + + cap_file_.retapPackets(); + tapDraw(this); + remove_tap_listener(this); + + for (int i = 0; i < statsTreeWidget()->topLevelItemCount(); i++) { + QTreeWidgetItem *ti = statsTreeWidget()->topLevelItem(i); + if (ti->type() != wlan_network_row_type_) continue; + + WlanNetworkTreeWidgetItem *wn_ti = static_cast<WlanNetworkTreeWidgetItem*>(ti); + wn_ti->addStations(); + } +} + +void WlanStatisticsDialog::updateHeaderLabels() +{ + if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == wlan_station_row_type_) { + statsTreeWidget()->headerItem()->setText(col_bssid_, node_col_0_title_); + statsTreeWidget()->headerItem()->setText(col_beacons_, node_col_4_title_); + statsTreeWidget()->headerItem()->setText(col_data_packets_, node_col_5_title_); + statsTreeWidget()->headerItem()->setText(col_protection_, node_col_11_title_); + } else { + statsTreeWidget()->headerItem()->setText(col_bssid_, network_col_0_title_); + statsTreeWidget()->headerItem()->setText(col_beacons_, network_col_4_title_); + statsTreeWidget()->headerItem()->setText(col_data_packets_, network_col_5_title_); + statsTreeWidget()->headerItem()->setText(col_protection_, network_col_11_title_); + } +} + +void WlanStatisticsDialog::captureFileClosing() +{ + remove_tap_listener(this); + updateWidgets(); +} + +// Stat command + args + +static void +wlan_statistics_init(const char *, void*) { + wsApp->emitStatCommandSignal("WlanStatistics", NULL, NULL); +} + +static stat_tap_ui wlan_statistics_ui = { + REGISTER_STAT_GROUP_GENERIC, + NULL, + "wlan,stat", + wlan_statistics_init, + 0, + NULL +}; + +extern "C" { +void +register_tap_listener_qt_wlan_statistics(void) +{ + register_stat_tap_ui(&wlan_statistics_ui, NULL); +} +} + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/wlan_statistics_dialog.h b/ui/qt/wlan_statistics_dialog.h new file mode 100644 index 0000000000..e39f4b6427 --- /dev/null +++ b/ui/qt/wlan_statistics_dialog.h @@ -0,0 +1,66 @@ +/* wlan_statistics_dialog.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef WLANSTATISTICSDIALOG_H +#define WLANSTATISTICSDIALOG_H + +#include "tap_parameter_dialog.h" + +class WlanStatisticsDialog : public TapParameterDialog +{ + Q_OBJECT + +public: + WlanStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter); + ~WlanStatisticsDialog(); + +protected: + +private: + int packet_count_; + + // Callbacks for register_tap_listener + static void tapReset(void *ws_dlg_ptr); + static gboolean tapPacket(void *ws_dlg_ptr, struct _packet_info *, struct epan_dissect *, const void *wlan_hdr_ptr); + static void tapDraw(void *ws_dlg_ptr); + + virtual const QString filterExpression(); + +private slots: + virtual void fillTree(); + void updateHeaderLabels(); + void captureFileClosing(); +}; + +#endif // WLANSTATISTICSDIALOG_H + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |