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