replace SPDX identifier GPL-2.0+ with GPL-2.0-or-later.
[metze/wireshark/wip.git] / ui / qt / wireless_frame.cpp
1 /* wireless_frame.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 #include "wireless_frame.h"
10 #include <ui_wireless_frame.h>
11
12 #include "config.h"
13
14 #include <glib.h>
15
16 #include <capchild/capture_session.h>
17 #include <capchild/capture_sync.h>
18
19 #include <caputils/ws80211_utils.h>
20
21 #include "ui/ws_ui_util.h"
22 #include <wsutil/utf8_entities.h>
23 #include <wsutil/frequency-utils.h>
24
25 #include <QProcess>
26
27 // To do:
28 // - Disable or hide invalid channel types.
29 // - Push more status messages ("switched to...") to the status bar.
30 // - Add a "Decrypt in the driver" checkbox?
31 // - Check for frequency and channel type changes.
32 // - Find something appropriate to run from the helperToolButton on Linux.
33
34 // Questions:
35 // - From our perspective, what's the difference between "NOHT" and "HT20"?
36
37 const int update_interval_ = 1500; // ms
38
39 WirelessFrame::WirelessFrame(QWidget *parent) :
40     QFrame(parent),
41     ui(new Ui::WirelessFrame),
42     interfaces_(NULL),
43     capture_in_progress_(false)
44 {
45     ui->setupUi(this);
46
47     ui->helperToolButton->hide();
48
49     if (ws80211_init() == 0) {
50         ui->stackedWidget->setEnabled(true);
51         ui->stackedWidget->setCurrentWidget(ui->interfacePage);
52
53 #ifdef HAVE_AIRPCAP
54         // We should arguably add ws80211_get_helper_name and ws80211_get_helper_tooltip.
55         // This works for now and is translatable.
56         ui->helperToolButton->setText(tr("AirPcap Control Panel"));
57         ui->helperToolButton->setToolTip(tr("Open the AirPcap Control Panel"));
58         ui->helperToolButton->show();
59         ui->helperToolButton->setEnabled(ws80211_get_helper_path() != NULL);
60 #endif
61
62     } else {
63         ui->stackedWidget->setEnabled(false);
64         ui->stackedWidget->setCurrentWidget(ui->noWirelessPage);
65     }
66
67     ui->fcsFilterFrame->setVisible(ws80211_has_fcs_filter());
68
69     getInterfaceInfo();
70     iface_timer_id_ = startTimer(update_interval_);
71 }
72
73 WirelessFrame::~WirelessFrame()
74 {
75     ws80211_free_interfaces(interfaces_);
76     delete ui;
77 }
78
79 void WirelessFrame::setCaptureInProgress(bool capture_in_progress)
80 {
81     capture_in_progress_ = capture_in_progress;
82     updateWidgets();
83 }
84
85 // Check to see if the ws80211 interface list matches the one in our
86 // combobox. Rebuild ours if necessary and select the first interface if
87 // the current selection goes away.
88 void WirelessFrame::timerEvent(QTimerEvent *event)
89 {
90     if (event->timerId() != iface_timer_id_) {
91         QFrame::timerEvent(event);
92         return;
93     }
94
95     // Don't interfere with user activity.
96     if (ui->interfaceComboBox->view()->isVisible()
97         || ui->channelComboBox->view()->isVisible()
98         || ui->channelTypeComboBox->view()->isVisible()
99         || ui->fcsComboBox->view()->isVisible()) return;
100
101     ws80211_free_interfaces(interfaces_);
102     interfaces_ = ws80211_find_interfaces();
103     const QString old_iface = ui->interfaceComboBox->currentText();
104     guint iface_count = 0;
105     bool list_changed = false;
106
107     if (interfaces_ && interfaces_->len > 0) {
108         iface_count = interfaces_->len;
109     }
110
111     if ((int) iface_count != ui->interfaceComboBox->count()) {
112         list_changed = true;
113     } else {
114         for (guint i = 0; i < iface_count; i++) {
115             struct ws80211_interface *iface = g_array_index(interfaces_, struct ws80211_interface *, i);
116             if (ui->interfaceComboBox->itemText(i).compare(iface->ifname) != 0) {
117                 list_changed = true;
118                 break;
119             }
120         }
121     }
122
123     if (list_changed) {
124         ui->interfaceComboBox->clear();
125         for (guint i = 0; i < iface_count; i++) {
126             struct ws80211_interface *iface = g_array_index(interfaces_, struct ws80211_interface *, i);
127             ui->interfaceComboBox->addItem(iface->ifname);
128             if (old_iface.compare(iface->ifname) == 0) {
129                 ui->interfaceComboBox->setCurrentIndex(ui->interfaceComboBox->count() - 1);
130             }
131         }
132     }
133
134     if (ui->interfaceComboBox->currentText().compare(old_iface) != 0) {
135         getInterfaceInfo();
136     }
137 }
138
139 void WirelessFrame::updateWidgets()
140 {
141     bool enable_interface = false;
142     bool enable_channel = false;
143     bool enable_offset = false;
144     bool enable_show_fcs = false;
145
146     if (ui->interfaceComboBox->count() > 0) {
147         enable_interface = true;
148         enable_show_fcs = true;
149     }
150
151     if (enable_interface && ui->channelComboBox->count() > 0) {
152         enable_channel = true;
153     }
154
155     if (enable_channel && ui->channelTypeComboBox->count() > 1) {
156         enable_offset = true;
157     }
158
159     ui->interfaceComboBox->setEnabled(enable_interface);
160     ui->channelComboBox->setEnabled(enable_channel);
161     ui->channelTypeComboBox->setEnabled(enable_offset);
162     ui->fcsComboBox->setEnabled(!capture_in_progress_ && enable_show_fcs);
163 }
164
165 void WirelessFrame::on_helperToolButton_clicked()
166 {
167     const QString helper_path = ws80211_get_helper_path();
168     if (helper_path.isEmpty()) return;
169
170     QString command = QString("\"%1\"").arg(helper_path);
171     QProcess::startDetached(command);
172 }
173
174 void WirelessFrame::on_prefsToolButton_clicked()
175 {
176     emit showWirelessPreferences(QString("wlan"));
177 }
178
179 void WirelessFrame::getInterfaceInfo()
180 {
181     const QString cur_iface = ui->interfaceComboBox->currentText();
182
183     ui->channelComboBox->clear();
184     ui->channelTypeComboBox->clear();
185     ui->fcsComboBox->clear();
186
187     if (cur_iface.isEmpty()) {
188         updateWidgets();
189         return;
190     }
191
192     for (guint i = 0; i < interfaces_->len; i++) {
193         struct ws80211_interface *iface = g_array_index(interfaces_, struct ws80211_interface *, i);
194         if (cur_iface.compare(iface->ifname) == 0) {
195             struct ws80211_iface_info iface_info;
196             QString units = " GHz";
197
198             ws80211_get_iface_info(iface->ifname, &iface_info);
199
200             for (guint j = 0; j < iface->frequencies->len; j++) {
201                 guint32 frequency = g_array_index(iface->frequencies, guint32, j);
202                 double ghz = frequency / 1000.0;
203                 QString chan_str = QString("%1 " UTF8_MIDDLE_DOT " %2%3")
204                         .arg(ieee80211_mhz_to_chan(frequency))
205                         .arg(ghz, 0, 'f', 3)
206                         .arg(units);
207                 ui->channelComboBox->addItem(chan_str, frequency);
208                 if ((int)frequency == iface_info.current_freq) {
209                     ui->channelComboBox->setCurrentIndex(ui->channelComboBox->count() - 1);
210                 }
211                 units = QString();
212             }
213             // XXX - Do we need to make a distinction between WS80211_CHAN_NO_HT
214             // and WS80211_CHAN_HT20? E.g. is there a driver that won't capture
215             // HT frames if you use WS80211_CHAN_NO_HT?
216             ui->channelTypeComboBox->addItem("20 MHz", WS80211_CHAN_NO_HT);
217             if (iface_info.current_chan_type == WS80211_CHAN_NO_HT || iface_info.current_chan_type == WS80211_CHAN_HT20) {
218                 ui->channelTypeComboBox->setCurrentIndex(0);
219             }
220             if (iface->channel_types & (1 << WS80211_CHAN_HT40MINUS)) {
221                 ui->channelTypeComboBox->addItem("HT 40-", WS80211_CHAN_HT40MINUS);
222                 if (iface_info.current_chan_type == WS80211_CHAN_HT40MINUS) {
223                     ui->channelTypeComboBox->setCurrentIndex(ui->channelTypeComboBox->count() - 1);
224                 }
225             }
226             if (iface->channel_types & (1 << WS80211_CHAN_HT40PLUS)) {
227                 ui->channelTypeComboBox->addItem("HT 40+", WS80211_CHAN_HT40PLUS);
228                 if (iface_info.current_chan_type == WS80211_CHAN_HT40PLUS) {
229                     ui->channelTypeComboBox->setCurrentIndex(ui->channelTypeComboBox->count() - 1);
230                 }
231             }
232             if (iface->channel_types & (1 << WS80211_CHAN_VHT80)) {
233                 ui->channelTypeComboBox->addItem("VHT 80", WS80211_CHAN_VHT80);
234                 if (iface_info.current_chan_type == WS80211_CHAN_VHT80) {
235                     ui->channelTypeComboBox->setCurrentIndex(ui->channelTypeComboBox->count() - 1);
236                 }
237             }
238             if (iface->channel_types & (1 << WS80211_CHAN_VHT160)) {
239                 ui->channelTypeComboBox->addItem("VHT 160", WS80211_CHAN_VHT160);
240                 if (iface_info.current_chan_type == WS80211_CHAN_VHT160) {
241                     ui->channelTypeComboBox->setCurrentIndex(ui->channelTypeComboBox->count() - 1);
242                 }
243             }
244
245             if (ws80211_has_fcs_filter()) {
246                 ui->fcsComboBox->setCurrentIndex(iface_info.current_fcs_validation);
247             }
248         }
249     }
250
251     updateWidgets();
252 }
253
254 void WirelessFrame::setInterfaceInfo()
255 {
256     QString cur_iface = ui->interfaceComboBox->currentText();
257     int cur_chan_idx = ui->channelComboBox->currentIndex();
258     int cur_type_idx = ui->channelTypeComboBox->currentIndex();
259     int cur_fcs_idx = ui->fcsComboBox->currentIndex();
260
261     if (cur_iface.isEmpty() || cur_chan_idx < 0 || cur_type_idx < 0) return;
262
263 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211) && defined(HAVE_LIBPCAP)
264     int frequency = ui->channelComboBox->itemData(cur_chan_idx).toInt();
265     int chan_type = ui->channelTypeComboBox->itemData(cur_type_idx).toInt();
266     int bandwidth = getBandwidthFromChanType(chan_type);
267     int center_freq = getCenterFrequency(frequency, bandwidth);
268     const gchar *chan_type_s = ws80211_chan_type_to_str(chan_type);
269     gchar *center_freq_s = NULL;
270     gchar *data, *primary_msg, *secondary_msg;
271     int ret;
272
273     if (frequency < 0 || chan_type < 0) return;
274
275     if (center_freq != -1) {
276         center_freq_s = g_strdup(QString::number(center_freq).toUtf8().constData());
277     }
278
279     ret = sync_interface_set_80211_chan(cur_iface.toUtf8().constData(),
280                                         QString::number(frequency).toUtf8().constData(), chan_type_s,
281                                         center_freq_s, NULL,
282                                         &data, &primary_msg, &secondary_msg, main_window_update);
283
284     g_free(center_freq_s);
285     g_free(data);
286     g_free(primary_msg);
287     g_free(secondary_msg);
288
289     /* Parse the error msg */
290     if (ret) {
291         QString err_str = tr("Unable to set channel or offset.");
292         emit pushAdapterStatus(err_str);
293     }
294 #elif defined(HAVE_AIRPCAP)
295     int frequency = ui->channelComboBox->itemData(cur_chan_idx).toInt();
296     int chan_type = ui->channelTypeComboBox->itemData(cur_type_idx).toInt();
297     if (frequency < 0 || chan_type < 0) return;
298
299     if (ws80211_set_freq(cur_iface.toUtf8().constData(), frequency, chan_type, -1, -1) != 0) {
300         QString err_str = tr("Unable to set channel or offset.");
301         emit pushAdapterStatus(err_str);
302     }
303 #endif
304
305     if (cur_fcs_idx >= 0) {
306         if (ws80211_set_fcs_validation(cur_iface.toUtf8().constData(), (enum ws80211_fcs_validation) cur_fcs_idx) != 0) {
307             QString err_str = tr("Unable to set FCS validation behavior.");
308             emit pushAdapterStatus(err_str);
309         }
310     }
311
312     getInterfaceInfo();
313 }
314
315 int WirelessFrame::getCenterFrequency(int control_frequency, int bandwidth)
316 {
317     if (bandwidth < 80 || control_frequency < 5180)
318         return -1;
319
320     return ((control_frequency - 5180) / bandwidth) * bandwidth + 5180 + (bandwidth / 2) - 10;
321 }
322
323 int WirelessFrame::getBandwidthFromChanType(int chan_type)
324 {
325     switch (chan_type) {
326     case WS80211_CHAN_VHT80:
327         return 80;
328     case WS80211_CHAN_VHT160:
329         return 160;
330     default:
331         return -1;
332     }
333 }
334
335 void WirelessFrame::on_interfaceComboBox_activated(int)
336 {
337     getInterfaceInfo();
338 }
339
340 void WirelessFrame::on_channelComboBox_activated(int)
341 {
342     setInterfaceInfo();
343 }
344
345 void WirelessFrame::on_channelTypeComboBox_activated(int)
346 {
347     setInterfaceInfo();
348 }
349
350 void WirelessFrame::on_fcsComboBox_activated(int)
351 {
352     setInterfaceInfo();
353 }
354
355 /*
356  * Editor modelines
357  *
358  * Local Variables:
359  * c-basic-offset: 4
360  * tab-width: 8
361  * indent-tabs-mode: nil
362  * End:
363  *
364  * ex: set shiftwidth=4 tabstop=8 expandtab:
365  * :indentSize=4:tabSize=8:noTabs=true:
366  */