dumpcap: Add support for 802.11ac monitor modes
authorMikael Kanstrup <mikael.kanstrup@gmail.com>
Sat, 6 Feb 2016 23:40:51 +0000 (00:40 +0100)
committerMichael Mann <mmann78@netscape.net>
Tue, 1 Mar 2016 15:23:44 +0000 (15:23 +0000)
Add dumpcap support for configuring 80MHz, 80+80MHz, 160MHz monitor
modes via nl80211.

Change-Id: I2ae8955670c2a9b5051e2223d45ce522459f2c5f
Reviewed-on: https://code.wireshark.org/review/13964
Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Michael Mann <mmann78@netscape.net>
ConfigureChecks.cmake
capchild/capture_sync.c
capchild/capture_sync.h
caputils/ws80211_utils.c
caputils/ws80211_utils.h
cmakeconfig.h.in
configure.ac
dumpcap.c
ui/gtk/main_80211_toolbar.c
ui/qt/wireless_frame.cpp

index 25e4a888d4be4155799cf88b53eb3ab374b4efa9..f7b2a7bb4a54f084128a4d4b32827d2f9e578c6f 100644 (file)
@@ -174,6 +174,13 @@ if (NL_FOUND)
                }"
                HAVE_NL80211_SPLIT_WIPHY_DUMP
        )
+       check_c_source_compiles(
+               "#include <linux/nl80211.h>
+               int main() {
+                       enum nl80211_attrs x = NL80211_ATTR_VHT_CAPABILITY;
+               }"
+               HAVE_NL80211_VHT_CAPABILITY
+       )
 endif()
 
 #
index 5cb3881da8a6d503b9e4358eefb3a662acf8d950..310c767f7db23e39afda9dd156e962403244b3cc 100644 (file)
@@ -1297,6 +1297,7 @@ sync_pipe_run_command(char** argv, gchar **data, gchar **primary_msg,
 
 int
 sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar *type,
+                              const gchar *center_freq1, const gchar *center_freq2,
                               gchar **data, gchar **primary_msg,
                               gchar **secondary_msg, void (*update_cb)(void))
 {
@@ -1317,7 +1318,7 @@ sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar
     argv = sync_pipe_add_arg(argv, &argc, iface);
 
     if (type)
-        opt = g_strdup_printf("%s,%s", freq, type);
+        opt = g_strdup_printf("%s,%s,%s,%s", freq, type, center_freq1, center_freq2);
     else
         opt = g_strdup_printf("%s", freq);
 
index 01e2a0c66013d0c1722c196556ad0c20eeefcbe8..17dc35d347a36b95b74c888d9ee8f51b916c7862 100644 (file)
@@ -66,6 +66,7 @@ sync_pipe_kill(ws_process_id fork_child);
 /** Set wireless channel using dumpcap */
 extern int
 sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar *type,
+                              const gchar *center_freq1, const gchar *center_freq2,
                               gchar **data, gchar **primary_msg,
                               gchar **secondary_msg, void (*update_cb)(void));
 
index e374372fffb46d320983e9acc1114c3ce1e3fe54..c556773a1d0cc9123824c5f4e3414e6c502377c3 100644 (file)
@@ -266,6 +266,25 @@ static void parse_band_ht_capa(struct ws80211_interface *iface,
 }
 #endif /* NL80211_BAND_ATTR_HT_CAPA */
 
+#ifdef HAVE_NL80211_VHT_CAPABILITY
+static void parse_band_vht_capa(struct ws80211_interface *iface,
+                               struct nlattr *tb)
+{
+       guint32 chan_capa;
+       if (!tb) return;
+
+       chan_capa = (nla_get_u32(tb) >> 2) & 3;
+       if (chan_capa == 1) {
+               iface->channel_types |= 1 << WS80211_CHAN_VHT160;
+       }
+       if (chan_capa == 2) {
+               iface->channel_types |= 1 << WS80211_CHAN_VHT160;
+               iface->channel_types |= 1 << WS80211_CHAN_VHT80P80;
+       }
+       iface->channel_types |= 1 << WS80211_CHAN_VHT80;
+}
+#endif /* HAVE_NL80211_VHT_CAPABILITY */
+
 static void parse_supported_iftypes(struct ws80211_interface *iface,
                                    struct nlattr *tb)
 {
@@ -333,6 +352,9 @@ static void parse_wiphy_bands(struct ws80211_interface *iface,
 #ifdef NL80211_BAND_ATTR_HT_CAPA
                parse_band_ht_capa(iface, tb_band[NL80211_BAND_ATTR_HT_CAPA]);
 #endif /* NL80211_BAND_ATTR_HT_CAPA */
+#ifdef HAVE_NL80211_VHT_CAPABILITY
+               parse_band_vht_capa(iface, tb_band[NL80211_BAND_ATTR_VHT_CAPA]);
+#endif /* HAVE_NL80211_VHT_CAPABILITY */
                parse_band_freqs(iface, tb_band[NL80211_BAND_ATTR_FREQS]);
        }
 }
