1 /* wlan_statistics_dialog.cpp
2 *
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "wlan_statistics_dialog.h"
11
12 #include <epan/packet.h>
13 #include <epan/strutil.h>
14 #include <epan/tap.h>
15
16 #include <epan/dissectors/packet-ieee80211.h>
17
18 #include <QElapsedTimer>
19 #include <QTreeWidget>
20 #include <QTreeWidgetItem>
21
22 #include <ui/qt/models/percent_bar_delegate.h>
23 #include <ui/qt/utils/qt_ui_utils.h>
24 #include "wireshark_application.h"
25
26 // To do:
27 // - Add the name resolution checkbox
28 // - Add the "Only show defined networks" checkbox
29
30 enum {
31 col_bssid_,
32 col_channel_,
33 col_ssid_,
34 col_pct_packets_,
35 col_pct_retry_,
36 col_retry_packets_,
37 col_beacons_,
38 col_data_packets_,
39 col_probe_reqs_,
40 col_probe_resps_,
41 col_auths_,
42 col_deauths_,
43 col_others_,
44 col_protection_
45 };
46
47 enum {
48 wlan_network_row_type_ = 1000,
49 wlan_station_row_type_
50 };
51
52 class WlanStationTreeWidgetItem : public QTreeWidgetItem
53 {
54 public:
WlanStationTreeWidgetItem(const address * addr)55 WlanStationTreeWidgetItem(const address *addr) :
56 QTreeWidgetItem (wlan_station_row_type_),
57 packets_(0),
58 retry_(0),
59 sent_(0),
60 received_(0),
61 probe_req_(0),
62 probe_resp_(0),
63 auth_(0),
64 deauth_(0),
65 other_(0)
66 {
67 copy_address(&addr_, addr);
68 setText(col_bssid_, address_to_qstring(&addr_));
69 }
isMatch(const address * addr)70 bool isMatch(const address *addr) {
71 return addresses_equal(&addr_, addr);
72 }
update(const wlan_hdr_t * wlan_hdr)73 void update(const wlan_hdr_t *wlan_hdr) {
74 bool is_sender = addresses_equal(&addr_, &wlan_hdr->src);
75
76 if (wlan_hdr->stats.fc_retry != 0) {
77 retry_++;
78 }
79
80 // XXX Should we count received probes and auths? This is what the
81 // GTK+ UI does, but it seems odd.
82 switch (wlan_hdr->type) {
83 case MGT_PROBE_REQ:
84 probe_req_++;
85 break;
86 case MGT_PROBE_RESP:
87 probe_resp_++;
88 break;
89 case MGT_BEACON:
90 // Skip
91 break;
92 case MGT_AUTHENTICATION:
93 auth_++;
94 break;
95 case MGT_DEAUTHENTICATION:
96 deauth_++;
97 break;
98 case DATA:
99 case DATA_CF_ACK:
100 case DATA_CF_POLL:
101 case DATA_CF_ACK_POLL:
102 case DATA_QOS_DATA:
103 case DATA_QOS_DATA_CF_ACK:
104 case DATA_QOS_DATA_CF_POLL:
105 case DATA_QOS_DATA_CF_ACK_POLL:
106 if (is_sender) {
107 sent_++;
108 } else {
109 received_++;
110 }
111 break;
112 default:
113 other_++;
114 break;
115 }
116 if (wlan_hdr->type != MGT_BEACON) packets_++;
117 }
draw(address * bssid,int num_packets)118 void draw(address *bssid, int num_packets) {
119 if (packets_ && num_packets > 0) {
120 setData(col_pct_packets_, Qt::UserRole, QVariant::fromValue<double>(packets_ * 100.0 / num_packets));
121 setData(col_pct_retry_, Qt::UserRole, QVariant::fromValue<double>(retry_ * 100.0 / packets_));
122 } else {
123 setData(col_pct_packets_, Qt::UserRole, QVariant::fromValue<double>(0));
124 setData(col_pct_retry_, Qt::UserRole, QVariant::fromValue<double>(0));
125 }
126 setText(col_beacons_, QString::number(sent_));
127 setText(col_data_packets_, QString::number(received_));
128 setText(col_retry_packets_, QString::number(retry_));
129 setText(col_probe_reqs_, QString::number(probe_req_));
130 setText(col_probe_resps_, QString::number(probe_resp_));
131 setText(col_auths_, QString::number(auth_));
132 setText(col_deauths_, QString::number(deauth_));
133 setText(col_others_, QString::number(other_));
134
135 if (!is_broadcast_bssid(bssid) && addresses_data_equal(&addr_, bssid)) {
136 setText(col_protection_, QObject::tr("Base station"));
137 }
138 }
operator <(const QTreeWidgetItem & other) const139 bool operator< (const QTreeWidgetItem &other) const
140 {
141 if (other.type() != wlan_station_row_type_) return QTreeWidgetItem::operator< (other);
142 const WlanStationTreeWidgetItem *other_row = static_cast<const WlanStationTreeWidgetItem *>(&other);
143
144 switch (treeWidget()->sortColumn()) {
145 case col_bssid_:
146 return cmp_address(&addr_, &other_row->addr_) < 0;
147 case col_pct_packets_:
148 return packets_ < other_row->packets_;
149 case col_beacons_:
150 return sent_ < other_row->sent_;
151 case col_data_packets_:
152 return received_ < other_row->received_;
153 case col_probe_reqs_:
154 return probe_req_ < other_row->probe_req_;
155 case col_probe_resps_:
156 return probe_resp_ < other_row->probe_resp_;
157 case col_auths_:
158 return auth_ < other_row->auth_;
159 case col_deauths_:
160 return deauth_ < other_row->deauth_;
161 case col_others_:
162 return other_ < other_row->other_;
163 case col_retry_packets_:
164 case col_pct_retry_:
165 return retry_ < other_row->retry_;
166 default:
167 break;
168 }
169
170 return QTreeWidgetItem::operator< (other);
171 }
rowData()172 QList<QVariant> rowData() {
173 return QList<QVariant>()
174 << address_to_qstring(&addr_)
175 << data(col_pct_packets_, Qt::UserRole).toDouble()
176 << data(col_pct_retry_, Qt::UserRole).toDouble() << retry_
177 << sent_ << received_ << probe_req_ << probe_resp_
178 << auth_ << deauth_ << other_ << text(col_protection_);
179 }
filterExpression()180 const QString filterExpression() {
181 QString filter_expr = QString("wlan.addr==%1")
182 .arg(address_to_qstring(&addr_));
183 return filter_expr;
184 }
185
186 private:
187 address addr_;
188 int packets_;
189 int retry_;
190 int sent_;
191 int received_;
192 int probe_req_;
193 int probe_resp_;
194 int auth_;
195 int deauth_;
196 int other_;
197
198 };
199
200 class WlanNetworkTreeWidgetItem : public QTreeWidgetItem
201 {
202 public:
WlanNetworkTreeWidgetItem(QTreeWidget * parent,const wlan_hdr_t * wlan_hdr)203 WlanNetworkTreeWidgetItem(QTreeWidget *parent, const wlan_hdr_t *wlan_hdr) :
204 QTreeWidgetItem (parent, wlan_network_row_type_),
205 beacon_(0),
206 data_packet_(0),
207 retry_packet_(0),
208 probe_req_(0),
209 probe_resp_(0),
210 auth_(0),
211 deauth_(0),
212 other_(0),
213 packets_(0)
214 {
215 updateBssid(wlan_hdr);
216 channel_ = wlan_hdr->stats.channel;
217 ssid_ = QByteArray::fromRawData((const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len);
218 QString ssid_text;
219
220 if (wlan_hdr->stats.ssid_len == 0) {
221 ssid_text = QObject::tr("<Broadcast>");
222 } else if (wlan_hdr->stats.ssid_len == 1 && wlan_hdr->stats.ssid[0] == 0) {
223 ssid_text = QObject::tr("<Hidden>");
224 } else {
225 gchar *str = format_text(NULL, wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len);
226 ssid_text = str;
227 wmem_free(NULL, str);
228 }
229
230 setText(col_ssid_, ssid_text);
231 }
232
isMatch(const wlan_hdr_t * wlan_hdr)233 bool isMatch(const wlan_hdr_t *wlan_hdr) {
234 bool is_bssid_match = false;
235 bool is_ssid_match = false;
236 bool update_bssid = false;
237 bool update_ssid = false;
238 // We want (but might not have) a unicast BSSID and a named SSID. Try
239 // to match the current packet and update our information if possible.
240
241 if (addresses_equal(&bssid_, &wlan_hdr->bssid)) {
242 is_bssid_match = true;
243 }
244
245 if ((wlan_hdr->stats.ssid_len > 0) && (wlan_hdr->stats.ssid[0] != 0)) {
246 QByteArray hdr_ssid = QByteArray::fromRawData((const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len);
247 if (ssid_ == hdr_ssid) {
248 is_ssid_match = true;
249 }
250 }
251
252 if (is_bssid_match && is_ssid_match) return true;
253
254 // Probe requests.
255 if (wlan_hdr->type == MGT_PROBE_REQ) {
256 // Probes with visible SSIDs. Unicast or broadcast.
257 if (is_ssid_match) {
258 if (is_broadcast_ && !is_broadcast_bssid(&wlan_hdr->bssid)) {
259 update_bssid = true;
260 }
261 // Probes with hidden SSIDs. Unicast.
262 } else if ((wlan_hdr->stats.ssid_len == 1) && (wlan_hdr->stats.ssid[0] == 0)) {
263 if (!is_broadcast_ && addresses_equal(&bssid_, &wlan_hdr->bssid)) {
264 is_bssid_match = true;
265 update_ssid = true;
266 }
267 // Probes with no SSID. Broadcast.
268 } else if (ssid_.isEmpty() && wlan_hdr->stats.ssid_len < 1) {
269 if (is_broadcast_ && is_broadcast_bssid(&wlan_hdr->bssid)) {
270 return true;
271 }
272 }
273 // Non-probe requests (responses, beacons, etc)
274 } else {
275 if (is_ssid_match) {
276 if (is_broadcast_ && !is_broadcast_bssid(&wlan_hdr->bssid)) {
277 update_bssid = true;
278 }
279 } else if (wlan_hdr->stats.ssid_len < 1) {
280 // No SSID.
281 is_ssid_match = true;
282 }
283 if (is_bssid_match) {
284 if ((ssid_.isEmpty() || ssid_[0] == '\0') && (wlan_hdr->stats.ssid_len > 0) && (wlan_hdr->stats.ssid[0] != 0)) {
285 update_ssid = true;
286 }
287 }
288 }
289
290 if (update_bssid) {
291 updateBssid(wlan_hdr);
292 is_bssid_match = true;
293 }
294
295 if (update_ssid) {
296 gchar* str;
297 ssid_ = QByteArray::fromRawData((const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len);
298 str = format_text(NULL, wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len);
299 setText(col_ssid_, str);
300 wmem_free(NULL, str);
301 is_ssid_match = true;
302 }
303
304 return is_bssid_match && is_ssid_match;
305 }
306
update(const wlan_hdr_t * wlan_hdr)307 void update(const wlan_hdr_t *wlan_hdr) {
308 if (channel_ == 0 && wlan_hdr->stats.channel != 0) {
309 channel_ = wlan_hdr->stats.channel;
310 }
311 if (text(col_protection_).isEmpty() && wlan_hdr->stats.protection[0] != 0) {
312 setText(col_protection_, wlan_hdr->stats.protection);
313 }
314 if (wlan_hdr->stats.fc_retry != 0) {
315 retry_packet_++;
316 }
317
318 switch (wlan_hdr->type) {
319 case MGT_PROBE_REQ:
320 probe_req_++;
321 break;
322 case MGT_PROBE_RESP:
323 probe_resp_++;
324 break;
325 case MGT_BEACON:
326 beacon_++;
327 break;
328 case MGT_AUTHENTICATION:
329 auth_++;
330 break;
331 case MGT_DEAUTHENTICATION:
332 deauth_++;
333 break;
334 case DATA:
335 case DATA_CF_ACK:
336 case DATA_CF_POLL:
337 case DATA_CF_ACK_POLL:
338 case DATA_QOS_DATA:
339 case DATA_QOS_DATA_CF_ACK:
340 case DATA_QOS_DATA_CF_POLL:
341 case DATA_QOS_DATA_CF_ACK_POLL:
342 data_packet_++;
343 break;
344 default:
345 other_++;
346 break;
347 }
348 packets_++;
349
350 WlanStationTreeWidgetItem* sender_ws_ti = NULL;
351 WlanStationTreeWidgetItem* receiver_ws_ti = NULL;
352 foreach (QTreeWidgetItem *cur_ti, stations_) {
353 WlanStationTreeWidgetItem *cur_ws_ti = dynamic_cast<WlanStationTreeWidgetItem *>(cur_ti);
354 if (cur_ws_ti && (cur_ws_ti->isMatch(&wlan_hdr->src))) sender_ws_ti = cur_ws_ti;
355 if (cur_ws_ti && (cur_ws_ti->isMatch(&wlan_hdr->dst))) receiver_ws_ti = cur_ws_ti;
356 if (sender_ws_ti && receiver_ws_ti) break;
357 }
358 if (!sender_ws_ti) {
359 sender_ws_ti = new WlanStationTreeWidgetItem(&wlan_hdr->src);
360 stations_ << sender_ws_ti;
361 }
362 if (!receiver_ws_ti) {
363 receiver_ws_ti = new WlanStationTreeWidgetItem(&wlan_hdr->dst);
364 stations_ << receiver_ws_ti;
365 }
366 sender_ws_ti->update(wlan_hdr);
367 receiver_ws_ti->update(wlan_hdr);
368 }
369
draw(int num_packets)370 void draw(int num_packets) {
371 if (channel_ > 0) setText(col_channel_, QString::number(channel_));
372 setData(col_pct_packets_, Qt::UserRole, QVariant::fromValue<double>(packets_ * 100.0 / num_packets));
373 setData(col_pct_retry_, Qt::UserRole, QVariant::fromValue<double>(retry_packet_ * 100.0 / packets_));
374 setText(col_retry_packets_, QString::number(retry_packet_));
375 setText(col_beacons_, QString::number(beacon_));
376 setText(col_data_packets_, QString::number(data_packet_));
377 setText(col_probe_reqs_, QString::number(probe_req_));
378 setText(col_probe_resps_, QString::number(probe_resp_));
379 setText(col_auths_, QString::number(auth_));
380 setText(col_deauths_, QString::number(deauth_));
381 setText(col_others_, QString::number(other_));
382 }
383
addStations()384 void addStations() {
385 foreach (QTreeWidgetItem *cur_ti, stations_) {
386 WlanStationTreeWidgetItem *cur_ws_ti = dynamic_cast<WlanStationTreeWidgetItem *>(cur_ti);
387 cur_ws_ti->draw(&bssid_, packets_ - beacon_);
388 for (int col = 0; col < treeWidget()->columnCount(); col++) {
389 cur_ws_ti->setTextAlignment(col, treeWidget()->headerItem()->textAlignment(col));
390 }
391 }
392
393 addChildren(stations_);
394 stations_.clear();
395 }
396
operator <(const QTreeWidgetItem & other) const397 bool operator< (const QTreeWidgetItem &other) const
398 {
399 if (other.type() != wlan_network_row_type_) return QTreeWidgetItem::operator< (other);
400 const WlanNetworkTreeWidgetItem *other_row = static_cast<const WlanNetworkTreeWidgetItem *>(&other);
401
402 switch (treeWidget()->sortColumn()) {
403 case col_bssid_:
404 return cmp_address(&bssid_, &other_row->bssid_) < 0;
405 case col_channel_:
406 return channel_ < other_row->channel_;
407 case col_ssid_:
408 return ssid_ < other_row->ssid_;
409 case col_pct_packets_:
410 return packets_ < other_row->packets_;
411 case col_beacons_:
412 return beacon_ < other_row->beacon_;
413 case col_data_packets_:
414 return data_packet_ < other_row->data_packet_;
415 case col_probe_reqs_:
416 return probe_req_ < other_row->probe_req_;
417 case col_probe_resps_:
418 return probe_resp_ < other_row->probe_resp_;
419 case col_auths_:
420 return auth_ < other_row->auth_;
421 case col_deauths_:
422 return deauth_ < other_row->deauth_;
423 case col_others_:
424 return other_ < other_row->other_;
425 case col_protection_:
426 default:
427 break;
428 }
429
430 return QTreeWidgetItem::operator< (other);
431 }
rowData()432 QList<QVariant> rowData() {
433 return QList<QVariant>()
434 << address_to_qstring(&bssid_) << channel_ << text(col_ssid_)
435 << data(col_pct_packets_, Qt::UserRole).toDouble()
436 << data(col_pct_retry_, Qt::UserRole).toDouble()
437 << retry_packet_ << beacon_ << data_packet_ << probe_req_
438 << probe_resp_ << auth_ << deauth_ << other_
439 << text(col_protection_);
440 }
441
filterExpression()442 const QString filterExpression() {
443 QString filter_expr = QString("(wlan.bssid==%1")
444 .arg(address_to_qstring(&bssid_));
445 if (!ssid_.isEmpty() && ssid_[0] != '\0') {
446 filter_expr += QString(" || wlan.ssid==\"%1\"")
447 .arg(ssid_.constData());
448 }
449 filter_expr += ")";
450 return filter_expr;
451 }
452
453 private:
454 address bssid_;
455 bool is_broadcast_;
456 int channel_;
457 QByteArray ssid_;
458 int beacon_;
459 int data_packet_;
460 int retry_packet_;
461 int probe_req_;
462 int probe_resp_;
463 int auth_;
464 int deauth_;
465 int other_;
466 int packets_;
467
468 // Adding items one at a time is slow. Gather up the stations in a list
469 // and add them all at once later.
470 QList<QTreeWidgetItem *>stations_;
471
updateBssid(const wlan_hdr_t * wlan_hdr)472 void updateBssid(const wlan_hdr_t *wlan_hdr) {
473 copy_address(&bssid_, &wlan_hdr->bssid);
474 is_broadcast_ = is_broadcast_bssid(&bssid_);
475 setText(col_bssid_, address_to_qstring(&bssid_));
476 }
477 };
478
479 static const QString network_col_0_title_ = QObject::tr("BSSID");
480 static const QString network_col_6_title_ = QObject::tr("Beacons");
481 static const QString network_col_7_title_ = QObject::tr("Data Pkts");
482 static const QString network_col_13_title_ = QObject::tr("Protection");
483
484 static const QString node_col_0_title_ = QObject::tr("Address");
485 static const QString node_col_4_title_ = QObject::tr("Pkts Sent");
486 static const QString node_col_5_title_ = QObject::tr("Pkts Received");
487 static const QString node_col_11_title_ = QObject::tr("Comment");
488
WlanStatisticsDialog(QWidget & parent,CaptureFile & cf,const char * filter)489 WlanStatisticsDialog::WlanStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter) :
490 TapParameterDialog(parent, cf, HELP_STATS_WLAN_TRAFFIC_DIALOG),
491 packet_count_(0),
492 cur_network_(0),
493 add_station_timer_(0)
494 {
495 setWindowSubtitle(tr("Wireless LAN Statistics"));
496 loadGeometry(parent.width() * 4 / 5, parent.height() * 3 / 4, "WlanStatisticsDialog");
497
498 QStringList header_labels = QStringList()
499 << "" << tr("Channel") << tr("SSID") << tr("Percent Packets") << tr("Percent Retry")
500 << tr("Retry") << "" << "" << tr("Probe Reqs") << tr("Probe Resp") << tr("Auths")
501 << tr("Deauths") << tr("Other");
502 statsTreeWidget()->setHeaderLabels(header_labels);
503 updateHeaderLabels();
504 packets_delegate_ = new PercentBarDelegate();
505 statsTreeWidget()->setItemDelegateForColumn(col_pct_packets_, packets_delegate_);
506 retry_delegate_ = new PercentBarDelegate();
507 statsTreeWidget()->setItemDelegateForColumn(col_pct_retry_, retry_delegate_);
508 statsTreeWidget()->sortByColumn(col_bssid_, Qt::AscendingOrder);
509
510 // resizeColumnToContents doesn't work well here, so set sizes manually.
511 int one_em = fontMetrics().height();
512 for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) {
513 switch (col) {
514 case col_bssid_:
515 statsTreeWidget()->setColumnWidth(col, one_em * 11);
516 break;
517 case col_ssid_:
518 statsTreeWidget()->setColumnWidth(col, one_em * 8);
519 break;
520 case col_pct_packets_:
521 case col_pct_retry_:
522 case col_protection_:
523 statsTreeWidget()->setColumnWidth(col, one_em * 6);
524 break;
525 default:
526 // The rest are numeric
527 statsTreeWidget()->setColumnWidth(col, one_em * 4);
528 statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight);
529 break;
530 }
531 }
532
533 addFilterActions();
534
535 if (filter) {
536 setDisplayFilter(filter);
537 }
538
539 add_station_timer_ = new QElapsedTimer();
540
541 connect(statsTreeWidget(), SIGNAL(itemSelectionChanged()),
542 this, SLOT(updateHeaderLabels()));
543
544 // Set handler for when display filter string is changed.
545 connect(this, SIGNAL(updateFilter(QString)),
546 this, SLOT(filterUpdated(QString)));
547 }
548
~WlanStatisticsDialog()549 WlanStatisticsDialog::~WlanStatisticsDialog()
550 {
551 delete packets_delegate_;
552 delete retry_delegate_;
553 delete add_station_timer_;
554 }
555
tapReset(void * ws_dlg_ptr)556 void WlanStatisticsDialog::tapReset(void *ws_dlg_ptr)
557 {
558 WlanStatisticsDialog *ws_dlg = static_cast<WlanStatisticsDialog *>(ws_dlg_ptr);
559 if (!ws_dlg) return;
560
561 ws_dlg->statsTreeWidget()->clear();
562 ws_dlg->packet_count_ = 0;
563 }
564
tapPacket(void * ws_dlg_ptr,_packet_info *,epan_dissect *,const void * wlan_hdr_ptr)565 tap_packet_status WlanStatisticsDialog::tapPacket(void *ws_dlg_ptr, _packet_info *, epan_dissect *, const void *wlan_hdr_ptr)
566 {
567 WlanStatisticsDialog *ws_dlg = static_cast<WlanStatisticsDialog *>(ws_dlg_ptr);
568 const wlan_hdr_t *wlan_hdr = (const wlan_hdr_t *)wlan_hdr_ptr;
569 if (!ws_dlg || !wlan_hdr) return TAP_PACKET_DONT_REDRAW;
570
571 guint16 frame_type = wlan_hdr->type & 0xff0;
572 if (!((frame_type == 0x0) || (frame_type == 0x20) || (frame_type == 0x30))
573 || ((frame_type == 0x20) && DATA_FRAME_IS_NULL(wlan_hdr->type))) {
574 /* Not a management or non null data or extension frame; let's skip it */
575 return TAP_PACKET_DONT_REDRAW;
576 }
577
578 ws_dlg->packet_count_++;
579
580 // XXX This is very slow for large numbers of networks. We might be
581 // able to store networks in a cache keyed on BSSID+SSID instead.
582 WlanNetworkTreeWidgetItem *wn_ti = NULL;
583 for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) {
584 QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i);
585 if (ti->type() != wlan_network_row_type_) continue;
586 WlanNetworkTreeWidgetItem *cur_wn_ti = static_cast<WlanNetworkTreeWidgetItem*>(ti);
587
588 if (cur_wn_ti->isMatch(wlan_hdr)) {
589 wn_ti = cur_wn_ti;
590 break;
591 }
592 }
593
594 if (!wn_ti) {
595 wn_ti = new WlanNetworkTreeWidgetItem(ws_dlg->statsTreeWidget(), wlan_hdr);
596 for (int col = 0; col < ws_dlg->statsTreeWidget()->columnCount(); col++) {
597 wn_ti->setTextAlignment(col, ws_dlg->statsTreeWidget()->headerItem()->textAlignment(col));
598 }
599 }
600
601 wn_ti->update(wlan_hdr);
602 return TAP_PACKET_REDRAW;
603 }
604
tapDraw(void * ws_dlg_ptr)605 void WlanStatisticsDialog::tapDraw(void *ws_dlg_ptr)
606 {
607 WlanStatisticsDialog *ws_dlg = static_cast<WlanStatisticsDialog *>(ws_dlg_ptr);
608 if (!ws_dlg) return;
609
610 for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) {
611 QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i);
612 if (ti->type() != wlan_network_row_type_) continue;
613
614 WlanNetworkTreeWidgetItem *wn_ti = static_cast<WlanNetworkTreeWidgetItem*>(ti);
615 wn_ti->draw(ws_dlg->packet_count_);
616 }
617 }
618
filterExpression()619 const QString WlanStatisticsDialog::filterExpression()
620 {
621 QString filter_expr;
622 if (statsTreeWidget()->selectedItems().count() > 0) {
623 QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
624
625 if (ti->type() == wlan_network_row_type_) {
626 WlanNetworkTreeWidgetItem *wn_ti = static_cast<WlanNetworkTreeWidgetItem*>(ti);
627 filter_expr = wn_ti->filterExpression();
628 } else if (ti->type() == wlan_station_row_type_) {
629 WlanStationTreeWidgetItem *ws_ti = static_cast<WlanStationTreeWidgetItem*>(ti);
630 filter_expr = ws_ti->filterExpression();
631 }
632 }
633 return filter_expr;
634 }
635
fillTree()636 void WlanStatisticsDialog::fillTree()
637 {
638 if (!registerTapListener("wlan",
639 this,
640 displayFilter_.toLatin1().data(),
641 TL_REQUIRES_NOTHING,
642 tapReset,
643 tapPacket,
644 tapDraw)) {
645 reject();
646 return;
647 }
648
649 statsTreeWidget()->setSortingEnabled(false);
650 cap_file_.retapPackets();
651 tapDraw(this);
652 removeTapListeners();
653 statsTreeWidget()->setSortingEnabled(true);
654
655 // Don't freeze if we have a large number of stations.
656 cur_network_ = 0;
657 QTimer::singleShot(0, this, SLOT(addStationTreeItems()));
658 }
659
660 static const int add_station_interval_ = 5; // ms
addStationTreeItems()661 void WlanStatisticsDialog::addStationTreeItems()
662 {
663 add_station_timer_->start();
664 while (add_station_timer_->elapsed() < add_station_interval_ && cur_network_ < statsTreeWidget()->topLevelItemCount()) {
665 QTreeWidgetItem *ti = statsTreeWidget()->topLevelItem(cur_network_);
666 if (ti->type() != wlan_network_row_type_) continue;
667
668 WlanNetworkTreeWidgetItem *wn_ti = static_cast<WlanNetworkTreeWidgetItem*>(ti);
669 wn_ti->addStations();
670 ++cur_network_;
671 }
672
673 if (cur_network_ < statsTreeWidget()->topLevelItemCount()) {
674 QTimer::singleShot(0, this, SLOT(addStationTreeItems()));
675 }
676 }
677
updateHeaderLabels()678 void WlanStatisticsDialog::updateHeaderLabels()
679 {
680 if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == wlan_station_row_type_) {
681 statsTreeWidget()->headerItem()->setText(col_bssid_, node_col_0_title_);
682 statsTreeWidget()->headerItem()->setText(col_beacons_, node_col_4_title_);
683 statsTreeWidget()->headerItem()->setText(col_data_packets_, node_col_5_title_);
684 statsTreeWidget()->headerItem()->setText(col_protection_, node_col_11_title_);
685 } else {
686 statsTreeWidget()->headerItem()->setText(col_bssid_, network_col_0_title_);
687 statsTreeWidget()->headerItem()->setText(col_beacons_, network_col_6_title_);
688 statsTreeWidget()->headerItem()->setText(col_data_packets_, network_col_7_title_);
689 statsTreeWidget()->headerItem()->setText(col_protection_, network_col_13_title_);
690 }
691 }
692
captureFileClosing()693 void WlanStatisticsDialog::captureFileClosing()
694 {
695 remove_tap_listener(this);
696
697 WiresharkDialog::captureFileClosing();
698 }
699
700 // Store filter from signal.
filterUpdated(QString filter)701 void WlanStatisticsDialog::filterUpdated(QString filter)
702 {
703 displayFilter_ = filter;
704 }
705
706 // This is how an item is represented for exporting.
treeItemData(QTreeWidgetItem * it) const707 QList<QVariant> WlanStatisticsDialog::treeItemData(QTreeWidgetItem *it) const
708 {
709 // Cast up to our type.
710 WlanNetworkTreeWidgetItem *nit = dynamic_cast<WlanNetworkTreeWidgetItem*>(it);
711 if (nit) {
712 return nit->rowData();
713 }
714 // TODO: not going to cast to WlanStationTreeWidgetItem* and do the same as
715 // some of the columns are different...
716
717 return QList<QVariant>();
718 }
719
720 // Stat command + args
721
722 static void
wlan_statistics_init(const char * args,void *)723 wlan_statistics_init(const char *args, void*) {
724 QStringList args_l = QString(args).split(',');
725 QByteArray filter;
726 if (args_l.length() > 2) {
727 filter = QStringList(args_l.mid(2)).join(",").toUtf8();
728 }
729 wsApp->emitStatCommandSignal("WlanStatistics", filter.constData(), NULL);
730 }
731
732 static stat_tap_ui wlan_statistics_ui = {
733 REGISTER_STAT_GROUP_GENERIC,
734 NULL,
735 "wlan,stat",
736 wlan_statistics_init,
737 0,
738 NULL
739 };
740
741 extern "C" {
742
743 void register_tap_listener_qt_wlan_statistics(void);
744
745 void
register_tap_listener_qt_wlan_statistics(void)746 register_tap_listener_qt_wlan_statistics(void)
747 {
748 register_stat_tap_ui(&wlan_statistics_ui, NULL);
749 }
750
751 }
752