diff options
-rw-r--r-- | caputils/ws80211_utils.c | 143 | ||||
-rw-r--r-- | caputils/ws80211_utils.h | 1 | ||||
-rw-r--r-- | configure.ac | 6 |
3 files changed, 126 insertions, 24 deletions
diff --git a/caputils/ws80211_utils.c b/caputils/ws80211_utils.c index 78f9cb721b..0e411c33a3 100644 --- a/caputils/ws80211_utils.c +++ b/caputils/ws80211_utils.c @@ -47,6 +47,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include <linux/nl80211.h> +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP +static int ws80211_get_protocol_features(int* features); +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ + /* libnl 1.x compatibility code */ #ifdef HAVE_LIBNL1 #define nl_sock nl_handle @@ -64,6 +68,7 @@ static inline void nl_socket_free(struct nl_sock *h) struct nl80211_state { struct nl_sock *nl_sock; int nl80211_id; + int have_split_wiphy; }; static struct nl80211_state nl_state; @@ -71,6 +76,9 @@ static struct nl80211_state nl_state; int ws80211_init(void) { int err; +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP + int features = 0; +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ struct nl80211_state *state = &nl_state; @@ -92,6 +100,11 @@ int ws80211_init(void) err = -ENOENT; goto out_handle_destroy; } +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP + ws80211_get_protocol_features(&features); + if (features & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + state->have_split_wiphy = TRUE; +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ return 0; @@ -154,6 +167,21 @@ struct nliface_cookie GArray *interfaces; }; +static struct ws80211_interface * + get_interface_by_name(GArray *interfaces, + char* ifname) +{ + unsigned int i; + struct ws80211_interface *iface; + + for (i = 0; i < interfaces->len; i++) { + iface = g_array_index(interfaces, struct ws80211_interface *, i); + if (!strcmp(iface->ifname, ifname)) + return iface; + } + return NULL; +} + /* * And now for a steaming heap of suck. * @@ -175,6 +203,43 @@ struct nliface_cookie #define nla_for_each_nested(pos, nla, rem) \ nla_for_each_attr(pos, (struct nlattr *)nla_data(nla), nla_len(nla), rem) +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP +static int get_features_handler(struct nl_msg *msg, void *arg) +{ + int *feat = (int*) arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) + *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]); + + return NL_SKIP; +} + +static int ws80211_get_protocol_features(int* features) +{ + struct nl_msg *msg; + struct nl_cb *cb; + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, 0, + NL80211_CMD_GET_PROTOCOL_FEATURES, 0); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_features_handler, features); + + return nl80211_do_cmd(msg, cb); +} +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ #ifdef NL80211_BAND_ATTR_HT_CAPA static void parse_band_ht_capa(struct ws80211_interface *iface, @@ -193,20 +258,18 @@ static void parse_band_ht_capa(struct ws80211_interface *iface, } #endif /* NL80211_BAND_ATTR_HT_CAPA */ -static int parse_supported_iftypes(struct nlattr *tb) +static void parse_supported_iftypes(struct ws80211_interface *iface, + struct nlattr *tb) { struct nlattr *nl_mode; int rem_mode; - int cap_monitor = 0; - if (!tb) return 0; + if (!tb) return; nla_for_each_nested(nl_mode, tb, rem_mode) { if (nla_type(nl_mode) == NL80211_IFTYPE_MONITOR) - cap_monitor = 1; + iface->cap_monitor = 1; } - - return cap_monitor; } static void parse_band_freqs(struct ws80211_interface *iface, @@ -291,41 +354,44 @@ static int get_phys_handler(struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); struct nliface_cookie *cookie = (struct nliface_cookie *)arg; + struct ws80211_interface *iface; - int cap_monitor = 0; + char* ifname; + int added = 0; nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); - if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) - return NL_SKIP; - - cap_monitor = parse_supported_iftypes(tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]); - - if (!cap_monitor) - return NL_SKIP; - - iface = (struct ws80211_interface *)g_malloc0(sizeof(*iface)); - if (!iface) + if (!tb_msg[NL80211_ATTR_WIPHY_NAME]) return NL_SKIP; - iface->frequencies = g_array_new(FALSE, FALSE, sizeof(int)); - iface->channel_types = 1 << WS80211_CHAN_NO_HT; + ifname = g_strdup_printf("%s.mon", nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME])); + iface = get_interface_by_name(cookie->interfaces, ifname); - if (tb_msg[NL80211_ATTR_WIPHY_NAME]) { - iface->ifname = g_strdup_printf("%s.mon", - nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME])); + if (!iface) { + iface = (struct ws80211_interface *)g_malloc0(sizeof(*iface)); + if (!iface) { + g_free(ifname); + return NL_SKIP; + } + added = 1; + iface->ifname = ifname; + iface->frequencies = g_array_new(FALSE, FALSE, sizeof(int)); + iface->channel_types = 1 << WS80211_CHAN_NO_HT; + } else { + g_free(ifname); } + parse_supported_iftypes(iface, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]); parse_wiphy_bands(iface, tb_msg[NL80211_ATTR_WIPHY_BANDS]); parse_supported_commands(iface, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]); - g_array_append_val(cookie->interfaces, iface); + if (added) + g_array_append_val(cookie->interfaces, iface); return NL_SKIP; } - static int ws80211_get_phys(GArray *interfaces) { struct nliface_cookie cookie; @@ -344,10 +410,20 @@ static int ws80211_get_phys(GArray *interfaces) genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, NLM_F_DUMP, NL80211_CMD_GET_WIPHY, 0); +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP + if (nl_state.have_split_wiphy) { + NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); + } +#endif /* #ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP */ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_phys_handler, &cookie); return nl80211_do_cmd(msg, cb); +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP +nla_put_failure: + fprintf(stderr, "building message failed\n"); + return -1; +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ } static int get_freq_wext(const char *ifname) @@ -478,6 +554,24 @@ int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_in return __ws80211_get_iface_info(name, &__iface_info); } +static int ws80211_keep_only_monitor(GArray *interfaces) +{ + unsigned int j; + struct ws80211_interface *iface; +restart: + for (j = 0; j < interfaces->len; j++) { + iface = g_array_index(interfaces, struct ws80211_interface *, j); + if (!iface->cap_monitor) { + g_array_remove_index(interfaces, j); + g_array_free(iface->frequencies, TRUE); + g_free(iface->ifname); + g_free(iface); + goto restart; + } + } + return 0; +} + static int ws80211_populate_devices(GArray *interfaces) { FILE *fh; @@ -494,6 +588,7 @@ static int ws80211_populate_devices(GArray *interfaces) /* Get a list of phy's that can handle monitor mode */ ws80211_get_phys(interfaces); + ws80211_keep_only_monitor(interfaces); fh = g_fopen("/proc/net/dev", "r"); if(!fh) { diff --git a/caputils/ws80211_utils.h b/caputils/ws80211_utils.h index 2a6e04f629..7fcdc4477b 100644 --- a/caputils/ws80211_utils.h +++ b/caputils/ws80211_utils.h @@ -41,6 +41,7 @@ struct ws80211_interface gboolean can_set_freq; GArray *frequencies; int channel_types; /* Union for all bands */ + int cap_monitor; }; struct ws80211_iface_info { diff --git a/configure.ac b/configure.ac index 9e82ce0f5e..cb3cbdeb81 100644 --- a/configure.ac +++ b/configure.ac @@ -653,6 +653,12 @@ linux*) [enum nl80211_commands x = NL80211_CMD_SET_CHANNEL;], [AC_MSG_RESULT(yes) AC_DEFINE(HAVE_NL80211_CMD_SET_CHANNEL, 1, [SET_CHANNEL is supported])], [AC_MSG_RESULT(no)]) + + AC_MSG_CHECKING([for NL80211_SPLIT_WIPHY_DUMP]) + AC_TRY_COMPILE([#include <linux/nl80211.h>], + [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)]) ;; *) |