@@ -489,10 +511,36 @@ static int get_iface_info_handler(struct nl_msg *msg, void *arg)
        }
 
        if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) {
+               gboolean found_ch_width = FALSE;
                iface_info->pub->current_freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]);
                iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT;
-
-               if (tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+#ifdef HAVE_NL80211_VHT_CAPABILITY
+               if (tb_msg[NL80211_ATTR_CHANNEL_WIDTH]) {
+                       switch (nla_get_u32(tb_msg[NL80211_ATTR_CHANNEL_WIDTH])) {
+                       case NL80211_CHAN_WIDTH_80:
+                               iface_info->pub->current_chan_type = WS80211_CHAN_VHT80;
+                               found_ch_width = TRUE;
+                               break;
+                       case NL80211_CHAN_WIDTH_80P80:
+                               iface_info->pub->current_chan_type = WS80211_CHAN_VHT80P80;
+                               found_ch_width = TRUE;
+                               break;
+                       case NL80211_CHAN_WIDTH_160:
+                               iface_info->pub->current_chan_type = WS80211_CHAN_VHT160;
+                               found_ch_width = TRUE;
+                               break;
+                       }
+               }
+               if (tb_msg[NL80211_ATTR_CENTER_FREQ1]) {
+                       iface_info->pub->current_center_freq1 =
+                               nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ1]);
+               }
+               if (tb_msg[NL80211_ATTR_CENTER_FREQ2]) {
+                       iface_info->pub->current_center_freq2 =
+                               nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ2]);
+               }
+#endif
+               if (!found_ch_width && tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
                        switch (nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
 
                        case NL80211_CHAN_NO_HT:
@@ -598,7 +646,7 @@ static int ws80211_populate_devices(GArray *interfaces)
        int i;
        unsigned int j;
 
-       struct ws80211_iface_info pub = {-1, WS80211_CHAN_NO_HT, WS80211_FCS_ALL};
+       struct ws80211_iface_info pub = {-1, WS80211_CHAN_NO_HT, -1, -1, WS80211_FCS_ALL};
        struct __iface_info iface_info;
        struct ws80211_interface *iface;
 
@@ -722,7 +770,7 @@ nla_put_failure:
 }
 DIAG_ON(shorten-64-to-32)
 
-int ws80211_set_freq(const char *name, int freq, int chan_type)
+int ws80211_set_freq(const char *name, int freq, int chan_type, int _U_ center_freq, int _U_ center_freq2)
 {
        int devidx, err;
        struct nl_msg *msg;
@@ -772,7 +820,23 @@ int ws80211_set_freq(const char *name, int freq, int chan_type)
                NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40PLUS);
                break;
 #endif
+#ifdef HAVE_NL80211_VHT_CAPABILITY
+       case WS80211_CHAN_VHT80:
+               NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80);
+               NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq);
+               break;
 
+       case WS80211_CHAN_VHT80P80:
+               NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80P80);
+               NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq);
+               NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, center_freq2);
+               break;
+
+       case WS80211_CHAN_VHT160:
+               NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_160);
+               NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq);
+               break;
+#endif
        default:
                break;
        }
@@ -820,6 +884,13 @@ ws80211_str_to_chan_type(const gchar *s)
                ret = WS80211_CHAN_HT40MINUS;
        if (!strcmp(s, CHAN_HT40PLUS))
                ret = WS80211_CHAN_HT40PLUS;
+       if (!strcmp(s, CHAN_VHT80))
+               ret = WS80211_CHAN_VHT80;
+       if (!strcmp(s, CHAN_VHT80P80))
+               ret = WS80211_CHAN_VHT80P80;
+       if (!strcmp(s, CHAN_VHT160))
+               ret = WS80211_CHAN_VHT160;
+
        return ret;
 }
 
@@ -835,6 +906,12 @@ const gchar
                return CHAN_HT40MINUS;
        case WS80211_CHAN_HT40PLUS:
                return CHAN_HT40PLUS;
+       case WS80211_CHAN_VHT80:
+               return CHAN_VHT80;
+       case WS80211_CHAN_VHT80P80:
+               return CHAN_VHT80P80;
+       case WS80211_CHAN_VHT160:
+               return CHAN_VHT160;
        }
        return NULL;
 }
