From 9f27e5d7d19027ddf8f29c5a6a321912e7c2f9dd Mon Sep 17 00:00:00 2001 From: Mikael Kanstrup Date: Sun, 7 Feb 2016 00:40:51 +0100 Subject: [PATCH] dumpcap: Add support for 802.11ac monitor modes 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 Petri-Dish: Gerald Combs Tested-by: Petri Dish Buildbot Reviewed-by: Michael Mann --- ConfigureChecks.cmake | 7 +++ capchild/capture_sync.c | 3 +- capchild/capture_sync.h | 1 + caputils/ws80211_utils.c | 89 ++++++++++++++++++++++++++++++++++--- caputils/ws80211_utils.h | 14 +++++- cmakeconfig.h.in | 3 ++ configure.ac | 6 +++ dumpcap.c | 25 ++++++++--- ui/gtk/main_80211_toolbar.c | 2 +- ui/qt/wireless_frame.cpp | 5 ++- 10 files changed, 136 insertions(+), 19 deletions(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 25e4a888d4..f7b2a7bb4a 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -174,6 +174,13 @@ if (NL_FOUND) }" HAVE_NL80211_SPLIT_WIPHY_DUMP ) + check_c_source_compiles( + "#include + int main() { + enum nl80211_attrs x = NL80211_ATTR_VHT_CAPABILITY; + }" + HAVE_NL80211_VHT_CAPABILITY + ) endif() # diff --git a/capchild/capture_sync.c b/capchild/capture_sync.c index 5cb3881da8..310c767f7d 100644 --- a/capchild/capture_sync.c +++ b/capchild/capture_sync.c @@ -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); diff --git a/capchild/capture_sync.h b/capchild/capture_sync.h index 01e2a0c660..17dc35d347 100644 --- a/capchild/capture_sync.h +++ b/capchild/capture_sync.h @@ -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)); diff --git a/caputils/ws80211_utils.c b/caputils/ws80211_utils.c index e374372fff..c556773a1d 100644 --- a/caputils/ws80211_utils.c +++ b/caputils/ws80211_utils.c @@ -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; } diff --git a/caputils/ws80211_utils.h b/caputils/ws80211_utils.h index d111030e25..3f58576758 100644 --- a/caputils/ws80211_utils.h +++ b/caputils/ws80211_utils.h @@ -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? */ diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in index 5d0543df96..aa4a2f9492 100644 --- a/cmakeconfig.h.in +++ b/cmakeconfig.h.in @@ -205,6 +205,9 @@ /* 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 header file. */ #cmakedefine HAVE_NTDDNDIS_H 1 diff --git a/configure.ac b/configure.ac index 719ae06cef..c917542472 100644 --- a/configure.ac +++ b/configure.ac @@ -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 ], + [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)]) ;; *) diff --git a/dumpcap.c b/dumpcap.c index 1783a4c6c6..4d91d3c6ab 100644 --- 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 ,[]\n"); + fprintf(output, " -k set channel on wifi interface:\n" + " ,[],[],[]\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))); diff --git a/ui/gtk/main_80211_toolbar.c b/ui/gtk/main_80211_toolbar.c index d1b58ea768..a844ef7159 100644 --- a/ui/gtk/main_80211_toolbar.c +++ b/ui/gtk/main_80211_toolbar.c @@ -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 */ diff --git a/ui/qt/wireless_frame.cpp b/ui/qt/wireless_frame.cpp index 4ba38f363e..67b139439e 100644 --- a/ui/qt/wireless_frame.cpp +++ b/ui/qt/wireless_frame.cpp @@ -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); }