X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fdrivers%2Fdriver_nl80211.c;h=d3e4cfb00f57ed6708eb66cc0113952f351dabef;hb=d02e5498310875b1e96921c7c1f53168f4ffee2c;hp=c86ba811cff5afeb00b22fedfc8415f504197aa2;hpb=72b2605f15553f1ddf4bc05be61450720aa4899a;p=mech_eap.git diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index c86ba81..d3e4cfb 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1,6 +1,6 @@ /* * Driver interaction with Linux nl80211/cfg80211 - * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * Copyright (c) 2007, Johannes Berg @@ -87,7 +87,6 @@ static void nl80211_handle_destroy(struct nl_handle *handle) #undef nl_socket_set_nonblocking #define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h) -#define genl_ctrl_resolve android_genl_ctrl_resolve #endif /* ANDROID */ @@ -132,6 +131,22 @@ static void nl80211_register_eloop_read(struct nl_handle **handle, eloop_sock_handler handler, void *eloop_data) { +#ifdef CONFIG_LIBNL20 + /* + * libnl uses a pretty small buffer (32 kB that gets converted to 64 kB) + * by default. It is possible to hit that limit in some cases where + * operations are blocked, e.g., with a burst of Deauthentication frames + * to hostapd and STA entry deletion. Try to increase the buffer to make + * this less likely to occur. + */ + if (nl_socket_set_buffer_size(*handle, 262144, 0) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not set nl_socket RX buffer size: %s", + strerror(errno)); + /* continue anyway with the default (smaller) buffer */ + } +#endif /* CONFIG_LIBNL20 */ + nl_socket_set_nonblocking(*handle); eloop_register_read_sock(nl_socket_get_fd(*handle), handler, eloop_data, *handle); @@ -148,6 +163,7 @@ static void nl80211_destroy_eloop_handle(struct nl_handle **handle) static void nl80211_global_deinit(void *priv); +static void nl80211_check_global(struct nl80211_global *global); static void wpa_driver_nl80211_deinit(struct i802_bss *bss); static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, @@ -160,13 +176,19 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, u64 *cookie, - int no_cck, int no_ack, int offchanok); + int no_cck, int no_ack, int offchanok, + const u16 *csa_offs, size_t csa_offs_len); static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report); -static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); -static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); +#define IFIDX_ANY -1 + +static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason); +static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason); +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason); static int nl80211_set_channel(struct i802_bss *bss, struct hostapd_freq_params *freq, int set_chan); @@ -290,7 +312,13 @@ static void nl80211_nlmsg_clear(struct nl_msg *msg) if (msg) { struct nlmsghdr *hdr = nlmsg_hdr(msg); void *data = nlmsg_data(hdr); - int len = nlmsg_datalen(hdr); + /* + * This would use nlmsg_datalen() or the older nlmsg_len() if + * only libnl were to maintain a stable API.. Neither will work + * with all released versions, so just calculate the length + * here. + */ + int len = hdr->nlmsg_len - NLMSG_HDRLEN; os_memset(data, 0, len); } @@ -417,6 +445,8 @@ static int nl_get_multicast_id(struct nl80211_global *global, void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int flags, uint8_t cmd) { + if (TEST_FAIL()) + return NULL; return genlmsg_put(msg, 0, 0, drv->global->nl80211_id, 0, flags, cmd, 0); } @@ -735,6 +765,15 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) } +static unsigned int nl80211_get_ifindex(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + return drv->ifindex; +} + + static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) { struct i802_bss *bss = priv; @@ -758,11 +797,12 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) static void wpa_driver_nl80211_event_newlink( - struct wpa_driver_nl80211_data *drv, const char *ifname) + struct nl80211_global *global, struct wpa_driver_nl80211_data *drv, + int ifindex, const char *ifname) { union wpa_event_data event; - if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) { if (if_nametoindex(drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK", drv->first_bss->ifname); @@ -776,19 +816,25 @@ static void wpa_driver_nl80211_event_newlink( } os_memset(&event, 0, sizeof(event)); + event.interface_status.ifindex = ifindex; os_strlcpy(event.interface_status.ifname, ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_ADDED; - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + if (drv) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + else + wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS, + &event); } static void wpa_driver_nl80211_event_dellink( - struct wpa_driver_nl80211_data *drv, const char *ifname) + struct nl80211_global *global, struct wpa_driver_nl80211_data *drv, + int ifindex, const char *ifname) { union wpa_event_data event; - if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) { if (drv->if_removed) { wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s", ifname); @@ -803,10 +849,15 @@ static void wpa_driver_nl80211_event_dellink( } os_memset(&event, 0, sizeof(event)); + event.interface_status.ifindex = ifindex; os_strlcpy(event.interface_status.ifname, ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_REMOVED; - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + if (drv) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + else + wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS, + &event); } @@ -842,6 +893,7 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, return 1; if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { + nl80211_check_global(drv->global); wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " "interface"); wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL); @@ -859,7 +911,7 @@ nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) dl_list_for_each(drv, &global->interfaces, struct wpa_driver_nl80211_data, list) { if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || - have_ifidx(drv, idx)) + have_ifidx(drv, idx, IFIDX_ANY)) return drv; } return NULL; @@ -879,13 +931,6 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, char ifname[IFNAMSIZ + 1]; char extra[100], *pos, *end; - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); - if (!drv) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK event for foreign ifindex %d", - ifi->ifi_index); - return; - } - extra[0] = '\0'; pos = extra; end = pos + sizeof(extra); @@ -929,17 +974,26 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) + goto event_newlink; + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + namebuf[0] = '\0'; if (if_indextoname(ifi->ifi_index, namebuf) && - linux_iface_up(drv->global->ioctl_sock, - drv->first_bss->ifname) > 0) { + linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event since interface %s is up", namebuf); drv->ignore_if_down_event = 0; return; } - wpa_printf(MSG_DEBUG, "nl80211: Interface down"); - if (drv->ignore_if_down_event) { + wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)", + namebuf, ifname); + if (os_strcmp(drv->first_bss->ifname, ifname) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Not the main interface (%s) - do not indicate interface down", + drv->first_bss->ifname); + } else if (drv->ignore_if_down_event) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event generated by mode change"); drv->ignore_if_down_event = 0; @@ -962,8 +1016,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { if (if_indextoname(ifi->ifi_index, namebuf) && - linux_iface_up(drv->global->ioctl_sock, - drv->first_bss->ifname) == 0) { + linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is down", namebuf); @@ -1019,10 +1072,12 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, -1, IF_OPER_UP); } +event_newlink: if (ifname[0]) - wpa_driver_nl80211_event_newlink(drv, ifname); + wpa_driver_nl80211_event_newlink(global, drv, ifi->ifi_index, + ifname); - if (ifi->ifi_family == AF_BRIDGE && brid) { + if (ifi->ifi_family == AF_BRIDGE && brid && drv) { struct i802_bss *bss; /* device has been added to bridge */ @@ -1034,7 +1089,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, } wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", brid, namebuf); - add_ifidx(drv, brid); + add_ifidx(drv, brid, ifi->ifi_index); for (bss = drv->first_bss; bss; bss = bss->next) { if (os_strcmp(ifname, bss->ifname) == 0) { @@ -1058,13 +1113,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, char ifname[IFNAMSIZ + 1]; char extra[100], *pos, *end; - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); - if (!drv) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_DELLINK event for foreign ifindex %d", - ifi->ifi_index); - return; - } - extra[0] = '\0'; pos = extra; end = pos + sizeof(extra); @@ -1105,10 +1153,9 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - if (ifname[0] && (ifi->ifi_family != AF_BRIDGE || !brid)) - wpa_driver_nl80211_event_dellink(drv, ifname); + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); - if (ifi->ifi_family == AF_BRIDGE && brid) { + if (ifi->ifi_family == AF_BRIDGE && brid && drv) { /* device has been removed from bridge */ char namebuf[IFNAMSIZ]; @@ -1121,8 +1168,12 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, "nl80211: Remove ifindex %u for bridge %s", brid, namebuf); } - del_ifidx(drv, brid); + del_ifidx(drv, brid, ifi->ifi_index); } + + if (ifi->ifi_family != AF_BRIDGE || !brid) + wpa_driver_nl80211_event_dellink(global, drv, ifi->ifi_index, + ifname); } @@ -1159,6 +1210,7 @@ static int get_link_signal(struct nl_msg *msg, void *arg) static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, + [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 }, }; struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { @@ -1187,6 +1239,13 @@ static int get_link_signal(struct nl_msg *msg, void *arg) else sig_change->avg_signal = 0; + if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]) + sig_change->avg_beacon_signal = + (s8) + nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]); + else + sig_change->avg_beacon_signal = 0; + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, sinfo[NL80211_STA_INFO_TX_BITRATE], @@ -1455,13 +1514,45 @@ err: } +static void nl80211_check_global(struct nl80211_global *global) +{ + struct nl_handle *handle; + const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL }; + int ret; + unsigned int i; + + /* + * Try to re-add memberships to handle case of cfg80211 getting reloaded + * and all registration having been cleared. + */ + handle = (void *) (((intptr_t) global->nl_event) ^ + ELOOP_SOCKET_INVALID); + + for (i = 0; groups[i]; i++) { + ret = nl_get_multicast_id(global, "nl80211", groups[i]); + if (ret >= 0) + ret = nl_socket_add_membership(handle, ret); + if (ret < 0) { + wpa_printf(MSG_INFO, + "nl80211: Could not re-add multicast membership for %s events: %d (%s)", + groups[i], ret, strerror(-ret)); + } + } +} + + static void wpa_driver_nl80211_rfkill_blocked(void *ctx) { + struct wpa_driver_nl80211_data *drv = ctx; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* - * This may be for any interface; use ifdown event to disable - * interface. + * rtnetlink ifdown handler will report interfaces other than the P2P + * Device interface as disabled. */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); } @@ -1474,7 +1565,16 @@ static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) "after rfkill unblock"); return; } - /* rtnetlink ifup handler will report interface as enabled */ + + if (is_p2p_net_interface(drv->nlmode)) + nl80211_disable_11b_rates(drv, drv->ifindex, 1); + + /* + * rtnetlink ifup handler will report interfaces other than the P2P + * Device interface as enabled. + */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); } @@ -1559,13 +1659,65 @@ static void nl80211_destroy_bss(struct i802_bss *bss) } +static void +wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv) +{ + struct rfkill_config *rcfg; + + if (drv->rfkill) + return; + + rcfg = os_zalloc(sizeof(*rcfg)); + if (!rcfg) + return; + + rcfg->ctx = drv; + + /* rfkill uses netdev sysfs for initialization. However, P2P Device is + * not associated with a netdev, so use the name of some other interface + * sharing the same wiphy as the P2P Device interface. + * + * Note: This is valid, as a P2P Device interface is always dynamically + * created and is created only once another wpa_s interface was added. + */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) { + struct nl80211_global *global = drv->global; + struct wpa_driver_nl80211_data *tmp1; + + dl_list_for_each(tmp1, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (drv == tmp1 || drv->wiphy_idx != tmp1->wiphy_idx || + !tmp1->rfkill) + continue; + + wpa_printf(MSG_DEBUG, + "nl80211: Use (%s) to initialize P2P Device rfkill", + tmp1->first_bss->ifname); + os_strlcpy(rcfg->ifname, tmp1->first_bss->ifname, + sizeof(rcfg->ifname)); + break; + } + } else { + os_strlcpy(rcfg->ifname, drv->first_bss->ifname, + sizeof(rcfg->ifname)); + } + + rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (!drv->rfkill) { + wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); + os_free(rcfg); + } +} + + static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, void *global_priv, int hostapd, const u8 *set_addr, const char *driver_params) { struct wpa_driver_nl80211_data *drv; - struct rfkill_config *rcfg; struct i802_bss *bss; if (global_priv == NULL) @@ -1577,8 +1729,17 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, drv->ctx = ctx; drv->hostapd = !!hostapd; drv->eapol_sock = -1; + + /* + * There is no driver capability flag for this, so assume it is + * supported and disable this on first attempt to use if the driver + * rejects the command due to missing support. + */ + drv->set_rekey_offload = 1; + drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); drv->if_indices = drv->default_if_indices; + drv->if_indices_reason = drv->default_if_indices_reason; drv->first_bss = os_zalloc(sizeof(*drv->first_bss)); if (!drv->first_bss) { @@ -1598,22 +1759,6 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, if (nl80211_init_bss(bss)) goto failed; - rcfg = os_zalloc(sizeof(*rcfg)); - if (rcfg == NULL) - goto failed; - rcfg->ctx = drv; - os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); - rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; - rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; - drv->rfkill = rfkill_init(rcfg); - if (drv->rfkill == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); - os_free(rcfg); - } - - if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0) - drv->start_iface_up = 1; - if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params)) goto failed; @@ -1640,6 +1785,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, } if (drv->global) { + nl80211_check_global(drv->global); dl_list_add(&drv->global->interfaces, &drv->list); drv->in_interface_list = 1; } @@ -1807,6 +1953,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) ret = -1; } #endif /* CONFIG_TDLS */ +#ifdef CONFIG_FST + /* FST Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0) + ret = -1; +#endif /* CONFIG_FST */ /* FT Action frames */ if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) @@ -1840,6 +1991,10 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0) ret = -1; + /* Radio Measurement - Radio Measurement Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0) + ret = -1; + /* Radio Measurement - Link Measurement Request */ if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) && (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0)) @@ -1901,6 +2056,49 @@ static int nl80211_register_spurious_class3(struct i802_bss *bss) } +static int nl80211_action_subscribe_ap(struct i802_bss *bss) +{ + int ret = 0; + + /* Public Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04", 1) < 0) + ret = -1; + /* RRM Measurement Report */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0) + ret = -1; + /* RRM Neighbor Report Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0) + ret = -1; + /* FT Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) + ret = -1; +#ifdef CONFIG_IEEE80211W + /* SA Query */ + if (nl80211_register_action_frame(bss, (u8 *) "\x08", 1) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + /* Protected Dual of Public Action */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09", 1) < 0) + ret = -1; + /* WNM */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a", 1) < 0) + ret = -1; + /* WMM */ + if (nl80211_register_action_frame(bss, (u8 *) "\x11", 1) < 0) + ret = -1; +#ifdef CONFIG_FST + /* FST Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0) + ret = -1; +#endif /* CONFIG_FST */ + /* Vendor-specific */ + if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0) + ret = -1; + + return ret; +} + + static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) { static const int stypes[] = { @@ -1909,7 +2107,6 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) WLAN_FC_STYPE_REASSOC_REQ, WLAN_FC_STYPE_DISASSOC, WLAN_FC_STYPE_DEAUTH, - WLAN_FC_STYPE_ACTION, WLAN_FC_STYPE_PROBE_REQ, /* Beacon doesn't work as mac80211 doesn't currently allow * it, but it wouldn't really be the right thing anyway as @@ -1934,6 +2131,9 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) } } + if (nl80211_action_subscribe_ap(bss)) + goto out_err; + if (nl80211_register_spurious_class3(bss)) goto out_err; @@ -1956,10 +2156,7 @@ static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " "handle %p (device SME)", bss->nl_mgmt); - if (nl80211_register_frame(bss, bss->nl_mgmt, - (WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_ACTION << 4), - NULL, 0) < 0) + if (nl80211_action_subscribe_ap(bss)) goto out_err; nl80211_mgmt_handle_register_eloop(bss); @@ -2035,6 +2232,60 @@ static int i802_set_iface_flags(struct i802_bss *bss, int up) } +#ifdef CONFIG_TESTING_OPTIONS +static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg) +{ + /* struct wpa_driver_nl80211_data *drv = arg; */ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + + wpa_printf(MSG_DEBUG, + "nl80211: QCA vendor test command response received"); + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_VENDOR_DATA]) { + wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute"); + return NL_SKIP; + } + + wpa_hexdump(MSG_DEBUG, + "nl80211: Received QCA vendor test command response", + nla_data(tb[NL80211_ATTR_VENDOR_DATA]), + nla_len(tb[NL80211_ATTR_VENDOR_DATA])); + + return NL_SKIP; +} +#endif /* CONFIG_TESTING_OPTIONS */ + + +static void qca_vendor_test(struct wpa_driver_nl80211_data *drv) +{ +#ifdef CONFIG_TESTING_OPTIONS + struct nl_msg *msg; + struct nlattr *params; + int ret; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_TEST) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) { + nlmsg_free(msg); + return; + } + nla_nest_end(msg, params); + + ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv); + wpa_printf(MSG_DEBUG, + "nl80211: QCA vendor test command returned %d (%s)", + ret, strerror(-ret)); +#endif /* CONFIG_TESTING_OPTIONS */ +} + + static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, const u8 *set_addr, int first, @@ -2056,6 +2307,11 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) bss->static_ap = 1; + if (first && + nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE && + linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0) + drv->start_iface_up = 1; + if (wpa_driver_nl80211_capa(drv)) return -1; @@ -2089,6 +2345,8 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (nlmode == NL80211_IFTYPE_P2P_DEVICE) nl80211_get_macaddr(bss); + wpa_driver_nl80211_drv_init_rfkill(drv); + if (!rfkill_is_blocked(drv->rfkill)) { int ret = i802_set_iface_flags(bss, 1); if (ret) { @@ -2096,31 +2354,41 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, "interface '%s' UP", bss->ifname); return ret; } + + if (is_p2p_net_interface(nlmode)) + nl80211_disable_11b_rates(bss->drv, + bss->drv->ifindex, 1); + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) return ret; } else { wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " "interface '%s' due to rfkill", bss->ifname); - if (nlmode == NL80211_IFTYPE_P2P_DEVICE) - return 0; - drv->if_disabled = 1; + if (nlmode != NL80211_IFTYPE_P2P_DEVICE) + drv->if_disabled = 1; + send_rfkill_event = 1; } - if (!drv->hostapd) + if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE) netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 1, IF_OPER_DORMANT); - if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - bss->addr)) - return -1; - os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN); + if (nlmode != NL80211_IFTYPE_P2P_DEVICE) { + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + bss->addr)) + return -1; + os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN); + } if (send_rfkill_event) { eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, drv, drv->ctx); } + if (drv->vendor_cmd_test_avail) + qca_vendor_test(drv); + return 0; } @@ -2146,6 +2414,7 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) static void wpa_driver_nl80211_deinit(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; + unsigned int i; wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d", bss->ifname, drv->disabled_11b_rates); @@ -2192,6 +2461,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) if (drv->if_indices != drv->default_if_indices) os_free(drv->if_indices); + if (drv->if_indices_reason != drv->default_if_indices_reason) + os_free(drv->if_indices_reason); + if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); @@ -2239,6 +2511,10 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) os_free(drv->extended_capa); os_free(drv->extended_capa_mask); + for (i = 0; i < drv->num_iface_ext_capa; i++) { + os_free(drv->iface_ext_capa[i].ext_capa); + os_free(drv->iface_ext_capa[i].ext_capa_mask); + } os_free(drv->first_bss); os_free(drv); } @@ -2335,6 +2611,7 @@ static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[], } +#ifdef CONFIG_DRIVER_NL80211_QCA static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, const u8 *key, size_t key_len) { @@ -2362,6 +2639,7 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, return ret; } +#endif /* CONFIG_DRIVER_NL80211_QCA */ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, @@ -2372,7 +2650,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, { struct wpa_driver_nl80211_data *drv = bss->drv; int ifindex; - struct nl_msg *msg; + struct nl_msg *msg = NULL; int ret; int tdls = 0; @@ -2392,6 +2670,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, } #endif /* CONFIG_TDLS */ +#ifdef CONFIG_DRIVER_NL80211_QCA if (alg == WPA_ALG_PMK && (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key", @@ -2399,17 +2678,22 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, ret = issue_key_mgmt_set_key(drv, key, key_len); return ret; } +#endif /* CONFIG_DRIVER_NL80211_QCA */ if (alg == WPA_ALG_NONE) { msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY); if (!msg) return -ENOBUFS; } else { + u32 suite; + + suite = wpa_alg_to_cipher_suite(alg, key_len); + if (!suite) + goto fail; msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY); if (!msg || nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) || - nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, - wpa_alg_to_cipher_suite(alg, key_len))) + nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, suite)) goto fail; wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len); } @@ -2465,7 +2749,10 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY); if (!msg || nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) || - nla_put_flag(msg, alg == WPA_ALG_IGTK ? + nla_put_flag(msg, (alg == WPA_ALG_IGTK || + alg == WPA_ALG_BIP_GMAC_128 || + alg == WPA_ALG_BIP_GMAC_256 || + alg == WPA_ALG_BIP_CMAC_256) ? NL80211_ATTR_KEY_DEFAULT_MGMT : NL80211_ATTR_KEY_DEFAULT)) goto fail; @@ -2508,9 +2795,15 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, const u8 *key, size_t key_len) { struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY); + u32 suite; + if (!key_attr) return -1; + suite = wpa_alg_to_cipher_suite(alg, key_len); + if (!suite) + return -1; + if (defkey && alg == WPA_ALG_IGTK) { if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT)) return -1; @@ -2520,8 +2813,7 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, } if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) || - nla_put_u32(msg, NL80211_KEY_CIPHER, - wpa_alg_to_cipher_suite(alg, key_len)) || + nla_put_u32(msg, NL80211_KEY_CIPHER, suite) || (seq && seq_len && nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) || nla_put(msg, NL80211_KEY_DATA, key_len, key)) @@ -2944,7 +3236,9 @@ static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, const void *data, size_t len, int encrypt, int noack, unsigned int freq, int no_cck, - int offchanok, unsigned int wait_time) + int offchanok, unsigned int wait_time, + const u16 *csa_offs, + size_t csa_offs_len) { struct wpa_driver_nl80211_data *drv = bss->drv; u64 cookie; @@ -2970,7 +3264,8 @@ static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd"); res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len, - &cookie, no_cck, noack, offchanok); + &cookie, no_cck, noack, offchanok, + csa_offs, csa_offs_len); if (res == 0 && !noack) { const struct ieee80211_mgmt *mgmt; u16 fc; @@ -2996,7 +3291,9 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, size_t data_len, int noack, unsigned int freq, int no_cck, int offchanok, - unsigned int wait_time) + unsigned int wait_time, + const u16 *csa_offs, + size_t csa_offs_len) { struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt *mgmt; @@ -3026,7 +3323,7 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, } return nl80211_send_frame_cmd(bss, freq, 0, data, data_len, NULL, 1, noack, - 1); + 1, csa_offs, csa_offs_len); } if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { @@ -3040,7 +3337,8 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, wait_time, data, data_len, &drv->send_action_cookie, - no_cck, noack, offchanok); + no_cck, noack, offchanok, + csa_offs, csa_offs_len); } if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && @@ -3060,13 +3358,30 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame"); return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, noack, freq, no_cck, offchanok, - wait_time); + wait_time, csa_offs, + csa_offs_len); +} + + +static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates) +{ + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; + + if (!basic_rates) + return 0; + + for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) + rates[rates_len++] = basic_rates[i] / 5; + + return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); } static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, int slot, int ht_opmode, int ap_isolate, - int *basic_rates) + const int *basic_rates) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; @@ -3081,27 +3396,13 @@ static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, (ht_opmode >= 0 && nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) || (ap_isolate >= 0 && - nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate))) - goto fail; - - if (basic_rates) { - u8 rates[NL80211_MAX_SUPP_RATES]; - u8 rates_len = 0; - int i; - - for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; - i++) - rates[rates_len++] = basic_rates[i] / 5; - - if (nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, - rates)) - goto fail; + nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) || + nl80211_put_basic_rates(msg, basic_rates)) { + nlmsg_free(msg); + return -ENOBUFS; } return send_and_recv_msgs(drv, msg, NULL, NULL); -fail: - nlmsg_free(msg); - return -ENOBUFS; } @@ -3111,7 +3412,7 @@ static int wpa_driver_nl80211_set_acl(void *priv, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - struct nlattr *acl; + struct nl_msg *acl; unsigned int i; int ret; @@ -3124,23 +3425,26 @@ static int wpa_driver_nl80211_set_acl(void *priv, wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)", params->acl_policy ? "Accept" : "Deny", params->num_mac_acl); + acl = nlmsg_alloc(); + if (!acl) + return -ENOMEM; + for (i = 0; i < params->num_mac_acl; i++) { + if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) { + nlmsg_free(acl); + return -ENOMEM; + } + } + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) || nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? NL80211_ACL_POLICY_DENY_UNLESS_LISTED : NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) || - (acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS)) == NULL) { + nla_put_nested(msg, NL80211_ATTR_MAC_ADDRS, acl)) { nlmsg_free(msg); + nlmsg_free(acl); return -ENOMEM; } - - for (i = 0; i < params->num_mac_acl; i++) { - if (nla_put(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr)) { - nlmsg_free(msg); - return -ENOMEM; - } - } - - nla_nest_end(msg, acl); + nlmsg_free(acl); ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { @@ -3152,6 +3456,18 @@ static int wpa_driver_nl80211_set_acl(void *priv, } +static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int) +{ + if (beacon_int > 0) { + wpa_printf(MSG_DEBUG, " * beacon_int=%d", beacon_int); + return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL, + beacon_int); + } + + return 0; +} + + static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_ap_params *params) { @@ -3166,7 +3482,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, u32 suites[10], suite; u32 ver; - beacon_set = bss->beacon_set; + beacon_set = params->reenable ? 0 : bss->beacon_set; wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)", beacon_set); @@ -3187,8 +3503,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, params->head) || nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail) || - nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL, - params->beacon_int) || + nl80211_put_beacon_int(msg, params->beacon_int) || nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) || nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; @@ -3280,24 +3595,26 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite)) goto fail; - switch (params->smps_mode) { - case HT_CAP_INFO_SMPS_DYNAMIC: - wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic"); - smps_mode = NL80211_SMPS_DYNAMIC; - break; - case HT_CAP_INFO_SMPS_STATIC: - wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static"); - smps_mode = NL80211_SMPS_STATIC; - break; - default: - /* invalid - fallback to smps off */ - case HT_CAP_INFO_SMPS_DISABLED: - wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off"); - smps_mode = NL80211_SMPS_OFF; - break; + if (params->ht_opmode != -1) { + switch (params->smps_mode) { + case HT_CAP_INFO_SMPS_DYNAMIC: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic"); + smps_mode = NL80211_SMPS_DYNAMIC; + break; + case HT_CAP_INFO_SMPS_STATIC: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static"); + smps_mode = NL80211_SMPS_STATIC; + break; + default: + /* invalid - fallback to smps off */ + case HT_CAP_INFO_SMPS_DISABLED: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off"); + smps_mode = NL80211_SMPS_OFF; + break; + } + if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) + goto fail; } - if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) - goto fail; if (params->beacon_ies) { wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies", @@ -3332,14 +3649,35 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", - ret, strerror(-ret)); - } else { - bss->beacon_set = 1; - nl80211_set_bss(bss, params->cts_protect, params->preamble, - params->short_slot_time, params->ht_opmode, +#ifdef CONFIG_P2P + if (params->p2p_go_ctwindow > 0) { + if (drv->p2p_go_ctwindow_supported) { + wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d", + params->p2p_go_ctwindow); + if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW, + params->p2p_go_ctwindow)) + goto fail; + } else { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support CTWindow configuration - ignore this parameter"); + } + } +#endif /* CONFIG_P2P */ + + if (params->pbss) { + wpa_printf(MSG_DEBUG, "nl80211: PBSS"); + if (nla_put_flag(msg, NL80211_ATTR_PBSS)) + goto fail; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", + ret, strerror(-ret)); + } else { + bss->beacon_set = 1; + nl80211_set_bss(bss, params->cts_protect, params->preamble, + params->short_slot_time, params->ht_opmode, params->isolate, params->basic_rates); if (beacon_set && params->freq && params->freq->bandwidth != bss->bandwidth) { @@ -3376,12 +3714,17 @@ fail: static int nl80211_put_freq_params(struct nl_msg *msg, const struct hostapd_freq_params *freq) { + wpa_printf(MSG_DEBUG, " * freq=%d", freq->freq); if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq)) return -ENOBUFS; + wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled); + wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled); + if (freq->vht_enabled) { enum nl80211_chan_width cw; + wpa_printf(MSG_DEBUG, " * bandwidth=%d", freq->bandwidth); switch (freq->bandwidth) { case 20: cw = NL80211_CHAN_WIDTH_20; @@ -3402,6 +3745,11 @@ static int nl80211_put_freq_params(struct nl_msg *msg, return -EINVAL; } + wpa_printf(MSG_DEBUG, " * channel_width=%d", cw); + wpa_printf(MSG_DEBUG, " * center_freq1=%d", + freq->center_freq1); + wpa_printf(MSG_DEBUG, " * center_freq2=%d", + freq->center_freq2); if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) || nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1) || @@ -3412,6 +3760,8 @@ static int nl80211_put_freq_params(struct nl_msg *msg, } else if (freq->ht_enabled) { enum nl80211_channel_type ct; + wpa_printf(MSG_DEBUG, " * sec_channel_offset=%d", + freq->sec_channel_offset); switch (freq->sec_channel_offset) { case -1: ct = NL80211_CHAN_HT40MINUS; @@ -3424,6 +3774,7 @@ static int nl80211_put_freq_params(struct nl_msg *msg, break; } + wpa_printf(MSG_DEBUG, " * channel_type=%d", ct); if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct)) return -ENOBUFS; } @@ -3477,6 +3828,8 @@ static u32 sta_flags_nl80211(int flags) f |= BIT(NL80211_STA_FLAG_TDLS_PEER); if (flags & WPA_STA_AUTHENTICATED) f |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (flags & WPA_STA_ASSOCIATED) + f |= BIT(NL80211_STA_FLAG_ASSOCIATED); return f; } @@ -3486,11 +3839,11 @@ static u32 sta_flags_nl80211(int flags) static u32 sta_plink_state_nl80211(enum mesh_plink_state state) { switch (state) { - case PLINK_LISTEN: + case PLINK_IDLE: return NL80211_PLINK_LISTEN; - case PLINK_OPEN_SENT: + case PLINK_OPN_SNT: return NL80211_PLINK_OPN_SNT; - case PLINK_OPEN_RCVD: + case PLINK_OPN_RCVD: return NL80211_PLINK_OPN_RCVD; case PLINK_CNF_RCVD: return NL80211_PLINK_CNF_RCVD; @@ -3529,7 +3882,17 @@ static int wpa_driver_nl80211_sta_add(void *priv, if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr)) goto fail; - if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) { + /* + * Set the below properties only in one of the following cases: + * 1. New station is added, already associated. + * 2. Set WPA_STA_TDLS_PEER station. + * 3. Set an already added unassociated station, if driver supports + * full AP client state. (Set these properties after station became + * associated will be rejected by the driver). + */ + if (!params->set || (params->flags & WPA_STA_TDLS_PEER) || + (params->set && FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) && + (params->flags & WPA_STA_ASSOCIATED))) { wpa_hexdump(MSG_DEBUG, " * supported rates", params->supp_rates, params->supp_rates_len); wpa_printf(MSG_DEBUG, " * capability=0x%x", @@ -3567,6 +3930,13 @@ static int wpa_driver_nl80211_sta_add(void *priv, params->ext_capab_len, params->ext_capab)) goto fail; } + + if (is_ap_interface(drv->nlmode) && + nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS, + params->support_p2p_ps ? + NL80211_P2P_PS_SUPPORTED : + NL80211_P2P_PS_UNSUPPORTED)) + goto fail; } if (!params->set) { if (params->aid) { @@ -3577,9 +3947,12 @@ static int wpa_driver_nl80211_sta_add(void *priv, /* * cfg80211 validates that AID is non-zero, so we have * to make this a non-zero value for the TDLS case where - * a dummy STA entry is used for now. + * a dummy STA entry is used for now and for a station + * that is still not associated. */ - wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)"); + wpa_printf(MSG_DEBUG, " * aid=1 (%s workaround)", + (params->flags & WPA_STA_TDLS_PEER) ? + "TDLS" : "UNASSOC_STA"); if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1)) goto fail; } @@ -3592,6 +3965,15 @@ static int wpa_driver_nl80211_sta_add(void *priv, wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid); if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid)) goto fail; + } else if (FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) && + (params->flags & WPA_STA_ASSOCIATED)) { + wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); + wpa_printf(MSG_DEBUG, " * listen_interval=%u", + params->listen_interval); + if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid) || + nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval)) + goto fail; } if (params->vht_opmode_enabled) { @@ -3622,6 +4004,36 @@ static int wpa_driver_nl80211_sta_add(void *priv, os_memset(&upd, 0, sizeof(upd)); upd.set = sta_flags_nl80211(params->flags); upd.mask = upd.set | sta_flags_nl80211(params->flags_mask); + + /* + * If the driver doesn't support full AP client state, ignore ASSOC/AUTH + * flags, as nl80211 driver moves a new station, by default, into + * associated state. + * + * On the other hand, if the driver supports that feature and the + * station is added in unauthenticated state, set the + * authenticated/associated bits in the mask to prevent moving this + * station to associated state before it is actually associated. + * + * This is irrelevant for mesh mode where the station is added to the + * driver as authenticated already, and ASSOCIATED isn't part of the + * nl80211 API. + */ + if (!is_mesh_interface(drv->nlmode)) { + if (!FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore ASSOC/AUTH flags since driver doesn't support full AP client state"); + upd.mask &= ~(BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_AUTHENTICATED)); + } else if (!params->set && + !(params->flags & WPA_STA_TDLS_PEER)) { + if (!(params->flags & WPA_STA_AUTHENTICATED)) + upd.mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (!(params->flags & WPA_STA_ASSOCIATED)) + upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); + } + } + wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x", upd.set, upd.mask); if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) @@ -3744,7 +4156,11 @@ void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx) /* stop listening for EAPOL on this interface */ dl_list_for_each(drv2, &drv->global->interfaces, struct wpa_driver_nl80211_data, list) - del_ifidx(drv2, ifidx); + { + del_ifidx(drv2, ifidx, IFIDX_ANY); + /* Remove all bridges learned for this iface */ + del_ifidx(drv2, IFIDX_ANY, ifidx); + } msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE); if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) @@ -3753,7 +4169,7 @@ void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx) } -static const char * nl80211_iftype_str(enum nl80211_iftype mode) +const char * nl80211_iftype_str(enum nl80211_iftype mode) { switch (mode) { case NL80211_IFTYPE_ADHOC: @@ -3852,7 +4268,7 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, iftype == NL80211_IFTYPE_WDS || iftype == NL80211_IFTYPE_MONITOR) { /* start listening for EAPOL on this interface */ - add_ifidx(drv, ifidx); + add_ifidx(drv, ifidx, IFIDX_ANY); } if (addr && iftype != NL80211_IFTYPE_MONITOR && @@ -3935,7 +4351,8 @@ static int nl80211_setup_ap(struct i802_bss *bss) if (drv->device_ap_sme && !drv->use_monitor) if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) - return -1; + wpa_printf(MSG_DEBUG, + "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it"); if (!drv->device_ap_sme && drv->use_monitor && nl80211_create_monitor_interface(drv) && @@ -4055,7 +4472,7 @@ static int wpa_driver_nl80211_hapd_send_eapol( memcpy(pos, data, data_len); res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0, - 0, 0, 0, 0); + 0, 0, 0, 0, NULL, 0); if (res < 0) { wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " "failed: %d (%s)", @@ -4068,8 +4485,9 @@ static int wpa_driver_nl80211_hapd_send_eapol( static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, - int total_flags, - int flags_or, int flags_and) + unsigned int total_flags, + unsigned int flags_or, + unsigned int flags_and) { struct i802_bss *bss = priv; struct nl_msg *msg; @@ -4136,7 +4554,8 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, return -1; } - if (nl80211_set_channel(drv->first_bss, ¶ms->freq, 0)) { + if (params->freq.freq && + nl80211_set_channel(drv->first_bss, ¶ms->freq, 0)) { if (old_mode != nlmode) wpa_driver_nl80211_set_mode(drv->first_bss, old_mode); nl80211_remove_monitor_interface(drv); @@ -4174,6 +4593,48 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, } +static int nl80211_ht_vht_overrides(struct nl_msg *msg, + struct wpa_driver_associate_params *params) +{ + if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT)) + return -1; + + if (params->htcaps && params->htcaps_mask) { + int sz = sizeof(struct ieee80211_ht_capabilities); + wpa_hexdump(MSG_DEBUG, " * htcaps", params->htcaps, sz); + wpa_hexdump(MSG_DEBUG, " * htcaps_mask", + params->htcaps_mask, sz); + if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz, + params->htcaps) || + nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, + params->htcaps_mask)) + return -1; + } + +#ifdef CONFIG_VHT_OVERRIDES + if (params->disable_vht) { + wpa_printf(MSG_DEBUG, " * VHT disabled"); + if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT)) + return -1; + } + + if (params->vhtcaps && params->vhtcaps_mask) { + int sz = sizeof(struct ieee80211_vht_capabilities); + wpa_hexdump(MSG_DEBUG, " * vhtcaps", params->vhtcaps, sz); + wpa_hexdump(MSG_DEBUG, " * vhtcaps_mask", + params->vhtcaps_mask, sz); + if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz, + params->vhtcaps) || + nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz, + params->vhtcaps_mask)) + return -1; + } +#endif /* CONFIG_VHT_OVERRIDES */ + + return 0; +} + + static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { @@ -4201,24 +4662,10 @@ retry: os_memcpy(drv->ssid, params->ssid, params->ssid_len); drv->ssid_len = params->ssid_len; - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq); - wpa_printf(MSG_DEBUG, " * ht_enabled=%d", params->freq.ht_enabled); - wpa_printf(MSG_DEBUG, " * sec_channel_offset=%d", - params->freq.sec_channel_offset); - wpa_printf(MSG_DEBUG, " * vht_enabled=%d", params->freq.vht_enabled); - wpa_printf(MSG_DEBUG, " * center_freq1=%d", params->freq.center_freq1); - wpa_printf(MSG_DEBUG, " * center_freq2=%d", params->freq.center_freq2); - wpa_printf(MSG_DEBUG, " * bandwidth=%d", params->freq.bandwidth); - if (nl80211_put_freq_params(msg, ¶ms->freq) < 0) + if (nl80211_put_freq_params(msg, ¶ms->freq) < 0 || + nl80211_put_beacon_int(msg, params->beacon_int)) goto fail; - if (params->beacon_int > 0) { - wpa_printf(MSG_DEBUG, " * beacon_int=%d", params->beacon_int); - if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL, - params->beacon_int)) - goto fail; - } - ret = nl80211_set_conn_keys(params, msg); if (ret) goto fail; @@ -4230,6 +4677,12 @@ retry: goto fail; } + if (params->fixed_freq) { + wpa_printf(MSG_DEBUG, " * fixed_freq"); + if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED)) + goto fail; + } + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || params->key_mgmt_suite == WPA_KEY_MGMT_PSK || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || @@ -4248,6 +4701,10 @@ retry: goto fail; } + ret = nl80211_ht_vht_overrides(msg, params); + if (ret < 0) + goto fail; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -4375,7 +4832,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_OSEN || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || - params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { int mgmt = WLAN_AKM_SUITE_PSK; switch (params->key_mgmt_suite) { @@ -4403,6 +4861,9 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, case WPA_KEY_MGMT_IEEE8021X_SUITE_B: mgmt = WLAN_AKM_SUITE_8021X_SUITE_B; break; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192; + break; case WPA_KEY_MGMT_PSK: default: mgmt = WLAN_AKM_SUITE_PSK; @@ -4422,50 +4883,35 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, if (params->rrm_used) { u32 drv_rrm_flags = drv->capa.rrm_flags; - if (!(drv_rrm_flags & - WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) || - !(drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET) || + if ((!((drv_rrm_flags & + WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) && + (drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) && + !(drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) || nla_put_flag(msg, NL80211_ATTR_USE_RRM)) return -1; } - if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT)) + if (nl80211_ht_vht_overrides(msg, params) < 0) return -1; - if (params->htcaps && params->htcaps_mask) { - int sz = sizeof(struct ieee80211_ht_capabilities); - wpa_hexdump(MSG_DEBUG, " * htcaps", params->htcaps, sz); - wpa_hexdump(MSG_DEBUG, " * htcaps_mask", - params->htcaps_mask, sz); - if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz, - params->htcaps) || - nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, - params->htcaps_mask)) - return -1; - } + if (params->p2p) + wpa_printf(MSG_DEBUG, " * P2P group"); -#ifdef CONFIG_VHT_OVERRIDES - if (params->disable_vht) { - wpa_printf(MSG_DEBUG, " * VHT disabled"); - if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT)) + if (params->pbss) { + wpa_printf(MSG_DEBUG, " * PBSS"); + if (nla_put_flag(msg, NL80211_ATTR_PBSS)) return -1; } - if (params->vhtcaps && params->vhtcaps_mask) { - int sz = sizeof(struct ieee80211_vht_capabilities); - wpa_hexdump(MSG_DEBUG, " * vhtcaps", params->vhtcaps, sz); - wpa_hexdump(MSG_DEBUG, " * vhtcaps_mask", - params->vhtcaps_mask, sz); - if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz, - params->vhtcaps) || - nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz, - params->vhtcaps_mask)) + drv->connect_reassoc = 0; + if (params->prev_bssid) { + wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, + MAC2STR(params->prev_bssid)); + if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, + params->prev_bssid)) return -1; + drv->connect_reassoc = 1; } -#endif /* CONFIG_VHT_OVERRIDES */ - - if (params->p2p) - wpa_printf(MSG_DEBUG, " * P2P group"); return 0; } @@ -4480,6 +4926,7 @@ static int wpa_driver_nl80211_try_connect( int ret; int algs; +#ifdef CONFIG_DRIVER_NL80211_QCA if (params->req_key_mgmt_offload && params->psk && (params->key_mgmt_suite == WPA_KEY_MGMT_PSK || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || @@ -4489,6 +4936,7 @@ static int wpa_driver_nl80211_try_connect( if (ret) return ret; } +#endif /* CONFIG_DRIVER_NL80211_QCA */ wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT); @@ -4617,14 +5065,6 @@ static int wpa_driver_nl80211_associate( if (ret) goto fail; - if (params->prev_bssid) { - wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, - MAC2STR(params->prev_bssid)); - if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, - params->prev_bssid)) - goto fail; - } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -4680,6 +5120,9 @@ static int wpa_driver_nl80211_set_mode_impl( int res; int mode_switch_res; + if (TEST_FAIL()) + return -1; + mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode); if (mode_switch_res && nlmode == nl80211_get_ifmode(bss)) mode_switch_res = 0; @@ -5036,6 +5479,8 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 }, + [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -5061,10 +5506,23 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) if (stats[NL80211_STA_INFO_INACTIVE_TIME]) data->inactive_msec = nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); + /* For backwards compatibility, fetch the 32-bit counters first. */ if (stats[NL80211_STA_INFO_RX_BYTES]) data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); if (stats[NL80211_STA_INFO_TX_BYTES]) data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); + if (stats[NL80211_STA_INFO_RX_BYTES64] && + stats[NL80211_STA_INFO_TX_BYTES64]) { + /* + * The driver supports 64-bit counters, so use them to override + * the 32-bit values. + */ + data->rx_bytes = + nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]); + data->tx_bytes = + nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]); + data->bytes_64bit = 1; + } if (stats[NL80211_STA_INFO_RX_PACKETS]) data->rx_packets = nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); @@ -5193,6 +5651,8 @@ static int i802_get_inact_sec(void *priv, const u8 *addr) data.inactive_msec = (unsigned long) -1; ret = i802_read_sta_data(priv, &data, addr); + if (ret == -ENOENT) + return -ENOENT; if (ret || data.inactive_msec == (unsigned long) -1) return -1; return data.inactive_msec / 1000; @@ -5231,7 +5691,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth), 0, 0, 0, 0, - 0); + 0, NULL, 0); } @@ -5258,7 +5718,7 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.disassoc), 0, 0, 0, 0, - 0); + 0, NULL, 0); } @@ -5273,7 +5733,9 @@ static void dump_ifidx(struct wpa_driver_nl80211_data *drv) for (i = 0; i < drv->num_if_indices; i++) { if (!drv->if_indices[i]) continue; - res = os_snprintf(pos, end - pos, " %d", drv->if_indices[i]); + res = os_snprintf(pos, end - pos, " %d(%d)", + drv->if_indices[i], + drv->if_indices_reason[i]); if (os_snprintf_error(end - pos, res)) break; pos += res; @@ -5285,14 +5747,16 @@ static void dump_ifidx(struct wpa_driver_nl80211_data *drv) } -static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason) { int i; - int *old; + int *old, *old_reason; - wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", - ifidx); - if (have_ifidx(drv, ifidx)) { + wpa_printf(MSG_DEBUG, + "nl80211: Add own interface ifindex %d (ifidx_reason %d)", + ifidx, ifidx_reason); + if (have_ifidx(drv, ifidx, ifidx_reason)) { wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list", ifidx); return; @@ -5300,6 +5764,7 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) for (i = 0; i < drv->num_if_indices; i++) { if (drv->if_indices[i] == 0) { drv->if_indices[i] = ifidx; + drv->if_indices_reason[i] = ifidx_reason; dump_ifidx(drv); return; } @@ -5310,32 +5775,57 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) else old = NULL; + if (drv->if_indices_reason != drv->default_if_indices_reason) + old_reason = drv->if_indices_reason; + else + old_reason = NULL; + drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1, sizeof(int)); + drv->if_indices_reason = os_realloc_array(old_reason, + drv->num_if_indices + 1, + sizeof(int)); if (!drv->if_indices) { if (!old) drv->if_indices = drv->default_if_indices; else drv->if_indices = old; + } + if (!drv->if_indices_reason) { + if (!old_reason) + drv->if_indices_reason = drv->default_if_indices_reason; + else + drv->if_indices_reason = old_reason; + } + if (!drv->if_indices || !drv->if_indices_reason) { wpa_printf(MSG_ERROR, "Failed to reallocate memory for " "interfaces"); wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); return; - } else if (!old) + } + if (!old) os_memcpy(drv->if_indices, drv->default_if_indices, sizeof(drv->default_if_indices)); + if (!old_reason) + os_memcpy(drv->if_indices_reason, + drv->default_if_indices_reason, + sizeof(drv->default_if_indices_reason)); drv->if_indices[drv->num_if_indices] = ifidx; + drv->if_indices_reason[drv->num_if_indices] = ifidx_reason; drv->num_if_indices++; dump_ifidx(drv); } -static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason) { int i; for (i = 0; i < drv->num_if_indices; i++) { - if (drv->if_indices[i] == ifidx) { + if ((drv->if_indices[i] == ifidx || ifidx == IFIDX_ANY) && + (drv->if_indices_reason[i] == ifidx_reason || + ifidx_reason == IFIDX_ANY)) { drv->if_indices[i] = 0; break; } @@ -5344,12 +5834,15 @@ static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) } -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason) { int i; for (i = 0; i < drv->num_if_indices; i++) - if (drv->if_indices[i] == ifidx) + if (drv->if_indices[i] == ifidx && + (drv->if_indices_reason[i] == ifidx_reason || + ifidx_reason == IFIDX_ANY)) return 1; return 0; @@ -5414,7 +5907,7 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) return; } - if (have_ifidx(drv, lladdr.sll_ifindex)) + if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY)) drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); } @@ -5441,7 +5934,7 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, } bss->added_bridge = 1; br_ifindex = if_nametoindex(brname); - add_ifidx(drv, br_ifindex); + add_ifidx(drv, br_ifindex, drv->ifindex); } bss->br_ifindex = br_ifindex; @@ -5481,8 +5974,8 @@ static void *i802_init(struct hostapd_data *hapd, struct wpa_driver_nl80211_data *drv; struct i802_bss *bss; size_t i; - char brname[IFNAMSIZ]; - int ifindex, br_ifindex; + char master_ifname[IFNAMSIZ]; + int ifindex, br_ifindex = 0; int br_added = 0; bss = wpa_driver_nl80211_drv_init(hapd, params->ifname, @@ -5493,41 +5986,55 @@ static void *i802_init(struct hostapd_data *hapd, drv = bss->drv; - if (linux_br_get(brname, params->ifname) == 0) { + if (linux_br_get(master_ifname, params->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", - params->ifname, brname); - br_ifindex = if_nametoindex(brname); - os_strlcpy(bss->brname, brname, IFNAMSIZ); + params->ifname, master_ifname); + br_ifindex = if_nametoindex(master_ifname); + os_strlcpy(bss->brname, master_ifname, IFNAMSIZ); + } else if ((params->num_bridge == 0 || !params->bridge[0]) && + linux_master_get(master_ifname, params->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s", + params->ifname, master_ifname); + /* start listening for EAPOL on the master interface */ + add_ifidx(drv, if_nametoindex(master_ifname), drv->ifindex); + + /* check if master itself is under bridge */ + if (linux_br_get(master_ifname, master_ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: which is in bridge %s", + master_ifname); + br_ifindex = if_nametoindex(master_ifname); + os_strlcpy(bss->brname, master_ifname, IFNAMSIZ); + } } else { - brname[0] = '\0'; - br_ifindex = 0; + master_ifname[0] = '\0'; } + bss->br_ifindex = br_ifindex; for (i = 0; i < params->num_bridge; i++) { if (params->bridge[i]) { ifindex = if_nametoindex(params->bridge[i]); if (ifindex) - add_ifidx(drv, ifindex); + add_ifidx(drv, ifindex, drv->ifindex); if (ifindex == br_ifindex) br_added = 1; } } /* start listening for EAPOL on the default AP interface */ - add_ifidx(drv, drv->ifindex); + add_ifidx(drv, drv->ifindex, IFIDX_ANY); if (params->num_bridge && params->bridge[0]) { if (i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) goto failed; - if (os_strcmp(params->bridge[0], brname) != 0) + if (os_strcmp(params->bridge[0], master_ifname) != 0) br_added = 1; } if (!br_added && br_ifindex && (params->num_bridge == 0 || !params->bridge[0])) - add_ifidx(drv, br_ifindex); + add_ifidx(drv, br_ifindex, drv->ifindex); #ifdef CONFIG_LIBNL3_ROUTE if (bss->added_if_into_bridge) { @@ -5599,13 +6106,12 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( return NL80211_IFTYPE_P2P_DEVICE; case WPA_IF_MESH: return NL80211_IFTYPE_MESH_POINT; + default: + return -1; } - return -1; } -#if defined(CONFIG_P2P) || defined(CONFIG_MESH) - static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) { struct wpa_driver_nl80211_data *drv; @@ -5641,8 +6147,6 @@ static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr) return 0; } -#endif /* CONFIG_P2P || CONFIG_MESH */ - struct wdev_info { u64 wdev_id; @@ -5675,7 +6179,8 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, - const char *bridge, int use_existing) + const char *bridge, int use_existing, + int setup_ap) { enum nl80211_iftype nlmode; struct i802_bss *bss = priv; @@ -5718,21 +6223,21 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } if (!addr) { - if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) os_memcpy(if_addr, bss->addr, ETH_ALEN); else if (linux_get_ifhwaddr(drv->global->ioctl_sock, - bss->ifname, if_addr) < 0) { + ifname, if_addr) < 0) { if (added) nl80211_remove_iface(drv, ifidx); return -1; } } -#if defined(CONFIG_P2P) || defined(CONFIG_MESH) if (!addr && (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || - type == WPA_IF_P2P_GO || type == WPA_IF_MESH)) { - /* Enforce unique P2P Interface Address */ + type == WPA_IF_P2P_GO || type == WPA_IF_MESH || + type == WPA_IF_STATION)) { + /* Enforce unique address */ u8 new_addr[ETH_ALEN]; if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, @@ -5743,8 +6248,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } if (nl80211_addr_in_use(drv->global, new_addr)) { wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " - "for %s interface", type == WPA_IF_MESH ? - "mesh" : "P2P group"); + "for interface %s type %d", ifname, type); if (nl80211_vif_addr(drv, new_addr) < 0) { if (added) nl80211_remove_iface(drv, ifidx); @@ -5759,9 +6263,8 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } os_memcpy(if_addr, new_addr, ETH_ALEN); } -#endif /* CONFIG_P2P || CONFIG_MESH */ - if (type == WPA_IF_AP_BSS) { + if (type == WPA_IF_AP_BSS && setup_ap) { struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); if (new_bss == NULL) { if (added) @@ -5817,7 +6320,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, nlmode == NL80211_IFTYPE_AP_VLAN || nlmode == NL80211_IFTYPE_WDS || nlmode == NL80211_IFTYPE_MONITOR)) - add_ifidx(drv, ifidx); + add_ifidx(drv, ifidx, IFIDX_ANY); return 0; } @@ -5837,8 +6340,10 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, else if (ifindex > 0 && !bss->added_if) { struct wpa_driver_nl80211_data *drv2; dl_list_for_each(drv2, &drv->global->interfaces, - struct wpa_driver_nl80211_data, list) - del_ifidx(drv2, ifindex); + struct wpa_driver_nl80211_data, list) { + del_ifidx(drv2, ifindex, IFIDX_ANY); + del_ifidx(drv2, IFIDX_ANY, ifindex); + } } if (type != WPA_IF_AP_BSS) @@ -5916,7 +6421,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, u64 *cookie_out, int no_cck, int no_ack, - int offchanok) + int offchanok, const u16 *csa_offs, + size_t csa_offs_len) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; @@ -5936,6 +6442,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) || (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) || (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) || + (csa_offs && nla_put(msg, NL80211_ATTR_CSA_C_OFFSETS_TX, + csa_offs_len * sizeof(u16), csa_offs)) || nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf)) goto fail; @@ -5953,6 +6461,20 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, if (cookie_out) *cookie_out = no_ack ? (u64) -1 : cookie; + + if (drv->num_send_action_cookies == MAX_SEND_ACTION_COOKIES) { + wpa_printf(MSG_DEBUG, + "nl80211: Drop oldest pending send action cookie 0x%llx", + (long long unsigned int) + drv->send_action_cookies[0]); + os_memmove(&drv->send_action_cookies[0], + &drv->send_action_cookies[1], + (MAX_SEND_ACTION_COOKIES - 1) * + sizeof(u64)); + drv->num_send_action_cookies--; + } + drv->send_action_cookies[drv->num_send_action_cookies] = cookie; + drv->num_send_action_cookies++; } fail: @@ -5995,29 +6517,28 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, !drv->use_monitor)) ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len, 0, freq, no_cck, 1, - wait_time); + wait_time, NULL, 0); else ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf, 24 + data_len, &drv->send_action_cookie, - no_cck, 0, 1); + no_cck, 0, 1, NULL, 0); os_free(buf); return ret; } -static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx", - (long long unsigned int) drv->send_action_cookie); + (long long unsigned int) cookie); if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) || - nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) { + nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) { nlmsg_free(msg); return; } @@ -6029,6 +6550,30 @@ static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) } +static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + unsigned int i; + u64 cookie; + + /* Cancel the last pending TX cookie */ + nl80211_frame_wait_cancel(bss, drv->send_action_cookie); + + /* + * Cancel the other pending TX cookies, if any. This is needed since + * the driver may keep a list of all pending offchannel TX operations + * and free up the radio only once they have expired or cancelled. + */ + for (i = drv->num_send_action_cookies; i > 0; i--) { + cookie = drv->send_action_cookies[i - 1]; + if (cookie != drv->send_action_cookie) + nl80211_frame_wait_cancel(bss, cookie); + } + drv->num_send_action_cookies = 0; +} + + static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, unsigned int duration) { @@ -6251,9 +6796,13 @@ static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) static void wpa_driver_nl80211_resume(void *priv) { struct i802_bss *bss = priv; + enum nl80211_iftype nlmode = nl80211_get_ifmode(bss); if (i802_set_iface_flags(bss, 1)) wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event"); + + if (is_p2p_net_interface(nlmode)) + nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1); } @@ -6326,8 +6875,12 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) os_memset(si, 0, sizeof(*si)); res = nl80211_get_link_signal(drv, si); - if (res != 0) - return res; + if (res) { + if (drv->nlmode != NL80211_IFTYPE_ADHOC && + drv->nlmode != NL80211_IFTYPE_MESH_POINT) + return res; + si->current_signal = 0; + } res = nl80211_get_channel_width(drv, si); if (res != 0) @@ -6337,61 +6890,20 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) } -static int wpa_driver_nl80211_shared_freq(void *priv) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct wpa_driver_nl80211_data *driver; - int freq = 0; - - /* - * If the same PHY is in connected state with some other interface, - * then retrieve the assoc freq. - */ - wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s", - drv->phyname); - - dl_list_for_each(driver, &drv->global->interfaces, - struct wpa_driver_nl80211_data, list) { - if (drv == driver || - os_strcmp(drv->phyname, driver->phyname) != 0 || - !driver->associated) - continue; - - wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s " - MACSTR, - driver->phyname, driver->first_bss->ifname, - MAC2STR(driver->first_bss->addr)); - if (is_ap_interface(driver->nlmode)) - freq = driver->first_bss->freq; - else - freq = nl80211_get_assoc_freq(driver); - wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d", - drv->phyname, freq); - } - - if (!freq) - wpa_printf(MSG_DEBUG, "nl80211: No shared interface for " - "PHY (%s) in associated state", drv->phyname); - - return freq; -} - - static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, int encrypt) { struct i802_bss *bss = priv; return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0, - 0, 0, 0, 0); + 0, 0, 0, 0, NULL, 0); } static int nl80211_set_param(void *priv, const char *param) { - wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); if (param == NULL) return 0; + wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); #ifdef CONFIG_P2P if (os_strstr(param, "use_p2p_group_interface=1")) { @@ -6429,7 +6941,7 @@ static int nl80211_set_param(void *priv, const char *param) } -static void * nl80211_global_init(void) +static void * nl80211_global_init(void *ctx) { struct nl80211_global *global; struct netlink_config *cfg; @@ -6437,6 +6949,7 @@ static void * nl80211_global_init(void) global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; + global->ctx = ctx; global->ioctl_sock = -1; dl_list_init(&global->interfaces); global->if_add_ifindex = -1; @@ -6723,18 +7236,24 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) } -static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, +static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len, + const u8 *kck, size_t kck_len, const u8 *replay_ctr) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nlattr *replay_nested; struct nl_msg *msg; + int ret; + + if (!drv->set_rekey_offload) + return; + wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload"); if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) || !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) || - nla_put(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek) || - nla_put(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck) || + nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) || + nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) || nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, replay_ctr)) { nl80211_nlmsg_clear(msg); @@ -6744,7 +7263,12 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, nla_nest_end(msg, replay_nested); - send_and_recv_msgs(drv, msg, NULL, (void *) -1); + ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); + if (ret == -EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, + "nl80211: Driver does not support rekey offload"); + drv->set_rekey_offload = 0; + } } @@ -6781,7 +7305,7 @@ static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr, os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0, - 0, 0) < 0) + 0, 0, NULL, 0) < 0) wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " "send poll frame"); } @@ -6792,6 +7316,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + int ret; if (!drv->poll_command_supported) { nl80211_send_null_frame(bss, own_addr, addr, qos); @@ -6804,7 +7329,12 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, return; } - send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Client probe request for " + MACSTR " failed: ret=%d (%s)", + MAC2STR(addr), ret, strerror(-ret)); + } } @@ -7045,6 +7575,19 @@ static int driver_nl80211_scan2(void *priv, struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; +#ifdef CONFIG_DRIVER_NL80211_QCA + struct wpa_driver_nl80211_data *drv = bss->drv; + + /* + * Do a vendor specific scan if possible. If only_new_results is + * set, do a normal scan since a kernel (cfg80211) BSS cache flush + * cannot be achieved through a vendor scan. The below condition may + * need to be modified if new scan flags are added in the future whose + * functionality can only be achieved through a normal scan. + */ + if (drv->scan_vendor_cmd_avail && !params->only_new_results) + return wpa_driver_nl80211_vendor_scan(bss, params); +#endif /* CONFIG_DRIVER_NL80211_QCA */ return wpa_driver_nl80211_scan(bss, params); } @@ -7081,11 +7624,14 @@ static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, static int driver_nl80211_send_mlme(void *priv, const u8 *data, - size_t data_len, int noack) + size_t data_len, int noack, + unsigned int freq, + const u16 *csa_offs, size_t csa_offs_len) { struct i802_bss *bss = priv; return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack, - 0, 0, 0, 0); + freq, 0, 0, 0, csa_offs, + csa_offs_len); } @@ -7160,7 +7706,7 @@ static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md, } -const u8 * wpa_driver_nl80211_get_macaddr(void *priv) +static const u8 * wpa_driver_nl80211_get_macaddr(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -7310,7 +7856,15 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "capa.max_stations=%u\n" "capa.probe_resp_offloads=0x%x\n" "capa.max_acl_mac_addrs=%u\n" - "capa.num_multichan_concurrent=%u\n", + "capa.num_multichan_concurrent=%u\n" + "capa.mac_addr_rand_sched_scan_supported=%d\n" + "capa.mac_addr_rand_scan_supported=%d\n" + "capa.conc_capab=%u\n" + "capa.max_conc_chan_2_4=%u\n" + "capa.max_conc_chan_5_0=%u\n" + "capa.max_sched_scan_plans=%u\n" + "capa.max_sched_scan_plan_interval=%u\n" + "capa.max_sched_scan_plan_iterations=%u\n", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth, @@ -7324,7 +7878,15 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) drv->capa.max_stations, drv->capa.probe_resp_offloads, drv->capa.max_acl_mac_addrs, - drv->capa.num_multichan_concurrent); + drv->capa.num_multichan_concurrent, + drv->capa.mac_addr_rand_sched_scan_supported, + drv->capa.mac_addr_rand_scan_supported, + drv->capa.conc_capab, + drv->capa.max_conc_chan_2_4, + drv->capa.max_conc_chan_5_0, + drv->capa.max_sched_scan_plans, + drv->capa.max_sched_scan_plan_interval, + drv->capa.max_sched_scan_plan_iterations); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -7367,6 +7929,8 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) struct wpa_driver_nl80211_data *drv = bss->drv; struct nlattr *beacon_csa; int ret = -ENOBUFS; + int csa_off_len = 0; + int i; wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)", settings->cs_count, settings->block_tx, @@ -7383,21 +7947,57 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) (drv->nlmode != NL80211_IFTYPE_P2P_GO)) return -EOPNOTSUPP; - /* check settings validity */ - if (!settings->beacon_csa.tail || - ((settings->beacon_csa.tail_len <= - settings->counter_offset_beacon) || - (settings->beacon_csa.tail[settings->counter_offset_beacon] != - settings->cs_count))) + /* + * Remove empty counters, assuming Probe Response and Beacon frame + * counters match. This implementation assumes that there are only two + * counters. + */ + if (settings->counter_offset_beacon[0] && + !settings->counter_offset_beacon[1]) { + csa_off_len = 1; + } else if (settings->counter_offset_beacon[1] && + !settings->counter_offset_beacon[0]) { + csa_off_len = 1; + settings->counter_offset_beacon[0] = + settings->counter_offset_beacon[1]; + settings->counter_offset_presp[0] = + settings->counter_offset_presp[1]; + } else if (settings->counter_offset_beacon[1] && + settings->counter_offset_beacon[0]) { + csa_off_len = 2; + } else { + wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided"); + return -EINVAL; + } + + /* Check CSA counters validity */ + if (drv->capa.max_csa_counters && + csa_off_len > drv->capa.max_csa_counters) { + wpa_printf(MSG_ERROR, + "nl80211: Too many CSA counters provided"); return -EINVAL; + } - if (settings->beacon_csa.probe_resp && - ((settings->beacon_csa.probe_resp_len <= - settings->counter_offset_presp) || - (settings->beacon_csa.probe_resp[settings->counter_offset_presp] != - settings->cs_count))) + if (!settings->beacon_csa.tail) return -EINVAL; + for (i = 0; i < csa_off_len; i++) { + u16 csa_c_off_bcn = settings->counter_offset_beacon[i]; + u16 csa_c_off_presp = settings->counter_offset_presp[i]; + + if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) || + (settings->beacon_csa.tail[csa_c_off_bcn] != + settings->cs_count)) + return -EINVAL; + + if (settings->beacon_csa.probe_resp && + ((settings->beacon_csa.probe_resp_len <= + csa_c_off_presp) || + (settings->beacon_csa.probe_resp[csa_c_off_presp] != + settings->cs_count))) + return -EINVAL; + } + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) || nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, settings->cs_count) || @@ -7420,11 +8020,13 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) if (ret) goto error; - if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON, - settings->counter_offset_beacon) || + if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON, + csa_off_len * sizeof(u16), + settings->counter_offset_beacon) || (settings->beacon_csa.probe_resp && - nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP, - settings->counter_offset_presp))) + nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP, + csa_off_len * sizeof(u16), + settings->counter_offset_presp))) goto fail; nla_nest_end(msg, beacon_csa); @@ -7639,7 +8241,7 @@ static int nl80211_set_wowlan(void *priv, wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan"); - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WOWLAN)) || + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_WOWLAN)) || !(wowlan_triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS)) || (triggers->any && @@ -7670,6 +8272,7 @@ static int nl80211_set_wowlan(void *priv, } +#ifdef CONFIG_DRIVER_NL80211_QCA static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) { struct i802_bss *bss = priv; @@ -7702,6 +8305,7 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) return send_and_recv_msgs(drv, msg, NULL, NULL); } +#endif /* CONFIG_DRIVER_NL80211_QCA */ static int nl80211_set_mac_addr(void *priv, const u8 *addr) @@ -7710,6 +8314,9 @@ static int nl80211_set_mac_addr(void *priv, const u8 *addr) struct wpa_driver_nl80211_data *drv = bss->drv; int new_addr = addr != NULL; + if (TEST_FAIL()) + return -1; + if (!addr) addr = drv->perm_addr; @@ -7757,84 +8364,35 @@ static int wpa_driver_nl80211_init_mesh(void *priv) } -static int -wpa_driver_nl80211_join_mesh(void *priv, +static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id, + size_t mesh_id_len) +{ + if (mesh_id) { + wpa_hexdump_ascii(MSG_DEBUG, " * Mesh ID (SSID)", + mesh_id, mesh_id_len); + return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id); + } + + return 0; +} + + +static int nl80211_join_mesh(struct i802_bss *bss, struct wpa_driver_mesh_join_params *params) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *container; - int ret = 0; + int ret = -1; wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH); - if (!msg) + if (!msg || + nl80211_put_freq_params(msg, ¶ms->freq) || + nl80211_put_basic_rates(msg, params->basic_rates) || + nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) || + nl80211_put_beacon_int(msg, params->beacon_int)) goto fail; - if (params->freq) { - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); - if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq)) - goto fail; - } - - if (params->ht_mode) { - unsigned int ht_value; - char *ht_mode = ""; - - switch (params->ht_mode) { - default: - case CHAN_NO_HT: - ht_value = NL80211_CHAN_NO_HT; - ht_mode = "NOHT"; - break; - case CHAN_HT20: - ht_value = NL80211_CHAN_HT20; - ht_mode = "HT20"; - break; - case CHAN_HT40PLUS: - ht_value = NL80211_CHAN_HT40PLUS; - ht_mode = "HT40+"; - break; - case CHAN_HT40MINUS: - ht_value = NL80211_CHAN_HT40MINUS; - ht_mode = "HT40-"; - break; - } - wpa_printf(MSG_DEBUG, " * ht_mode=%s", ht_mode); - if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ht_value)) - goto fail; - } - - if (params->basic_rates) { - u8 rates[NL80211_MAX_SUPP_RATES]; - u8 rates_len = 0; - int i; - - for (i = 0; i < NL80211_MAX_SUPP_RATES; i++) { - if (params->basic_rates[i] < 0) - break; - rates[rates_len++] = params->basic_rates[i] / 5; - } - - if (nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, - rates)) - goto fail; - } - - if (params->meshid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->meshid, params->meshid_len); - if (nla_put(msg, NL80211_ATTR_MESH_ID, params->meshid_len, - params->meshid)) - goto fail; - } - - if (params->beacon_int > 0) { - wpa_printf(MSG_DEBUG, " * beacon_int=%d", params->beacon_int); - if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL, - params->beacon_int)) - goto fail; - } wpa_printf(MSG_DEBUG, " * flags=%08X", params->flags); @@ -7869,10 +8427,20 @@ wpa_driver_nl80211_join_mesh(void *priv, if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) && nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0)) goto fail; - if ((params->conf.flags & WPA_DRIVER_MESH_FLAG_DRIVER_MPM) && - nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, + if (nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, params->max_peer_links)) goto fail; + + /* + * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because + * the timer could disconnect stations even in that case. + */ + if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT, + params->conf.peer_link_timeout)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT"); + goto fail; + } + nla_nest_end(msg, container); ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -7883,7 +8451,7 @@ wpa_driver_nl80211_join_mesh(void *priv, goto fail; } ret = 0; - bss->freq = params->freq; + drv->assoc_freq = bss->freq = params->freq.freq; wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully"); fail: @@ -7892,6 +8460,37 @@ fail: } +static int +wpa_driver_nl80211_join_mesh(void *priv, + struct wpa_driver_mesh_join_params *params) +{ + struct i802_bss *bss = priv; + int ret, timeout; + + timeout = params->conf.peer_link_timeout; + + /* Disable kernel inactivity timer */ + if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) + params->conf.peer_link_timeout = 0; + + ret = nl80211_join_mesh(bss, params); + if (ret == -EINVAL && params->conf.peer_link_timeout == 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Mesh join retry for peer_link_timeout"); + /* + * Old kernel does not support setting + * NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds + * into future from peer_link_timeout. + */ + params->conf.peer_link_timeout = timeout + 60; + ret = nl80211_join_mesh(priv, params); + } + + params->conf.peer_link_timeout = timeout; + return ret; +} + + static int wpa_driver_nl80211_leave_mesh(void *priv) { struct i802_bss *bss = priv; @@ -8114,7 +8713,7 @@ static const char * drv_br_port_attr_str(enum drv_br_port_attr attr) { switch (attr) { case DRV_BR_PORT_ATTR_PROXYARP: - return "proxyarp"; + return "proxyarp_wifi"; case DRV_BR_PORT_ATTR_HAIRPIN_MODE: return "hairpin_mode"; } @@ -8149,9 +8748,9 @@ static const char * drv_br_net_param_str(enum drv_br_net_param param) switch (param) { case DRV_BR_NET_PARAM_GARP_ACCEPT: return "arp_accept"; + default: + return NULL; } - - return NULL; } @@ -8163,6 +8762,13 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param, const char *param_txt; int ip_version = 4; + if (param == DRV_BR_MULTICAST_SNOOPING) { + os_snprintf(path, sizeof(path), + "/sys/devices/virtual/net/%s/bridge/multicast_snooping", + bss->brname); + goto set_val; + } + param_txt = drv_br_net_param_str(param); if (param_txt == NULL) return -EINVAL; @@ -8178,6 +8784,7 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param, os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s", ip_version, bss->brname, param_txt); +set_val: if (linux_write_system_file(path, val)) return -1; @@ -8185,6 +8792,8 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param, } +#ifdef CONFIG_DRIVER_NL80211_QCA + static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode) { switch (hw_mode) { @@ -8196,12 +8805,34 @@ static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode) return QCA_ACS_MODE_IEEE80211A; case HOSTAPD_MODE_IEEE80211AD: return QCA_ACS_MODE_IEEE80211AD; + case HOSTAPD_MODE_IEEE80211ANY: + return QCA_ACS_MODE_IEEE80211ANY; default: return -1; } } +static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list) +{ + int i, len, ret; + u32 *freqs; + + if (!freq_list) + return 0; + len = int_array_len(freq_list); + freqs = os_malloc(sizeof(u32) * len); + if (!freqs) + return -1; + for (i = 0; i < len; i++) + freqs[i] = freq_list[i]; + ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST, + sizeof(u32) * len, freqs); + os_free(freqs); + return ret; +} + + static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params) { struct i802_bss *bss = priv; @@ -8224,12 +8855,25 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params) (params->ht_enabled && nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) || (params->ht40_enabled && - nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED))) { + nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) || + (params->vht_enabled && + nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) || + nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, + params->ch_width) || + (params->ch_list_len && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len, + params->ch_list)) || + add_acs_freq_list(msg, params->freq_list)) { nlmsg_free(msg); return -ENOBUFS; } nla_nest_end(msg, data); + wpa_printf(MSG_DEBUG, + "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d CH_LIST_LEN: %u", + params->hw_mode, params->ht_enabled, params->ht40_enabled, + params->vht_enabled, params->ch_width, params->ch_list_len); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, @@ -8240,6 +8884,366 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params) } +static int nl80211_set_band(void *priv, enum set_band band) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *data; + int ret; + enum qca_set_band qca_band; + + if (!drv->setband_vendor_cmd_avail) + return -1; + + switch (band) { + case WPA_SETBAND_AUTO: + qca_band = QCA_SETBAND_AUTO; + break; + case WPA_SETBAND_5G: + qca_band = QCA_SETBAND_5G; + break; + case WPA_SETBAND_2G: + qca_band = QCA_SETBAND_2G; + break; + default: + return -1; + } + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SETBAND) || + !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE, qca_band)) { + nlmsg_free(msg); + return -ENOBUFS; + } + nla_nest_end(msg, data); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Driver setband function failed: %s", + strerror(errno)); + } + return ret; +} + + +struct nl80211_pcl { + unsigned int num; + unsigned int *freq_list; +}; + +static int preferred_freq_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nl80211_pcl *param = arg; + struct nlattr *nl_vend, *attr; + enum qca_iface_type iface_type; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + unsigned int num, max_num; + u32 *freqs; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + if (!nl_vend) + return NL_SKIP; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE]; + if (!attr) { + wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found"); + param->num = 0; + return NL_SKIP; + } + + iface_type = (enum qca_iface_type) nla_get_u32(attr); + wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d", + iface_type); + + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]; + if (!attr) { + wpa_printf(MSG_ERROR, + "nl80211: preferred_freq_list couldn't be found"); + param->num = 0; + return NL_SKIP; + } + + /* + * param->num has the maximum number of entries for which there + * is room in the freq_list provided by the caller. + */ + freqs = nla_data(attr); + max_num = nla_len(attr) / sizeof(u32); + if (max_num > param->num) + max_num = param->num; + for (num = 0; num < max_num; num++) + param->freq_list[num] = freqs[num]; + param->num = num; + + return NL_SKIP; +} + + +static int nl80211_get_pref_freq_list(void *priv, + enum wpa_driver_if_type if_type, + unsigned int *num, + unsigned int *freq_list) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + unsigned int i; + struct nlattr *params; + struct nl80211_pcl param; + enum qca_iface_type iface_type; + + if (!drv->get_pref_freq_list) + return -1; + + switch (if_type) { + case WPA_IF_STATION: + iface_type = QCA_IFACE_TYPE_STA; + break; + case WPA_IF_AP_BSS: + iface_type = QCA_IFACE_TYPE_AP; + break; + case WPA_IF_P2P_GO: + iface_type = QCA_IFACE_TYPE_P2P_GO; + break; + case WPA_IF_P2P_CLIENT: + iface_type = QCA_IFACE_TYPE_P2P_CLIENT; + break; + case WPA_IF_IBSS: + iface_type = QCA_IFACE_TYPE_IBSS; + break; + case WPA_IF_TDLS: + iface_type = QCA_IFACE_TYPE_TDLS; + break; + default: + return -1; + } + + param.num = *num; + param.freq_list = freq_list; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, + iface_type)) { + wpa_printf(MSG_ERROR, + "%s: err in adding vendor_cmd and vendor_data", + __func__); + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, params); + + os_memset(freq_list, 0, *num * sizeof(freq_list[0])); + ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m); + if (ret) { + wpa_printf(MSG_ERROR, + "%s: err in send_and_recv_msgs", __func__); + return ret; + } + + *num = param.num; + + for (i = 0; i < *num; i++) { + wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d", + i, freq_list[i]); + } + + return 0; +} + + +static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + struct nlattr *params; + + if (!drv->set_prob_oper_freq) + return -1; + + wpa_printf(MSG_DEBUG, + "nl80211: Set P2P probable operating freq %u for ifindex %d", + freq, bss->ifindex); + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE, + QCA_IFACE_TYPE_P2P_CLIENT) || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ, + freq)) { + wpa_printf(MSG_ERROR, + "%s: err in adding vendor_cmd and vendor_data", + __func__); + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, params); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs", + __func__); + return ret; + } + nlmsg_free(msg); + return 0; +} + +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + +static int nl80211_write_to_file(const char *name, unsigned int val) +{ + int fd, len; + char tmp[128]; + + fd = open(name, O_RDWR); + if (fd < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to open %s: %s", + name, strerror(errno)); + return fd; + } + + len = os_snprintf(tmp, sizeof(tmp), "%u\n", val); + len = write(fd, tmp, len); + if (len < 0) + wpa_printf(MSG_ERROR, "nl80211: Failed to write to %s: %s", + name, strerror(errno)); + close(fd); + + return 0; +} + + +static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags) +{ + struct i802_bss *bss = priv; + char path[128]; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x", + filter_flags); + + /* Configure filtering of unicast frame encrypted using GTK */ + ret = os_snprintf(path, sizeof(path), + "/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast", + bss->ifname); + if (os_snprintf_error(sizeof(path), ret)) + return -1; + + ret = nl80211_write_to_file(path, + !!(filter_flags & + WPA_DATA_FRAME_FILTER_FLAG_GTK)); + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to set IPv4 unicast in multicast filter"); + return ret; + } + + os_snprintf(path, sizeof(path), + "/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast", + bss->ifname); + ret = nl80211_write_to_file(path, + !!(filter_flags & + WPA_DATA_FRAME_FILTER_FLAG_GTK)); + + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to set IPv6 unicast in multicast filter"); + return ret; + } + + /* Configure filtering of unicast frame encrypted using GTK */ + os_snprintf(path, sizeof(path), + "/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp", + bss->ifname); + ret = nl80211_write_to_file(path, + !!(filter_flags & + WPA_DATA_FRAME_FILTER_FLAG_ARP)); + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Failed set gratuitous ARP filter"); + return ret; + } + + /* Configure filtering of IPv6 NA frames */ + os_snprintf(path, sizeof(path), + "/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na", + bss->ifname); + ret = nl80211_write_to_file(path, + !!(filter_flags & + WPA_DATA_FRAME_FILTER_FLAG_NA)); + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to set unsolicited NA filter"); + return ret; + } + + return 0; +} + + +static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type, + const u8 **ext_capa, const u8 **ext_capa_mask, + unsigned int *ext_capa_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + enum nl80211_iftype nlmode; + unsigned int i; + + if (!ext_capa || !ext_capa_mask || !ext_capa_len) + return -1; + + nlmode = wpa_driver_nl80211_if_type(type); + + /* By default, use the per-radio values */ + *ext_capa = drv->extended_capa; + *ext_capa_mask = drv->extended_capa_mask; + *ext_capa_len = drv->extended_capa_len; + + /* Replace the default value if a per-interface type value exists */ + for (i = 0; i < drv->num_iface_ext_capa; i++) { + if (nlmode == drv->iface_ext_capa[i].iftype) { + *ext_capa = drv->iface_ext_capa[i].ext_capa; + *ext_capa_mask = drv->iface_ext_capa[i].ext_capa_mask; + *ext_capa_len = drv->iface_ext_capa[i].ext_capa_len; + break; + } + } + + return 0; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -8250,6 +9254,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .sched_scan = wpa_driver_nl80211_sched_scan, .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan, .get_scan_results2 = wpa_driver_nl80211_get_scan_results, + .abort_scan = wpa_driver_nl80211_abort_scan, .deauthenticate = driver_nl80211_deauthenticate, .authenticate = driver_nl80211_authenticate, .associate = wpa_driver_nl80211_associate, @@ -8299,7 +9304,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .signal_monitor = nl80211_signal_monitor, .signal_poll = nl80211_signal_poll, .send_frame = nl80211_send_frame, - .shared_freq = wpa_driver_nl80211_shared_freq, .set_param = nl80211_set_param, .get_radio_name = nl80211_get_radio_name, .add_pmkid = nl80211_add_pmkid, @@ -8327,12 +9331,16 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie, #endif /* ANDROID_P2P */ #ifdef ANDROID +#ifndef ANDROID_LIB_STUB .driver_cmd = wpa_driver_nl80211_driver_cmd, +#endif /* !ANDROID_LIB_STUB */ #endif /* ANDROID */ .vendor_cmd = nl80211_vendor_cmd, .set_qos_map = nl80211_set_qos_map, .set_wowlan = nl80211_set_wowlan, +#ifdef CONFIG_DRIVER_NL80211_QCA .roaming = nl80211_roaming, +#endif /* CONFIG_DRIVER_NL80211_QCA */ .set_mac_addr = nl80211_set_mac_addr, #ifdef CONFIG_MESH .init_mesh = wpa_driver_nl80211_init_mesh, @@ -8345,5 +9353,13 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .br_set_net_param = wpa_driver_br_set_net_param, .add_tx_ts = nl80211_add_ts, .del_tx_ts = nl80211_del_ts, + .get_ifindex = nl80211_get_ifindex, +#ifdef CONFIG_DRIVER_NL80211_QCA .do_acs = wpa_driver_do_acs, + .set_band = nl80211_set_band, + .get_pref_freq_list = nl80211_get_pref_freq_list, + .set_prob_oper_freq = nl80211_set_prob_oper_freq, +#endif /* CONFIG_DRIVER_NL80211_QCA */ + .configure_data_frame_filters = nl80211_configure_data_frame_filters, + .get_ext_capab = nl80211_get_ext_capab, };