@@ -987,7 +1064,7 @@ int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_in
        return 0;
 }
 
-int ws80211_set_freq(const char *name, int freq, int chan_type)
+int ws80211_set_freq(const char *name, int freq, int chan_type, int _U_ center_freq, int _U_ center_freq2)
 {
        GList *airpcap_if_list;
        int err;
@@ -1145,7 +1222,7 @@ int ws80211_get_iface_info(const char *name _U_, struct ws80211_iface_info *ifac
        return -1;
 }
 
-int ws80211_set_freq(const char *name _U_, int freq _U_, int _U_ chan_type)
+int ws80211_set_freq(const char *name _U_, int freq _U_, int _U_ chan_type, int _U_ center_freq, int _U_ center_freq2)
 {
        return -1;
 }
index d111030e25074b092406bb47dfab3a4945d9add9..3f58576758cbe3ca715e2f58d1b34b1bfaff3cc9 100644 (file)
@@ -31,13 +31,19 @@ enum ws80211_channel_type {
        WS80211_CHAN_NO_HT,
        WS80211_CHAN_HT20,
        WS80211_CHAN_HT40MINUS,
-       WS80211_CHAN_HT40PLUS
+       WS80211_CHAN_HT40PLUS,
+       WS80211_CHAN_VHT80,
+       WS80211_CHAN_VHT80P80,
+       WS80211_CHAN_VHT160
 };
 
 #define CHAN_NO_HT     "NOHT"
 #define CHAN_HT20      "HT20"
 #define CHAN_HT40MINUS "HT40-"
 #define CHAN_HT40PLUS  "HT40+"
+#define CHAN_VHT80     "VHT80"
+#define CHAN_VHT80P80  "VHT80+80"
+#define CHAN_VHT160    "VHT160"
 
 /* XXX This doesn't match AirpcapValidationType. Should it? */
 enum ws80211_fcs_validation {
@@ -59,6 +65,8 @@ struct ws80211_interface
 struct ws80211_iface_info {
        int current_freq;
        enum ws80211_channel_type current_chan_type;
+       int current_center_freq1;
+       int current_center_freq2;
        enum ws80211_fcs_validation current_fcs_validation;
 };
 
@@ -92,9 +100,11 @@ void ws80211_free_interfaces(GArray *interfaces);
  * @param name The interface name.
  * @param freq The frequency in MHz.
  * @param chan_type The HT channel type (no, 20Mhz, 40Mhz...).
+ * @param center_freq The center frequency in MHz (if 80MHz, 80+80MHz or 160MHz).
+ * @param center_freq2 The 2nd center frequency in MHz (if 80+80MHz).
  * @return Zero on success, nonzero on failure.
  */
-int ws80211_set_freq(const char *name, int freq, int chan_type);
+int ws80211_set_freq(const char *name, int freq, int chan_type, int _U_ center_freq, int _U_ center_freq2);
 
 int ws80211_str_to_chan_type(const gchar *s); /* GTK+ only? */
 const gchar *ws80211_chan_type_to_str(int type); /* GTK+ only? */
index 5d0543df962fa2b03429d2137cd29e7d2f1ef78f..aa4a2f949258730a2aa5d9778244669893668af2 100644 (file)
 /* SPLIT_WIPHY_DUMP is supported */
 #cmakedefine HAVE_NL80211_SPLIT_WIPHY_DUMP 1
 
+/* VHT_CAPABILITY is supported */
+#cmakedefine HAVE_NL80211_VHT_CAPABILITY 1
+
 /* Define to 1 if you have the <Ntddndis.h> header file. */
 #cmakedefine HAVE_NTDDNDIS_H 1
 
index 719ae06cefdfe05bcf9a5515b60bc406630c489f..c917542472c1f269d016f93ab5aafa422db83fa3 100644 (file)
@@ -743,6 +743,12 @@ linux*)
            [enum nl80211_protocol_features x = NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP;],
            [AC_MSG_RESULT(yes) AC_DEFINE(HAVE_NL80211_SPLIT_WIPHY_DUMP, 1, [SPLIT_WIPHY_DUMP is supported])],
            [AC_MSG_RESULT(no)])
+
+       AC_MSG_CHECKING([for NL80211_VHT_CAPABILITY])
+         AC_TRY_COMPILE([#include <linux/nl80211.h>],
+           [enum nl80211_attrs x = NL80211_ATTR_VHT_CAPABILITY;],
+           [AC_MSG_RESULT(yes) AC_DEFINE(HAVE_NL80211_VHT_CAPABILITY, 1, [VHT_CAPABILITY is supported])],
+           [AC_MSG_RESULT(no)])
        ;;
 
 *)
