X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fdrivers%2Fdriver_nl80211.c;h=d3e4cfb00f57ed6708eb66cc0113952f351dabef;hb=d02e5498310875b1e96921c7c1f53168f4ffee2c;hp=e83a3dfbd45a3eea86b9a2bb92ea30fc654b3c94;hpb=e8dc205f1710522ebc9a416140c32e8e15afd069;p=mech_eap.git diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index e83a3df..d3e4cfb 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -181,9 +181,14 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, 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); @@ -440,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); } @@ -758,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; @@ -781,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); @@ -799,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); @@ -826,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); } @@ -883,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; @@ -903,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); @@ -953,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) && @@ -1047,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 */ @@ -1062,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) { @@ -1086,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); @@ -1133,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]; @@ -1149,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); } @@ -1520,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); } @@ -1541,7 +1569,12 @@ static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) if (is_p2p_net_interface(drv->nlmode)) nl80211_disable_11b_rates(drv, drv->ifindex, 1); - /* rtnetlink ifup handler will report interface as enabled */ + /* + * 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); } @@ -1626,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) @@ -1654,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) { @@ -1673,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; @@ -1921,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)) @@ -1982,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[] = { @@ -1990,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 @@ -2015,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; @@ -2037,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); @@ -2191,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; @@ -2224,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) { @@ -2241,20 +2364,22 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, } 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, @@ -2289,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); @@ -2335,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); @@ -2382,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); } @@ -3462,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", @@ -3529,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)", @@ -3687,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; } @@ -3696,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; @@ -3739,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", @@ -3777,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) { @@ -3787,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; } @@ -3802,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) { @@ -3832,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)) @@ -3954,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) @@ -3963,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: @@ -4062,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 && @@ -4145,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) && @@ -4494,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; @@ -4675,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; } @@ -4688,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; } @@ -4840,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) { @@ -4903,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; @@ -5259,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), @@ -5284,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]); @@ -5498,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; @@ -5510,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; @@ -5525,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; } @@ -5535,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; } @@ -5569,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; @@ -5639,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); } @@ -5666,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; @@ -5728,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'; } @@ -5739,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], @@ -5758,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) { @@ -5903,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; @@ -5987,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) @@ -6043,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; } @@ -6063,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) @@ -6182,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: @@ -6236,17 +6529,16 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, } -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; } @@ -6258,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) { @@ -6559,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) @@ -6581,9 +6901,9 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, 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")) { @@ -6621,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; @@ -6629,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; @@ -7385,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; @@ -7540,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, @@ -7559,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; @@ -7987,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; @@ -8097,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; @@ -8122,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: @@ -8791,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", @@ -8801,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, @@ -8899,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, };