From e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:34:10 +0200 Subject: Adding upstream version 4.2.2. Signed-off-by: Daniel Baumann --- capture/ws80211_utils.c | 1265 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1265 insertions(+) create mode 100644 capture/ws80211_utils.c (limited to 'capture/ws80211_utils.c') diff --git a/capture/ws80211_utils.c b/capture/ws80211_utils.c new file mode 100644 index 0000000..2211f80 --- /dev/null +++ b/capture/ws80211_utils.c @@ -0,0 +1,1265 @@ +/* + * ws80211 utilities + * Copyright 2012, Pontus Fuchs + +Parts of this file was copied from iw: + +Copyright (c) 2007, 2008 Johannes Berg +Copyright (c) 2007 Andy Lutomirski +Copyright (c) 2007 Mike Kershaw +Copyright (c) 2008-2009 Luis R. Rodriguez + +SPDX-License-Identifier: ISC +*/ + +#include "config.h" +#include "ws80211_utils.h" + +#include + +#include +#include + +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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 +static inline struct nl_handle *nl_socket_alloc(void) +{ + return nl_handle_alloc(); +} + +static inline void nl_socket_free(struct nl_sock *h) +{ + nl_handle_destroy(h); +} +#endif /* HAVE_LIBNL1 */ + +struct nl80211_state { + struct nl_sock *nl_sock; + int nl80211_id; + int have_split_wiphy; +}; + +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; + + state->nl_sock = nl_socket_alloc(); + if (!state->nl_sock) { + fprintf(stderr, "Failed to allocate netlink socket.\n"); + return -ENOMEM; + } + + if (genl_connect(state->nl_sock)) { + fprintf(stderr, "Failed to connect to generic netlink.\n"); + err = -ENOLINK; + goto out_handle_destroy; + } + + state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211"); + if (state->nl80211_id < 0) { + fprintf(stderr, "nl80211 not found.\n"); + 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 WS80211_INIT_OK; + + out_handle_destroy: + nl_socket_free(state->nl_sock); + state->nl_sock = 0; + return err; +} + +static int error_handler(struct sockaddr_nl *nla _U_, struct nlmsgerr *err, + void *arg) +{ + int *ret = (int *)arg; + *ret = err->error; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg _U_, void *arg) +{ + int *ret = (int *)arg; + *ret = 0; + return NL_SKIP; +} + +static int ack_handler(struct nl_msg *msg _U_, void *arg) +{ + int *ret = (int *)arg; + *ret = 0; + return NL_STOP; +} + +static int nl80211_do_cmd(struct nl_msg *msg, struct nl_cb *cb) +{ + /* + * XXX - Coverity doesn't understand how libnl works, so it + * doesn't know that nl_recvmsgs() calls the callback, and + * that the callback has had a pointer to err registered + * with it, and therefore that nl_recvmsgs() can change + * err as a side-effect, so it thinks this can loop + * infinitely. + * + * The proper way to address this is to help Coverity to + * understand the behaviour of nl_recvmsgs(), in that it + * does call the callback, setting err. This help would be + * provided through a so called 'model' of this function. + * We declare err to be volatile to work around it. + * + * XXX - that workaround provokes a compiler complaint that + * casting a pointer to it to "void *" discards the + * volatile qualifier. Perhaps we should just re-close + * Coverity CID 997052 as "false positive". + */ + volatile int err; + + if (!nl_state.nl_sock) + return -ENOLINK; + + err = nl_send_auto_complete(nl_state.nl_sock, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, (void *)&err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, (void *)&err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, (void *)&err); + + while (err > 0) + nl_recvmsgs(nl_state.nl_sock, cb); + out: + nl_cb_put(cb); + + return err; +} + +struct nliface_cookie +{ + char *ifname; + 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; +} + +#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; + int ret; + + 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); + + ret = nl80211_do_cmd(msg, cb); + nlmsg_free(msg); + return ret; +} +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ + +#ifdef NL80211_BAND_ATTR_HT_CAPA +static void parse_band_ht_capa(struct ws80211_interface *iface, + struct nlattr *tb) +{ + bool ht40; + + if (!tb) return; + + iface->channel_types |= 1 << WS80211_CHAN_HT20; + ht40 = !!(nla_get_u16(tb) & 0x02); + if (ht40) { + iface->channel_types |= 1 << WS80211_CHAN_HT40MINUS; + iface->channel_types |= 1 << WS80211_CHAN_HT40PLUS; + } +} +#endif /* NL80211_BAND_ATTR_HT_CAPA */ + +#ifdef HAVE_NL80211_VHT_CAPABILITY +static void parse_band_vht_capa(struct ws80211_interface *iface, + struct nlattr *tb) +{ + uint32_t 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) +{ + struct nlattr *nl_mode; + int rem_mode; + + if (!tb) return; + + nla_for_each_nested(nl_mode, tb, rem_mode) { + if (nla_type(nl_mode) == NL80211_IFTYPE_MONITOR) + iface->cap_monitor = 1; + } +} + +static void parse_band_freqs(struct ws80211_interface *iface, + struct nlattr *tb) +{ + struct nlattr *nl_freq; + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + {NLA_UNSPEC, 0, 0}, /* __NL80211_FREQUENCY_ATTR_INVALID */ + {NLA_U32, 0, 0}, /* NL80211_FREQUENCY_ATTR_FREQ */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_DISABLED */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_PASSIVE_SCAN */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_NO_IBSS */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_RADAR */ + {NLA_U32, 0, 0} /* NL80211_FREQUENCY_ATTR_MAX_TX_POWER */ + }; + int rem_freq; + + if (!tb) return; + + nla_for_each_nested(nl_freq, tb, rem_freq) { + uint32_t freq; + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + (struct nlattr *)nla_data(nl_freq), + nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + continue; + + freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + g_array_append_val(iface->frequencies, freq); + } +} + +static void parse_wiphy_bands(struct ws80211_interface *iface, + struct nlattr *tb) +{ + struct nlattr *nl_band; + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + int rem_band; + + if (!tb) return; + + nla_for_each_nested(nl_band, tb, rem_band) { + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, + (struct nlattr *)nla_data(nl_band), + nla_len(nl_band), NULL); + +#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]); + } +} + +static void parse_supported_commands(struct ws80211_interface *iface, + struct nlattr *tb) +{ + /* Can frequency be set? Only newer versions of cfg80211 supports this */ +#ifdef HAVE_NL80211_CMD_SET_CHANNEL + int cmd; + struct nlattr *nl_cmd; + + if (!tb) return; + + nla_for_each_nested(nl_cmd, tb, cmd) { + if(nla_get_u32(nl_cmd) == NL80211_CMD_SET_CHANNEL) + iface->can_set_freq = true; + } +#else + iface->can_set_freq = true; +#endif +} + +static int get_phys_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + + struct nliface_cookie *cookie = (struct nliface_cookie *)arg; + + struct ws80211_interface *iface; + 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_NAME]) + return NL_SKIP; + + ifname = ws_strdup_printf("%s.mon", nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME])); + iface = get_interface_by_name(cookie->interfaces, ifname); + + 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(uint32_t)); + 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]); + + if (added) + g_array_append_val(cookie->interfaces, iface); + + return NL_SKIP; +} + +static int ws80211_get_phys(GArray *interfaces) +{ + struct nliface_cookie cookie; + struct nl_msg *msg; + struct nl_cb *cb; + int ret; + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + cookie.interfaces = 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); + + ret = nl80211_do_cmd(msg, cb); + nlmsg_free(msg); + return ret; + +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP +nla_put_failure: + nlmsg_free(msg); + fprintf(stderr, "building message failed\n"); + return -1; +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ +} + +static int get_freq_wext(const char *ifname) +{ + int fd; + int ret = -1; + /* Ugly hack to avoid including wireless.h */ + struct { + char name1[IFNAMSIZ]; + __s32 m; + __s16 e; + __u8 i; + __u8 flags; + } wrq; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) + return -1; + + (void) g_strlcpy(wrq.name1, ifname, IFNAMSIZ); + /* SIOCGIWFREQ */ + if (ioctl(fd, 0x8B05, &wrq) == 0) { + if (wrq.e == 6) + ret = wrq.m; + } + close(fd); + return ret; +} + +struct __iface_info +{ + struct ws80211_iface_info *pub; + int type; + int phyidx; +}; + +static int get_iface_info_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct __iface_info *iface_info = (struct __iface_info *)arg; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb_msg[NL80211_ATTR_IFTYPE]) { + iface_info->type = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]); + } + if (tb_msg[NL80211_ATTR_WIPHY]) { + iface_info->phyidx = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]); + } + + if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) { + bool 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; +#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: + iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT; + break; + + case NL80211_CHAN_HT20: + iface_info->pub->current_chan_type = WS80211_CHAN_HT20; + break; + + case NL80211_CHAN_HT40MINUS: + iface_info->pub->current_chan_type = WS80211_CHAN_HT40MINUS; + break; + + case NL80211_CHAN_HT40PLUS: + iface_info->pub->current_chan_type = WS80211_CHAN_HT40PLUS; + break; + } + } + + } + return NL_SKIP; +} + + +static int __ws80211_get_iface_info(const char *name, struct __iface_info *iface_info) +{ + int devidx; + 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); + + devidx = if_nametoindex(name); + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_GET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_iface_info_handler, iface_info); + + if (nl80211_do_cmd(msg, cb)) { + nlmsg_free(msg); + return -1; + } + + /* Old kernels can't get the current freq via netlink. Try WEXT too :( */ + if (iface_info->pub->current_freq == -1) + iface_info->pub->current_freq = get_freq_wext(name); + nlmsg_free(msg); + return 0; + +nla_put_failure: + nlmsg_free(msg); + fprintf(stderr, "building message failed\n"); + return -1; +} + +int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info) +{ + struct __iface_info __iface_info; + + memset(iface_info, 0, sizeof(*iface_info)); + __iface_info.pub = iface_info; + __iface_info.type = -1; + __iface_info.phyidx= -1; + __iface_info.pub->current_freq = -1; + __iface_info.pub->current_chan_type = WS80211_CHAN_NO_HT; + + 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; + char line[200]; + char *t; + char *t2; + char *ret; + int i; + unsigned int j; + + struct ws80211_iface_info pub = {-1, WS80211_CHAN_NO_HT, -1, -1, WS80211_FCS_ALL}; + struct __iface_info iface_info; + struct ws80211_interface *iface; + + /* 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) { + fprintf(stderr, "Cannot open /proc/net/dev"); + return -ENOENT; + } + + /* Skip the first two lines */ + for (i = 0; i < 2; i++) { + ret = fgets(line, sizeof(line), fh); + if (ret == NULL) { + fprintf(stderr, "Error parsing /proc/net/dev"); + fclose(fh); + return -1; + } + } + + /* Update names of user created monitor interfaces */ + while(fgets(line, sizeof(line), fh)) { + t = index(line, ':'); + if (!t) + continue; + *t = 0; + t = line; + while (*t == ' ') + t++; + memset(&iface_info, 0, sizeof(iface_info)); + iface_info.pub = &pub; + __ws80211_get_iface_info(t, &iface_info); + + if (iface_info.type == NL80211_IFTYPE_MONITOR) { + for (j = 0; j < interfaces->len; j++) { + iface = g_array_index(interfaces, struct ws80211_interface *, j); + t2 = ws_strdup_printf("phy%d.mon", iface_info.phyidx); + if (t2) { + if (!strcmp(t2, iface->ifname)) { + g_free(iface->ifname); + iface->ifname = g_strdup(t); + } + g_free(t2); + } + } + } + } + fclose(fh); + return 0; +} + +static int ws80211_iface_up(const char *ifname) +{ + int sock; + struct ifreq ifreq; + + sock = socket(AF_PACKET, SOCK_RAW, 0); + if (sock == -1) + return -1; + + (void) g_strlcpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name)); + + if (ioctl(sock, SIOCGIFFLAGS, &ifreq)) + goto out_err; + + ifreq.ifr_flags |= IFF_UP; + + if (ioctl(sock, SIOCSIFFLAGS, &ifreq)) + goto out_err; + + close(sock); + return 0; + +out_err: + close(sock); + return -1; +} + +/* Needed for NLA_PUT_STRING, which passes strlen as an int */ +DIAG_OFF_CLANG(shorten-64-to-32) +static int ws80211_create_on_demand_interface(const char *name) +{ + int devidx, phyidx, err; + struct nl_msg *msg; + struct nl_cb *cb; + + devidx = if_nametoindex(name); + if (devidx) + return ws80211_iface_up(name); + + if (sscanf(name, "phy%d.mon", &phyidx) != 1) + return -EINVAL; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_NEW_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phyidx); + + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR); + + err = nl80211_do_cmd(msg, cb); + nlmsg_free(msg); + if (err) + return err; + return ws80211_iface_up(name); + +nla_put_failure: + nlmsg_free(msg); + fprintf(stderr, "building message failed\n"); + return 2; +} +DIAG_ON_CLANG(shorten-64-to-32) + +int ws80211_set_freq(const char *name, uint32_t freq, int chan_type, uint32_t _U_ center_freq, uint32_t _U_ center_freq2) +{ + int devidx, err; + struct nl_msg *msg; + struct nl_cb *cb; + + err = ws80211_create_on_demand_interface(name); + if (err) + return err; + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + devidx = if_nametoindex(name); + +#ifdef HAVE_NL80211_CMD_SET_CHANNEL + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_SET_CHANNEL, 0); +#else + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_SET_WIPHY, 0); +#endif + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + + switch (chan_type) { + +#ifdef NL80211_BAND_ATTR_HT_CAPA + case WS80211_CHAN_NO_HT: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_NO_HT); + break; + + case WS80211_CHAN_HT20: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20); + break; + + case WS80211_CHAN_HT40MINUS: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40MINUS); + break; + + case WS80211_CHAN_HT40PLUS: + 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; + } + err = nl80211_do_cmd(msg, cb); + nlmsg_free(msg); + return err; + +nla_put_failure: + nlmsg_free(msg); + fprintf(stderr, "building message failed\n"); + return 2; + +} + +GArray* ws80211_find_interfaces(void) +{ + GArray *interfaces; + + if (!nl_state.nl_sock) + return NULL; + + interfaces = g_array_new(false, false, sizeof(struct ws80211_interface *)); + if (!interfaces) + return NULL; + + if (ws80211_populate_devices(interfaces)) { + ws80211_free_interfaces(interfaces); + return NULL; + } + return interfaces; +} + +int +ws80211_str_to_chan_type(const char *s) +{ + int ret = -1; + if (!s) + return -1; + + if (!strcmp(s, CHAN_NO_HT)) + ret = WS80211_CHAN_NO_HT; + if (!strcmp(s, CHAN_HT20)) + ret = WS80211_CHAN_HT20; + if (!strcmp(s, CHAN_HT40MINUS)) + 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; +} + +const char +*ws80211_chan_type_to_str(int type) +{ + switch (type) { + case WS80211_CHAN_NO_HT: + return CHAN_NO_HT; + case WS80211_CHAN_HT20: + return CHAN_HT20; + case WS80211_CHAN_HT40MINUS: + 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; +} + +bool ws80211_has_fcs_filter(void) +{ + return false; +} + +int ws80211_set_fcs_validation(const char *name _U_, enum ws80211_fcs_validation fcs_validation _U_) +{ + return -1; +} + +const char *network_manager_path = "/usr/sbin/NetworkManager"; /* Is this correct? */ +const char *ws80211_get_helper_path(void) { + if (g_file_test(network_manager_path, G_FILE_TEST_IS_EXECUTABLE)) { + return network_manager_path; + } + return NULL; +} + +#elif defined(HAVE_AIRPCAP) + +#include + +#include "airpcap.h" +#include "airpcap_loader.h" + +int ws80211_init(void) +{ + if (airpcap_get_dll_state() == AIRPCAP_DLL_OK) { + return WS80211_INIT_OK; + } + return WS80211_INIT_NOT_SUPPORTED; +} + +static const char *airpcap_dev_prefix_ = "\\\\.\\"; + +GArray* ws80211_find_interfaces(void) +{ + GArray *interfaces; + GList *airpcap_if_list, *cur_if; + int err; + char *err_str = NULL; + + interfaces = g_array_new(false, false, sizeof(struct ws80211_interface *)); + if (!interfaces) + return NULL; + + airpcap_if_list = get_airpcap_interface_list(&err, &err_str); + + if (airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0){ + g_free(err_str); + g_array_free(interfaces, true); + return NULL; + } + + for (cur_if = airpcap_if_list; cur_if; cur_if = g_list_next(cur_if)) { + struct ws80211_interface *iface; + airpcap_if_info_t *airpcap_if_info = (airpcap_if_info_t *) cur_if->data; + char *ifname; + uint32_t chan; + uint32_t i; + + if (!airpcap_if_info) continue; + ifname = airpcap_if_info->name; + if (strlen(ifname) > 4 && g_str_has_prefix(ifname, airpcap_dev_prefix_)) ifname += 4; + + iface = (struct ws80211_interface *)g_malloc0(sizeof(*iface)); + iface->ifname = g_strdup(ifname); + iface->can_set_freq = true; + iface->frequencies = g_array_new(false, false, sizeof(uint32_t)); + + iface->channel_types = 1 << WS80211_CHAN_NO_HT; + /* + * AirPcap stores per-channel capabilities. We should probably + * do the same. */ + for (i = 0; i < airpcap_if_info->numSupportedChannels; i++) { + if (airpcap_if_info->pSupportedChannels[i].Flags & FLAG_CAN_BE_HIGH) { + iface->channel_types |= 1 << WS80211_CHAN_HT40MINUS; + iface->channel_types |= 1 << WS80211_CHAN_HT40PLUS; + break; + } + } + + iface->cap_monitor = 1; + + for (chan = 0; chan < airpcap_if_info->numSupportedChannels; chan++) { + g_array_append_val(iface->frequencies, airpcap_if_info->pSupportedChannels[chan].Frequency); + } + + g_array_append_val(interfaces, iface); + } + + return interfaces; +} + +int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info) +{ + GList *airpcap_if_list; + int err; + char *err_str = NULL; + airpcap_if_info_t *airpcap_if_info; + + if (!iface_info) return -1; + + airpcap_if_list = get_airpcap_interface_list(&err, &err_str); + + if (airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0){ + g_free(err_str); + return -1; + } + + airpcap_if_info = get_airpcap_if_from_name(airpcap_if_list, name); + + if (!airpcap_if_info) { + free_airpcap_interface_list(airpcap_if_list); + return -1; + } + + memset(iface_info, 0, sizeof(*iface_info)); + iface_info->current_freq = airpcap_if_info->channelInfo.Frequency; + switch (airpcap_if_info->channelInfo.ExtChannel) { + case 0: + iface_info->current_chan_type = WS80211_CHAN_NO_HT; + break; + case -1: + iface_info->current_chan_type = WS80211_CHAN_HT40MINUS; + break; + case 1: + iface_info->current_chan_type = WS80211_CHAN_HT40PLUS; + break; + default: + return -1; + } + + switch (airpcap_if_info->CrcValidationOn) { + case AIRPCAP_VT_ACCEPT_CORRECT_FRAMES: + iface_info->current_fcs_validation = WS80211_FCS_VALID; + break; + case AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES: + iface_info->current_fcs_validation = WS80211_FCS_INVALID; + break; + default: + iface_info->current_fcs_validation = WS80211_FCS_ALL; + break; + } + + return 0; +} + +int ws80211_set_freq(const char *name, uint32_t freq, int chan_type, uint32_t _U_ center_freq, uint32_t _U_ center_freq2) +{ + GList *airpcap_if_list; + int err; + char *err_str = NULL; + airpcap_if_info_t *airpcap_if_info; + char err_buf[AIRPCAP_ERRBUF_SIZE]; + PAirpcapHandle adapter; + int ret_val = -1; + + airpcap_if_list = get_airpcap_interface_list(&err, &err_str); + + if (airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0){ + g_free(err_str); + return ret_val; + } + + airpcap_if_info = get_airpcap_if_from_name(airpcap_if_list, name); + + if (!airpcap_if_info) { + free_airpcap_interface_list(airpcap_if_list); + return ret_val; + } + + adapter = airpcap_if_open(airpcap_if_info->name, err_buf); + if (adapter) { + airpcap_if_info->channelInfo.Frequency = freq; + switch (chan_type) { + case WS80211_CHAN_HT40MINUS: + airpcap_if_info->channelInfo.ExtChannel = -1; + break; + case WS80211_CHAN_HT40PLUS: + airpcap_if_info->channelInfo.ExtChannel = 1; + break; + default: + airpcap_if_info->channelInfo.ExtChannel = 0; + break; + } + + if (airpcap_if_set_device_channel_ex(adapter, airpcap_if_info->channelInfo)) { + ret_val = 0; + } + airpcap_if_close(adapter); + } + + free_airpcap_interface_list(airpcap_if_list); + return ret_val; +} + +int ws80211_str_to_chan_type(const char *s _U_) +{ + return -1; +} + +const char *ws80211_chan_type_to_str(int type _U_) +{ + return NULL; +} + +bool ws80211_has_fcs_filter(void) +{ + return true; +} + +int ws80211_set_fcs_validation(const char *name, enum ws80211_fcs_validation fcs_validation) +{ + GList *airpcap_if_list; + int err; + char *err_str = NULL; + airpcap_if_info_t *airpcap_if_info; + char err_buf[AIRPCAP_ERRBUF_SIZE]; + PAirpcapHandle adapter; + int ret_val = -1; + + airpcap_if_list = get_airpcap_interface_list(&err, &err_str); + + if (airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0){ + g_free(err_str); + return ret_val; + } + + airpcap_if_info = get_airpcap_if_from_name(airpcap_if_list, name); + + if (!airpcap_if_info) { + free_airpcap_interface_list(airpcap_if_list); + return ret_val; + } + + adapter = airpcap_if_open(airpcap_if_info->name, err_buf); + if (adapter) { + AirpcapValidationType val_type = AIRPCAP_VT_ACCEPT_EVERYTHING; + switch (fcs_validation) { + case WS80211_FCS_VALID: + val_type = AIRPCAP_VT_ACCEPT_CORRECT_FRAMES; + break; + case WS80211_FCS_INVALID: + val_type = AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES; + break; + default: + break; + } + + if (airpcap_if_set_fcs_validation(adapter, val_type)) { + /* Appears to be necessary for this to take effect. */ + airpcap_if_store_cur_config_as_adapter_default(adapter); + ret_val = 0; + } + airpcap_if_close(adapter); + } + + free_airpcap_interface_list(airpcap_if_list); + return ret_val; +} + +static char *airpcap_conf_path = NULL; +const char *ws80211_get_helper_path(void) +{ + HKEY h_key = NULL; + + if (!airpcap_conf_path && RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\AirPcap"), 0, KEY_QUERY_VALUE|KEY_WOW64_32KEY, &h_key) == ERROR_SUCCESS) { + DWORD reg_ret; + TCHAR airpcap_dir_utf16[MAX_PATH]; + DWORD ad_size = sizeof(airpcap_dir_utf16)/sizeof(TCHAR); + + reg_ret = RegQueryValueEx(h_key, NULL, NULL, NULL, + (LPBYTE) &airpcap_dir_utf16, &ad_size); + + if (reg_ret == ERROR_SUCCESS) { + airpcap_dir_utf16[ad_size-1] = L'\0'; + g_free(airpcap_conf_path); + airpcap_conf_path = ws_strdup_printf("%s\\AirpcapConf.exe", utf_16to8(airpcap_dir_utf16)); + + if (!g_file_test(airpcap_conf_path, G_FILE_TEST_IS_EXECUTABLE)) { + g_free(airpcap_conf_path); + airpcap_conf_path = NULL; + } + } + } + + return airpcap_conf_path; +} + +#else /* Everyone else. */ +int ws80211_init(void) +{ + return WS80211_INIT_NOT_SUPPORTED; +} + +GArray* ws80211_find_interfaces(void) +{ + return NULL; +} + +int ws80211_get_iface_info(const char *name _U_, struct ws80211_iface_info *iface_info _U_) +{ + return -1; +} + +int ws80211_set_freq(const char *name _U_, uint32_t freq _U_, int _U_ chan_type, uint32_t _U_ center_freq, uint32_t _U_ center_freq2) +{ + return -1; +} + +int ws80211_str_to_chan_type(const char *s _U_) +{ + return -1; +} + +const char *ws80211_chan_type_to_str(int type _U_) +{ + return NULL; +} + +bool ws80211_has_fcs_filter(void) +{ + return false; +} + +int ws80211_set_fcs_validation(const char *name _U_, enum ws80211_fcs_validation fcs_validation _U_) +{ + return -1; +} + +const char *ws80211_get_helper_path(void) { + return NULL; +} + +#endif /* HAVE_LIBNL && HAVE_NL80211 */ + +/* Common to everyone */ + +void ws80211_free_interfaces(GArray *interfaces) +{ + struct ws80211_interface *iface; + + if (!interfaces) + return; + + while (interfaces->len) { + iface = g_array_index(interfaces, struct ws80211_interface *, 0); + g_array_remove_index(interfaces, 0); + g_array_free(iface->frequencies, true); + g_free(iface->ifname); + g_free(iface); + } + g_array_free(interfaces, true); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ -- cgit v1.2.3