index 1783a4c6c615dd1401a889dd3c10e754847a51f8..4d91d3c6ab6284a3b5cd39d844d9d0ca963aa1eb 100644 (file)
--- a/dumpcap.c
+++ b/dumpcap.c
@@ -486,7 +486,8 @@ print_usage(FILE *output)
 #ifdef HAVE_BPF_IMAGE
     fprintf(output, "  -d                       print generated BPF code for capture filter\n");
 #endif
-    fprintf(output, "  -k                       set channel on wifi interface <freq>,[<type>]\n");
+    fprintf(output, "  -k                       set channel on wifi interface:\n"
+                    "                           <freq>,[<type>],[<center_freq1>],[<center_freq2>]\n");
     fprintf(output, "  -S                       print statistics for each interface once per second\n");
     fprintf(output, "  -M                       for -D, -L, and -S, produce machine-readable output\n");
     fprintf(output, "\n");
@@ -3648,23 +3649,33 @@ capture_loop_queue_packet_cb(u_char *pcap_opts_p, const struct pcap_pkthdr *phdr
 static int
 set_80211_channel(const char *iface, const char *opt)
 {
-    int     freq    = 0, type, ret;
+    int freq = 0;
+    int type = -1;
+    int center_freq1 = -1;
+    int center_freq2 = -1;
+    int args;
+    int ret;
     gchar **options = NULL;
 
-    options = g_strsplit_set(opt, ",", 2);
+    options = g_strsplit_set(opt, ",", 4);
+    for (args = 0; options[args]; args++);
 
     if (options[0])
         freq = atoi(options[0]);
 
-    if (options[1]) {
+    if (args >= 1 && options[1]) {
         type = ws80211_str_to_chan_type(options[1]);
         if (type == -1) {
             ret = EINVAL;
             goto out;
         }
     }
-    else
-        type = -1;
+
+    if (args >= 2 && options[2])
+        center_freq1 = atoi(options[2]);
+
+    if (args >= 3 && options[3])
+        center_freq2 = atoi(options[3]);
 
     ret = ws80211_init();
     if (ret) {
@@ -3672,7 +3683,7 @@ set_80211_channel(const char *iface, const char *opt)
         ret = 2;
         goto out;
     }
-    ret = ws80211_set_freq(iface, freq, type);
+    ret = ws80211_set_freq(iface, freq, type, center_freq1, center_freq2);
 
     if (ret) {
         cmdarg_err("%d: Failed to set channel: %s\n", abs(ret), g_strerror(abs(ret)));
index d1b58ea768c73365384046a5b8af3f15fab8583e..a844ef71598fc8468fa0f0ffea9079fc03eb6c9b 100644 (file)
@@ -170,7 +170,7 @@ tb80211_do_set_channel(char *iface, int freq, int type)
 
     freq_s = g_strdup_printf("%d", freq);
     type_s = ws80211_chan_type_to_str(type);
-    ret = sync_interface_set_80211_chan(iface, freq_s, type_s,
+    ret = sync_interface_set_80211_chan(iface, freq_s, type_s, "-1", "-1",
                                         &data, &primary_msg, &secondary_msg, main_window_update);
 
     /* Parse the error msg */
index 4ba38f363e6e21624a44b3056fd1e60151f54385..67b139439ec52989cb63e3c97da43d342cce58f9 100644 (file)
@@ -265,7 +265,8 @@ void WirelessFrame::setInterfaceInfo()
 
     if (frequency.isEmpty() || chan_type < 0) return;
 
-    ret = sync_interface_set_80211_chan(cur_iface.toUtf8().constData(), frequency.toUtf8().constData(), chan_type_s,
+    ret = sync_interface_set_80211_chan(cur_iface.toUtf8().constData(), frequency.toUtf8().constData(),
+                                        chan_type_s, "-1", "-1",
                                         &data, &primary_msg, &secondary_msg, main_window_update);
 
     g_free(data);
@@ -282,7 +283,7 @@ void WirelessFrame::setInterfaceInfo()
     int chan_type = ui->channelTypeComboBox->itemData(cur_type_idx).toInt();
     if (frequency < 0 || chan_type < 0) return;
 
-    if (ws80211_set_freq(cur_iface.toUtf8().constData(), frequency, chan_type) != 0) {
+    if (ws80211_set_freq(cur_iface.toUtf8().constData(), frequency, chan_type, -1, -1) != 0) {
         QString err_str = tr("Unable to set channel or offset.");
         emit pushAdapterStatus(err_str);
     }