3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
10 #include "wireless_frame.h"
11 #include <ui_wireless_frame.h>
15 #include <capture/capture_session.h>
16 #include <capture/capture_sync.h>
18 #include <capture/ws80211_utils.h>
20 #include "ui/ws_ui_util.h"
21 #include <wsutil/utf8_entities.h>
22 #include <wsutil/802_11-utils.h>
23 #include "main_application.h"
24 #include "utils/qt_ui_utils.h"
27 #include <QAbstractItemView>
30 // - Disable or hide invalid channel types.
31 // - Push more status messages ("switched to...") to the status bar.
32 // - Add a "Decrypt in the driver" checkbox?
33 // - Check for frequency and channel type changes.
34 // - Find something appropriate to run from the helperToolButton on Linux.
37 // - From our perspective, what's the difference between "NOHT" and "HT20"?
39 const int update_interval_
= 1500; // ms
41 WirelessFrame::WirelessFrame(QWidget
*parent
) :
43 ui(new Ui::WirelessFrame
),
45 capture_in_progress_(false),
50 ui
->helperToolButton
->hide();
52 if (ws80211_init() == WS80211_INIT_OK
) {
53 ui
->stackedWidget
->setEnabled(true);
54 ui
->stackedWidget
->setCurrentWidget(ui
->interfacePage
);
56 ui
->stackedWidget
->setEnabled(false);
57 ui
->stackedWidget
->setCurrentWidget(ui
->noWirelessPage
);
60 ui
->fcsFilterFrame
->setVisible(ws80211_has_fcs_filter());
62 updateInterfaceList();
63 connect(mainApp
, &MainApplication::localInterfaceEvent
,
64 this, &WirelessFrame::handleInterfaceEvent
);
67 WirelessFrame::~WirelessFrame()
69 ws80211_free_interfaces(interfaces_
);
73 void WirelessFrame::setCaptureInProgress(bool capture_in_progress
)
75 capture_in_progress_
= capture_in_progress
;
80 int WirelessFrame::startTimer(int interval
)
82 if (iface_timer_id_
!= -1) {
83 killTimer(iface_timer_id_
);
86 iface_timer_id_
= QFrame::startTimer(interval
);
87 return iface_timer_id_
;
90 void WirelessFrame::handleInterfaceEvent(const char *ifname _U_
, int added
, int up _U_
)
93 // Unfortunately when an interface removed event is received the network
94 // interface is still present for a while in the system.
95 // To overcome this update the interface list after a while.
96 startTimer(update_interval_
);
98 updateInterfaceList();
102 void WirelessFrame::timerEvent(QTimerEvent
*event
)
104 if (event
->timerId() != iface_timer_id_
) {
105 QFrame::timerEvent(event
);
108 killTimer(iface_timer_id_
);
109 iface_timer_id_
= -1;
110 updateInterfaceList();
113 // Check to see if the ws80211 interface list matches the one in our
114 // combobox. Rebuild ours if necessary and select the first interface if
115 // the current selection goes away.
116 void WirelessFrame::updateInterfaceList()
118 ws80211_free_interfaces(interfaces_
);
119 interfaces_
= ws80211_find_interfaces();
120 const QString old_iface
= ui
->interfaceComboBox
->currentText();
121 unsigned iface_count
= 0;
122 bool list_changed
= false;
124 // Don't interfere with user activity.
125 if (ui
->interfaceComboBox
->view()->isVisible()
126 || ui
->channelComboBox
->view()->isVisible()
127 || ui
->channelTypeComboBox
->view()->isVisible()
128 || ui
->fcsComboBox
->view()->isVisible()) {
129 startTimer(update_interval_
);
133 if (interfaces_
&& interfaces_
->len
> 0) {
134 iface_count
= interfaces_
->len
;
137 if ((int) iface_count
!= ui
->interfaceComboBox
->count()) {
140 for (unsigned i
= 0; i
< iface_count
; i
++) {
141 struct ws80211_interface
*iface
= g_array_index(interfaces_
, struct ws80211_interface
*, i
);
142 if (ui
->interfaceComboBox
->itemText(i
).compare(iface
->ifname
) != 0) {
150 ui
->interfaceComboBox
->clear();
151 for (unsigned i
= 0; i
< iface_count
; i
++) {
152 struct ws80211_interface
*iface
= g_array_index(interfaces_
, struct ws80211_interface
*, i
);
153 ui
->interfaceComboBox
->addItem(iface
->ifname
);
154 if (old_iface
.compare(iface
->ifname
) == 0) {
155 ui
->interfaceComboBox
->setCurrentIndex(ui
->interfaceComboBox
->count() - 1);
160 if (ui
->interfaceComboBox
->currentText().compare(old_iface
) != 0) {
165 void WirelessFrame::updateWidgets()
167 bool enable_interface
= false;
168 bool enable_channel
= false;
169 bool enable_offset
= false;
170 bool enable_show_fcs
= false;
172 if (ui
->interfaceComboBox
->count() > 0) {
173 enable_interface
= true;
174 enable_show_fcs
= true;
177 if (enable_interface
&& ui
->channelComboBox
->count() > 0) {
178 enable_channel
= true;
181 if (enable_channel
&& ui
->channelTypeComboBox
->count() > 1) {
182 enable_offset
= true;
185 ui
->interfaceComboBox
->setEnabled(enable_interface
);
186 ui
->channelComboBox
->setEnabled(enable_channel
);
187 ui
->channelTypeComboBox
->setEnabled(enable_offset
);
188 ui
->fcsComboBox
->setEnabled(!capture_in_progress_
&& enable_show_fcs
);
191 void WirelessFrame::on_helperToolButton_clicked()
193 const QString helper_path
= ws80211_get_helper_path();
194 if (helper_path
.isEmpty()) return;
196 QString command
= QStringLiteral("\"%1\"").arg(helper_path
);
197 QProcess::startDetached(command
, QStringList());
200 void WirelessFrame::on_prefsToolButton_clicked()
202 emit
showWirelessPreferences("wlan");
205 void WirelessFrame::getInterfaceInfo()
207 const QString cur_iface
= ui
->interfaceComboBox
->currentText();
209 ui
->channelComboBox
->clear();
210 ui
->channelTypeComboBox
->clear();
211 ui
->fcsComboBox
->clear();
213 if (cur_iface
.isEmpty()) {
218 for (unsigned i
= 0; i
< interfaces_
->len
; i
++) {
219 struct ws80211_interface
*iface
= g_array_index(interfaces_
, struct ws80211_interface
*, i
);
220 if (cur_iface
.compare(iface
->ifname
) == 0) {
221 struct ws80211_iface_info iface_info
;
222 QString units
= " GHz";
224 ws80211_get_iface_info(iface
->ifname
, &iface_info
);
226 for (unsigned j
= 0; j
< iface
->frequencies
->len
; j
++) {
227 uint32_t frequency
= g_array_index(iface
->frequencies
, uint32_t, j
);
228 double ghz
= frequency
/ 1000.0;
229 QString chan_str
= QStringLiteral("%1 %2 %3%4")
230 .arg(ieee80211_mhz_to_chan(frequency
))
231 .arg(UTF8_MIDDLE_DOT
)
234 ui
->channelComboBox
->addItem(chan_str
, frequency
);
235 if ((int)frequency
== iface_info
.current_freq
) {
236 ui
->channelComboBox
->setCurrentIndex(ui
->channelComboBox
->count() - 1);
240 // XXX - Do we need to make a distinction between WS80211_CHAN_NO_HT
241 // and WS80211_CHAN_HT20? E.g. is there a driver that won't capture
242 // HT frames if you use WS80211_CHAN_NO_HT?
243 ui
->channelTypeComboBox
->addItem("20 MHz", WS80211_CHAN_NO_HT
);
244 if (iface_info
.current_chan_type
== WS80211_CHAN_NO_HT
|| iface_info
.current_chan_type
== WS80211_CHAN_HT20
) {
245 ui
->channelTypeComboBox
->setCurrentIndex(0);
247 if (iface
->channel_types
& (1 << WS80211_CHAN_HT40MINUS
)) {
248 ui
->channelTypeComboBox
->addItem("HT 40-", WS80211_CHAN_HT40MINUS
);
249 if (iface_info
.current_chan_type
== WS80211_CHAN_HT40MINUS
) {
250 ui
->channelTypeComboBox
->setCurrentIndex(ui
->channelTypeComboBox
->count() - 1);
253 if (iface
->channel_types
& (1 << WS80211_CHAN_HT40PLUS
)) {
254 ui
->channelTypeComboBox
->addItem("HT 40+", WS80211_CHAN_HT40PLUS
);
255 if (iface_info
.current_chan_type
== WS80211_CHAN_HT40PLUS
) {
256 ui
->channelTypeComboBox
->setCurrentIndex(ui
->channelTypeComboBox
->count() - 1);
259 if (iface
->channel_types
& (1 << WS80211_CHAN_VHT80
)) {
260 ui
->channelTypeComboBox
->addItem("VHT 80", WS80211_CHAN_VHT80
);
261 if (iface_info
.current_chan_type
== WS80211_CHAN_VHT80
) {
262 ui
->channelTypeComboBox
->setCurrentIndex(ui
->channelTypeComboBox
->count() - 1);
265 if (iface
->channel_types
& (1 << WS80211_CHAN_VHT160
)) {
266 ui
->channelTypeComboBox
->addItem("VHT 160", WS80211_CHAN_VHT160
);
267 if (iface_info
.current_chan_type
== WS80211_CHAN_VHT160
) {
268 ui
->channelTypeComboBox
->setCurrentIndex(ui
->channelTypeComboBox
->count() - 1);
272 if (ws80211_has_fcs_filter()) {
273 ui
->fcsComboBox
->setCurrentIndex(iface_info
.current_fcs_validation
);
281 void WirelessFrame::setInterfaceInfo()
283 QString cur_iface
= ui
->interfaceComboBox
->currentText();
284 int cur_chan_idx
= ui
->channelComboBox
->currentIndex();
285 int cur_type_idx
= ui
->channelTypeComboBox
->currentIndex();
286 int cur_fcs_idx
= ui
->fcsComboBox
->currentIndex();
288 if (cur_iface
.isEmpty() || cur_chan_idx
< 0 || cur_type_idx
< 0) return;
292 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211) && defined(HAVE_LIBPCAP)
293 int frequency
= ui
->channelComboBox
->itemData(cur_chan_idx
).toInt();
294 int chan_type
= ui
->channelTypeComboBox
->itemData(cur_type_idx
).toInt();
295 int bandwidth
= getBandwidthFromChanType(chan_type
);
296 int center_freq
= getCenterFrequency(frequency
, bandwidth
);
297 const char *chan_type_s
= ws80211_chan_type_to_str(chan_type
);
298 char *center_freq_s
= NULL
;
299 char *data
, *primary_msg
, *secondary_msg
;
302 if (frequency
< 0 || chan_type
< 0) return;
304 if (center_freq
!= -1) {
305 center_freq_s
= qstring_strdup(QString::number(center_freq
));
308 ret
= sync_interface_set_80211_chan(cur_iface
.toUtf8().constData(),
309 QString::number(frequency
).toUtf8().constData(), chan_type_s
,
311 &data
, &primary_msg
, &secondary_msg
, main_window_update
);
313 g_free(center_freq_s
);
316 g_free(secondary_msg
);
318 /* Parse the error msg */
320 err_str
= tr("Unable to set channel or offset.");
324 if (cur_fcs_idx
>= 0) {
325 if (ws80211_set_fcs_validation(cur_iface
.toUtf8().constData(), (enum ws80211_fcs_validation
) cur_fcs_idx
) != 0) {
326 err_str
= tr("Unable to set FCS validation behavior.");
330 if (!err_str
.isEmpty()) {
331 mainApp
->pushStatus(MainApplication::TemporaryStatus
, err_str
);
337 int WirelessFrame::getCenterFrequency(int control_frequency
, int bandwidth
)
339 if (bandwidth
< 80 || control_frequency
< 5180)
342 return ((control_frequency
- 5180) / bandwidth
) * bandwidth
+ 5180 + (bandwidth
/ 2) - 10;
345 int WirelessFrame::getBandwidthFromChanType(int chan_type
)
348 case WS80211_CHAN_VHT80
:
350 case WS80211_CHAN_VHT160
:
357 void WirelessFrame::on_interfaceComboBox_activated(int)
362 void WirelessFrame::on_channelComboBox_activated(int)
367 void WirelessFrame::on_channelTypeComboBox_activated(int)
372 void WirelessFrame::on_fcsComboBox_activated(int)