X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fdrivers%2Fdriver_nl80211.c;h=d3e4cfb00f57ed6708eb66cc0113952f351dabef;hb=d02e5498310875b1e96921c7c1f53168f4ffee2c;hp=b13d18c25d25eb96f48f3b98c98aad1b10f32d5b;hpb=b658547dd58492d2e618650f9ae0b85466bb3e9e;p=mech_eap.git diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index b13d18c..d3e4cfb 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -176,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); @@ -439,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); } @@ -757,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; @@ -780,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); @@ -798,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); @@ -825,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); } @@ -882,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; @@ -902,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); @@ -952,6 +974,10 @@ 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) && @@ -1046,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 */ @@ -1061,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) { @@ -1085,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); @@ -1132,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]; @@ -1148,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); } @@ -1519,11 +1543,16 @@ static void nl80211_check_global(struct nl80211_global *global) 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); } @@ -1536,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); } @@ -1621,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) @@ -1649,6 +1739,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, 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) { @@ -1668,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; @@ -1916,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)) @@ -1977,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[] = { @@ -1985,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 @@ -2010,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; @@ -2032,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); @@ -2186,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; @@ -2219,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) { @@ -2226,25 +2354,32 @@ 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, @@ -2279,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); @@ -2325,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); @@ -2372,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); } @@ -3093,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; @@ -3119,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; @@ -3145,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; @@ -3175,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)) { @@ -3189,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 && @@ -3209,7 +3358,8 @@ 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); } @@ -3445,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", @@ -3512,6 +3664,12 @@ static int wpa_driver_nl80211_set_ap(void *priv, } #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)", @@ -3670,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; } @@ -3679,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; @@ -3722,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", @@ -3760,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) { @@ -3770,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; } @@ -3785,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) { @@ -3815,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)) @@ -3937,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) @@ -3946,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: @@ -4045,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 && @@ -4128,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) && @@ -4248,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)", @@ -4477,8 +4701,9 @@ retry: goto fail; } - if (nl80211_ht_vht_overrides(msg, params) < 0) - return -1; + ret = nl80211_ht_vht_overrides(msg, params); + if (ret < 0) + goto fail; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -4658,9 +4883,10 @@ 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; } @@ -4671,6 +4897,22 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, if (params->p2p) wpa_printf(MSG_DEBUG, " * P2P group"); + if (params->pbss) { + wpa_printf(MSG_DEBUG, " * PBSS"); + if (nla_put_flag(msg, NL80211_ATTR_PBSS)) + return -1; + } + + 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; + } + return 0; } @@ -4823,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) { @@ -4886,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; @@ -5242,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), @@ -5267,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]); @@ -5439,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); } @@ -5466,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); } @@ -5481,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; @@ -5493,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; @@ -5508,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; } @@ -5518,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; } @@ -5552,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; @@ -5622,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); } @@ -5649,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; @@ -5711,7 +5996,15 @@ static void *i802_init(struct hostapd_data *hapd, 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)); + 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 { master_ifname[0] = '\0'; } @@ -5722,14 +6015,14 @@ static void *i802_init(struct hostapd_data *hapd, 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], @@ -5741,7 +6034,7 @@ static void *i802_init(struct hostapd_data *hapd, 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) { @@ -5886,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; @@ -5970,7 +6264,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, os_memcpy(if_addr, new_addr, ETH_ALEN); } - 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) @@ -6026,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; } @@ -6046,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) @@ -6125,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; @@ -6145,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; @@ -6162,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: @@ -6204,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; } @@ -6238,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) { @@ -6460,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); } @@ -6535,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) @@ -6551,15 +6895,15 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, { 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")) { @@ -6597,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; @@ -6605,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; @@ -6960,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"); } @@ -7280,11 +7625,13 @@ 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, - unsigned int freq) + 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, - freq, 0, 0, 0); + freq, 0, 0, 0, csa_offs, + csa_offs_len); } @@ -7359,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; @@ -7514,7 +7861,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "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_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, @@ -7533,7 +7883,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) 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_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; @@ -7576,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, @@ -7592,20 +7947,56 @@ 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; + } - 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))) + /* 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.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, @@ -7629,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); @@ -7921,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; @@ -8031,8 +8427,7 @@ static int nl80211_join_mesh(struct i802_bss *bss, 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; @@ -8056,7 +8451,7 @@ static int nl80211_join_mesh(struct i802_bss *bss, goto fail; } ret = 0; - bss->freq = params->freq.freq; + drv->assoc_freq = bss->freq = params->freq.freq; wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully"); fail: @@ -8725,6 +9120,130 @@ static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq) #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", @@ -8735,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, @@ -8833,10 +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, };