From 5abc7823bd01f69b8afbe1fd19f65fff86137c44 Mon Sep 17 00:00:00 2001 From: Venkateswara Naralasetty Date: Wed, 5 Dec 2018 11:23:53 +0100 Subject: [PATCH] wpa_supplicant: Add Multi-AP backhaul STA support Advertise vendor specific Multi-AP IE in (Re)Association Request frames and process Multi-AP IE from (Re)Association Response frames if the user enables Multi-AP fuctionality. If the (Re)Association Response frame does not contain the Multi-AP IE, disassociate. This adds a new configuration parameter 'multi_ap_backhaul_sta' to enable/disable Multi-AP functionality. Enable 4-address mode after association (if the Association Response frame contains the Multi-AP IE). Also enable the bridge in that case. This is necessary because wpa_supplicant only enables the bridge in wpa_drv_if_add(), which only gets called when an interface is added through the control interface, not when it is configured from the command line. Signed-off-by: Venkateswara Naralasetty Signed-off-by: Jouni Malinen Signed-off-by: Arnout Vandecappelle (Essensium/Mind) --- src/drivers/driver.h | 9 ++++++ src/drivers/driver_nl80211.c | 44 ++++++++++++++++++++++++++ wpa_supplicant/config.c | 1 + wpa_supplicant/config_ssid.h | 7 +++++ wpa_supplicant/driver_i.h | 8 +++++ wpa_supplicant/events.c | 50 ++++++++++++++++++++++++++++++ wpa_supplicant/sme.c | 16 ++++++++++ wpa_supplicant/wpa_supplicant.c | 18 +++++++++++ wpa_supplicant/wpa_supplicant.conf | 7 +++++ wpa_supplicant/wpa_supplicant_i.h | 1 + 10 files changed, 161 insertions(+) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 4a011f6ba..6b8334711 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -4120,6 +4120,15 @@ struct wpa_driver_ops { */ int (*send_external_auth_status)(void *priv, struct external_auth *params); + + /** + * set_4addr_mode - Set 4-address mode + * @priv: Private driver interface data + * @bridge_ifname: Bridge interface name + * @val: 0 - disable 4addr mode, 1 - enable 4addr mode + * Returns: 0 on success, < 0 on failure + */ + int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val); }; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index c9dd86211..929b852f6 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -10738,6 +10738,49 @@ fail: } +static int nl80211_set_4addr_mode(void *priv, const char *bridge_ifname, + int val) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -ENOBUFS; + + wpa_printf(MSG_DEBUG, "nl80211: %s 4addr mode (bridge_ifname: %s)", + val ? "Enable" : "Disable", bridge_ifname); + + msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE); + if (!msg || nla_put_u8(msg, NL80211_ATTR_4ADDR, val)) + goto fail; + + if (bridge_ifname[0] && bss->added_if_into_bridge && !val) { + if (linux_br_del_if(drv->global->ioctl_sock, + bridge_ifname, bss->ifname)) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to remove interface %s from bridge %s", + bss->ifname, bridge_ifname); + return -1; + } + bss->added_if_into_bridge = 0; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (!ret) { + if (bridge_ifname[0] && val && + i802_check_bridge(drv, bss, bridge_ifname, bss->ifname) < 0) + return -1; + return 0; + } + +fail: + nlmsg_free(msg); + wpa_printf(MSG_ERROR, "nl80211: Failed to enable/disable 4addr"); + + return ret; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -10867,4 +10910,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .get_ext_capab = nl80211_get_ext_capab, .update_connect_params = nl80211_update_connection_params, .send_external_auth_status = nl80211_send_external_auth_status, + .set_4addr_mode = nl80211_set_4addr_mode, }; diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 78da98de3..0f71e9fb6 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -2384,6 +2384,7 @@ static const struct parse_data ssid_fields[] = { #endif /* CONFIG_DPP */ { INT_RANGE(owe_group, 0, 65535) }, { INT_RANGE(owe_only, 0, 1) }, + { INT_RANGE(multi_ap_backhaul_sta, 0, 1) }, }; #undef OFFSET diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 6427fbb01..5205b0746 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -948,6 +948,13 @@ struct wpa_ssid { * the selection attempts for OWE BSS exceed the configured threshold. */ int owe_transition_bss_select_count; + + /** + * multi_ap_backhaul_sta - Multi-AP backhaul STA + * 0 = normal (non-Multi-AP) station + * 1 = Multi-AP backhaul station + */ + int multi_ap_backhaul_sta; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 5581bb064..86c891aa4 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -1054,4 +1054,12 @@ wpa_drv_send_external_auth_status(struct wpa_supplicant *wpa_s, params); } +static inline int wpa_drv_set_4addr_mode(struct wpa_supplicant *wpa_s, int val) +{ + if (!wpa_s->driver->set_4addr_mode) + return -1; + return wpa_s->driver->set_4addr_mode(wpa_s->drv_priv, + wpa_s->bridge_ifname, val); +} + #endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 8eac4be0f..7f2302900 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -324,6 +324,9 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) os_memset(wpa_s->last_tk, 0, sizeof(wpa_s->last_tk)); #endif /* CONFIG_TESTING_OPTIONS */ wpa_s->ieee80211ac = 0; + + if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0) + wpa_s->enabled_4addr_mode = 0; } @@ -2267,6 +2270,50 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, #endif /* CONFIG_INTERWORKING */ +static void multi_ap_process_assoc_resp(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems elems; + const u8 *map_sub_elem, *pos; + size_t len; + + if (!wpa_s->current_ssid || + !wpa_s->current_ssid->multi_ap_backhaul_sta || + !ies || + ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) + return; + + if (!elems.multi_ap || elems.multi_ap_len < 7) { + wpa_printf(MSG_INFO, "AP doesn't support Multi-AP protocol"); + goto fail; + } + + pos = elems.multi_ap + 4; + len = elems.multi_ap_len - 4; + + map_sub_elem = get_ie(pos, len, MULTI_AP_SUB_ELEM_TYPE); + if (!map_sub_elem || map_sub_elem[1] < 1) { + wpa_printf(MSG_INFO, "invalid Multi-AP sub elem type"); + goto fail; + } + + if (!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS)) { + wpa_printf(MSG_INFO, "AP doesn't support backhaul BSS"); + goto fail; + } + + if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) { + wpa_printf(MSG_ERROR, "Failed to set 4addr mode"); + goto fail; + } + wpa_s->enabled_4addr_mode = 1; + return; + +fail: + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); +} + + #ifdef CONFIG_FST static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s, const u8 *ie, size_t ie_len) @@ -2343,6 +2390,9 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, get_ie(data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP)) wpa_s->ieee80211ac = 1; + + multi_ap_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); } if (data->assoc_info.beacon_ies) wpa_hexdump(MSG_DEBUG, "beacon_ies", diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 779c79b43..b61e0c33c 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -1554,6 +1554,22 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, } #endif /* CONFIG_OWE */ + if (wpa_s->current_ssid && wpa_s->current_ssid->multi_ap_backhaul_sta) { + size_t multi_ap_ie_len; + + multi_ap_ie_len = add_multi_ap_ie( + wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len, + MULTI_AP_BACKHAUL_STA); + if (multi_ap_ie_len == 0) { + wpa_printf(MSG_ERROR, + "Multi-AP: Failed to build Multi-AP IE"); + return; + } + wpa_s->sme.assoc_req_ie_len += multi_ap_ie_len; + } + params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index bd66d9b59..1ade6253f 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -2816,6 +2816,21 @@ static u8 * wpas_populate_assoc_ies( } #endif /* CONFIG_IEEE80211R */ + if (ssid->multi_ap_backhaul_sta) { + size_t multi_ap_ie_len; + + multi_ap_ie_len = add_multi_ap_ie(wpa_ie + wpa_ie_len, + max_wpa_ie_len - wpa_ie_len, + MULTI_AP_BACKHAUL_STA); + if (multi_ap_ie_len == 0) { + wpa_printf(MSG_ERROR, + "Multi-AP: Failed to build Multi-AP IE"); + os_free(wpa_ie); + return NULL; + } + wpa_ie_len += multi_ap_ie_len; + } + params->wpa_ie = wpa_ie; params->wpa_ie_len = wpa_ie_len; params->auth_alg = algs; @@ -3294,6 +3309,9 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, zero_addr = 1; } + if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0) + wpa_s->enabled_4addr_mode = 0; + #ifdef CONFIG_TDLS wpa_tdls_teardown_peers(wpa_s->wpa); #endif /* CONFIG_TDLS */ diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index b93990004..ffe5d2e0c 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -1403,6 +1403,13 @@ fast_reauth=1 # 2: MCS 0-9 # 3: not supported +# multi_ap_backhaul_sta: Multi-AP backhaul STA functionality +# 0 = normal STA (default) +# 1 = backhaul STA +# A backhaul STA sends the Multi-AP IE, fails to associate if the AP does not +# support Multi-AP, and sets 4-address mode if it does. Thus, the netdev can be +# added to a bridge to allow forwarding frames over this backhaul link. + ##### Fast Session Transfer (FST) support ##################################### # # The options in this section are only available when the build configuration diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 8b749f44e..b3a7db674 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1234,6 +1234,7 @@ struct wpa_supplicant { unsigned int disable_fils:1; #endif /* CONFIG_FILS */ unsigned int ieee80211ac:1; + unsigned int enabled_4addr_mode:1; };