hostapd: add Multi-AP patches and config options

Cherry-pick Multi-AP commits from uptream:
 9c06f0f6a hostapd: Add Multi-AP protocol support
 5abc7823b wpa_supplicant: Add Multi-AP backhaul STA support
 a1debd338 tests: Refactor test_multi_ap
 bfcdac1c8 Multi-AP: Don't reject backhaul STA on fronthaul BSS
 cb3c156e7 tests: Update multi_ap_fronthaul_on_ap to match implementation
 56a2d788f WPS: Add multi_ap_subelem to wps_build_wfa_ext()
 83ebf5586 wpa_supplicant: Support Multi-AP backhaul STA onboarding with WPS
 66819b07b hostapd: Support Multi-AP backhaul STA onboarding with WPS
 8682f384c hostapd: Add README-MULTI-AP
 b1daf498a tests: Multi-AP WPS provisioning

Add support for Multi-AP to the UCI configuration. Every wifi-iface gets
an option 'multi_ap'. For APs, its value can be 0 (multi-AP support
disabled), 1 (backhaul AP), 2 (fronthaul AP), or 3 (fronthaul + backhaul
AP). For STAs, it can be 0 (not a backhaul STA) or 1 (backhaul STA, can
only associate with backhaul AP).

Also add new optional parameter to wps_start ubus call of
wpa_supplicant to indicate that a Multi-AP backhaul link is required.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
v19.07.3_mercusys_ac12_duma
Arnout Vandecappelle (Essensium/Mind) 5 years ago committed by Daniel Golle
parent 8554982e1f
commit 2e0f41e73a

@ -59,7 +59,7 @@
static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
bool set_bw, bool is_ht40)
{
@@ -8943,6 +8995,7 @@ static void rt2800_init_rfcsr_6352(struc
@@ -8956,6 +9008,7 @@ static void rt2800_init_rfcsr_6352(struc
rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);

@ -161,7 +161,7 @@
static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
bool set_bw, bool is_ht40)
{
@@ -8995,6 +9149,7 @@ static void rt2800_init_rfcsr_6352(struc
@@ -9008,6 +9162,7 @@ static void rt2800_init_rfcsr_6352(struc
rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);

@ -72,7 +72,7 @@
static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
bool set_bw, bool is_ht40)
{
@@ -9151,6 +9216,7 @@ static void rt2800_init_rfcsr_6352(struc
@@ -9164,6 +9229,7 @@ static void rt2800_init_rfcsr_6352(struc
rt2800_r_calibration(rt2x00dev);
rt2800_rf_self_txdc_cal(rt2x00dev);

@ -387,7 +387,7 @@
static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
bool set_bw, bool is_ht40)
{
@@ -9219,6 +9599,7 @@ static void rt2800_init_rfcsr_6352(struc
@@ -9232,6 +9612,7 @@ static void rt2800_init_rfcsr_6352(struc
rt2800_rxdcoc_calibration(rt2x00dev);
rt2800_bw_filter_calibration(rt2x00dev, true);
rt2800_bw_filter_calibration(rt2x00dev, false);

@ -958,7 +958,7 @@
static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
bool set_bw, bool is_ht40)
{
@@ -9599,6 +10550,7 @@ static void rt2800_init_rfcsr_6352(struc
@@ -9612,6 +10563,7 @@ static void rt2800_init_rfcsr_6352(struc
rt2800_rxdcoc_calibration(rt2x00dev);
rt2800_bw_filter_calibration(rt2x00dev, true);
rt2800_bw_filter_calibration(rt2x00dev, false);

@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=hostapd
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_SOURCE_URL:=http://w1.fi/hostap.git
PKG_SOURCE_PROTO:=git

@ -212,9 +212,12 @@ hostapd_common_add_bss_config() {
config_add_string wpa_psk_file
config_add_int multi_ap
config_add_boolean wps_pushbutton wps_label ext_registrar wps_pbc_in_m1
config_add_int wps_ap_setup_locked wps_independent
config_add_string wps_device_type wps_device_name wps_manufacturer wps_pin
config_add_string multi_ap_backhaul_ssid multi_ap_backhaul_key
config_add_boolean ieee80211v wnm_sleep_mode bss_transition
config_add_int time_advertisement
@ -261,7 +264,8 @@ hostapd_set_bss_options() {
macfilter ssid utf8_ssid wmm uapsd hidden short_preamble rsn_preauth \
iapp_interface eapol_version dynamic_vlan ieee80211w nasid \
acct_server acct_secret acct_port acct_interval \
bss_load_update_period chan_util_avg_period sae_require_mfp
bss_load_update_period chan_util_avg_period sae_require_mfp \
multi_ap multi_ap_backhaul_ssid multi_ap_backhaul_key
set_default isolate 0
set_default maxassoc 0
@ -278,7 +282,8 @@ hostapd_set_bss_options() {
set_default bss_load_update_period 60
set_default chan_util_avg_period 600
set_default utf8_ssid 1
set_default multi_ap 0
append bss_conf "ctrl_interface=/var/run/hostapd"
if [ "$isolate" -gt 0 ]; then
append bss_conf "ap_isolate=$isolate" "$N"
@ -298,6 +303,7 @@ hostapd_set_bss_options() {
append bss_conf "ignore_broadcast_ssid=$hidden" "$N"
append bss_conf "uapsd_advertisement_enabled=$uapsd" "$N"
append bss_conf "utf8_ssid=$utf8_ssid" "$N"
append bss_conf "multi_ap=$multi_ap" "$N"
[ "$tdls_prohibit" -gt 0 ] && append bss_conf "tdls_prohibit=$tdls_prohibit" "$N"
@ -420,6 +426,9 @@ hostapd_set_bss_options() {
[ "$wps_pushbutton" -gt 0 ] && append config_methods push_button
[ "$wps_label" -gt 0 ] && append config_methods label
# WPS not possible on Multi-AP backhaul-only SSID
[ "$multi_ap" = 1 ] && wps_possible=
[ -n "$wps_possible" -a -n "$config_methods" ] && {
set_default ext_registrar 0
set_default wps_device_type "6-0050F204-1"
@ -442,6 +451,19 @@ hostapd_set_bss_options() {
append bss_conf "wps_independent=$wps_independent" "$N"
[ -n "$wps_ap_setup_locked" ] && append bss_conf "ap_setup_locked=$wps_ap_setup_locked" "$N"
[ "$wps_pbc_in_m1" -gt 0 ] && append bss_conf "pbc_in_m1=$wps_pbc_in_m1" "$N"
[ "$multi_ap" -gt 0 ] && [ -n "$multi_ap_backhaul_ssid" ] && {
append bss_conf "multi_ap_backhaul_ssid=\"$multi_ap_backhaul_ssid\"" "$N"
if [ -z "$multi_ap_backhaul_key" ]; then
:
elif [ ${#multi_ap_backhaul_key} -lt 8 ]; then
wireless_setup_vif_failed INVALID_WPA_PSK
return 1
elif [ ${#multi_ap_backhaul_key} -eq 64 ]; then
append bss_conf "multi_ap_backhaul_wpa_psk=$multi_ap_backhaul_key" "$N"
else
append bss_conf "multi_ap_backhaul_wpa_passphrase=$multi_ap_backhaul_key" "$N"
fi
}
}
append bss_conf "ssid=$ssid" "$N"
@ -640,7 +662,7 @@ wpa_supplicant_prepare_interface() {
_wpa_supplicant_common "$1"
json_get_vars mode wds
json_get_vars mode wds multi_ap
[ -n "$network_bridge" ] && {
fail=
@ -649,7 +671,7 @@ wpa_supplicant_prepare_interface() {
fail=1
;;
sta)
[ "$wds" = 1 ] || fail=1
[ "$wds" = 1 -o "$multi_ap" = 1 ] || fail=1
;;
esac
@ -675,6 +697,12 @@ wpa_supplicant_prepare_interface() {
country_str="country=$country"
}
multiap_flag_file="${_config}.is_multiap"
if [ "$multi_ap" = "1" ]; then
touch "$multiap_flag_file"
else
[ -e "$multiap_flag_file" ] && rm "$multiap_flag_file"
fi
wpa_supplicant_teardown_interface "$ifname"
cat > "$_config" <<EOF
$ap_scan
@ -716,9 +744,11 @@ wpa_supplicant_add_network() {
json_get_vars \
ssid bssid key \
basic_rate mcast_rate \
ieee80211w ieee80211r
ieee80211w ieee80211r \
multi_ap
set_default ieee80211r 0
set_default multi_ap 0
local key_mgmt='NONE'
local enc_str=
@ -752,6 +782,8 @@ wpa_supplicant_add_network() {
[ "$_w_mode" = "adhoc" -o "$_w_mode" = "mesh" ] && append network_data "$_w_modestr" "$N$T"
[ "$multi_ap" = 1 -a "$_w_mode" = "sta" ] && append network_data "multi_ap_backhaul_sta=1" "$N$T"
case "$auth_type" in
none) ;;
owe)

@ -48,7 +48,13 @@ if [ "$ACTION" = "pressed" -a "$BUTTON" = "wps" ]; then
wps_done=0
ubusobjs="$( ubus -S list wpa_supplicant.* )"
for ubusobj in $ubusobjs; do
ubus -S call $ubusobj wps_start && wps_done=1
ifname="$(echo $ubusobj | cut -d'.' -f2 )"
multi_ap=""
if [ -e "/var/run/wpa_supplicant-${ifname}.conf.is_multiap" ]; then
ubus -S call $ubusobj wps_start '{ "multi_ap": true }' && wps_done=1
else
ubus -S call $ubusobj wps_start && wps_done=1
fi
done
[ $wps_done = 0 ] || wps_catch_credentials &
fi

@ -0,0 +1,306 @@
From 9c06f0f6aed26c1628acaa74df0232dd7b345e9a Mon Sep 17 00:00:00 2001
From: Venkateswara Naralasetty <vnaralas@codeaurora.org>
Date: Wed, 5 Dec 2018 11:23:51 +0100
Subject: [PATCH] hostapd: Add Multi-AP protocol support
The purpose of Multi-AP specification is to enable inter-operability
across Wi-Fi access points (APs) from different vendors.
This patch introduces one new configuration parameter 'multi_ap' to
enable Multi-AP functionality and to configure the BSS as a backhaul
and/or fronthaul BSS.
Advertise vendor specific Multi-AP capabilities in (Re)Association
Response frame, if Multi-AP functionality is enabled through the
configuration parameter.
A backhaul AP must support receiving both 3addr and 4addr frames from a
backhaul STA, so create a VLAN for it just like is done for WDS, i.e.,
by calling hostapd_set_wds_sta(). Since Multi-AP requires WPA2 (never
WEP), we can safely call hostapd_set_wds_encryption() as well and we can
reuse the entire WDS condition.
To parse the Multi-AP Extension subelement, we use get_ie(): even though
that function is meant for parsing IEs, it works for subelements.
Signed-off-by: Venkateswara Naralasetty <vnaralas@codeaurora.org>
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
---
hostapd/config_file.c | 10 +++++
hostapd/hostapd.conf | 7 ++++
src/ap/ap_config.h | 4 ++
src/ap/ieee802_11.c | 77 +++++++++++++++++++++++++++++++++-
src/ap/sta_info.c | 2 +-
src/ap/sta_info.h | 1 +
src/common/ieee802_11_common.c | 24 +++++++++++
src/common/ieee802_11_common.h | 4 ++
src/common/ieee802_11_defs.h | 7 ++++
9 files changed, 134 insertions(+), 2 deletions(-)
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -4115,6 +4115,16 @@ static int hostapd_config_fill(struct ho
} else if (os_strcmp(buf, "coloc_intf_reporting") == 0) {
bss->coloc_intf_reporting = atoi(pos);
#endif /* CONFIG_OWE */
+ } else if (os_strcmp(buf, "multi_ap") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 3) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid multi_ap '%s'",
+ line, buf);
+ return -1;
+ }
+
+ bss->multi_ap = val;
} else {
wpa_printf(MSG_ERROR,
"Line %d: unknown configuration item '%s'",
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -438,6 +438,13 @@ wmm_ac_vo_txop_limit=47
wmm_ac_vo_acm=0
# Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102
+# Enable Multi-AP functionality
+# 0 = disabled (default)
+# 1 = AP support backhaul BSS
+# 2 = AP support fronthaul BSS
+# 3 = AP supports both backhaul BSS and fronthaul BSS
+#multi_ap=0
+
# Static WEP key configuration
#
# The key number to use when transmitting.
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -688,6 +688,10 @@ struct hostapd_bss_config {
#endif /* CONFIG_OWE */
int coloc_intf_reporting;
+
+#define BACKHAUL_BSS 1
+#define FRONTHAUL_BSS 2
+ int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
};
/**
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -62,6 +62,22 @@ prepare_auth_resp_fils(struct hostapd_da
int *is_pub);
#endif /* CONFIG_FILS */
+
+u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 multi_ap_val = 0;
+
+ if (!hapd->conf->multi_ap)
+ return eid;
+ if (hapd->conf->multi_ap & BACKHAUL_BSS)
+ multi_ap_val |= MULTI_AP_BACKHAUL_BSS;
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+ multi_ap_val |= MULTI_AP_FRONTHAUL_BSS;
+
+ return eid + add_multi_ap_ie(eid, 9, multi_ap_val);
+}
+
+
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
@@ -2210,6 +2226,57 @@ static u16 check_wmm(struct hostapd_data
return WLAN_STATUS_SUCCESS;
}
+static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *multi_ap_ie, size_t multi_ap_len)
+{
+ u8 multi_ap_value = 0;
+
+ sta->flags &= ~WLAN_STA_MULTI_AP;
+
+ if (!hapd->conf->multi_ap)
+ return WLAN_STATUS_SUCCESS;
+
+ if (multi_ap_ie) {
+ const u8 *multi_ap_subelem;
+
+ multi_ap_subelem = get_ie(multi_ap_ie + 4,
+ multi_ap_len - 4,
+ MULTI_AP_SUB_ELEM_TYPE);
+ if (multi_ap_subelem && multi_ap_subelem[1] == 1) {
+ multi_ap_value = multi_ap_subelem[2];
+ } else {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP IE has missing or invalid Multi-AP subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+ }
+
+ if (multi_ap_value == MULTI_AP_BACKHAUL_STA)
+ sta->flags |= WLAN_STA_MULTI_AP;
+
+ if ((hapd->conf->multi_ap & BACKHAUL_BSS) &&
+ multi_ap_value == MULTI_AP_BACKHAUL_STA)
+ return WLAN_STATUS_SUCCESS;
+
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS) {
+ if (multi_ap_value == MULTI_AP_BACKHAUL_STA) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Backhaul STA tries to associate with fronthaul-only BSS");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Non-Multi-AP STA tries to associate with backhaul-only BSS");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+}
+
static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee802_11_elems *elems)
@@ -2466,6 +2533,11 @@ static u16 check_assoc_ies(struct hostap
resp = copy_supp_rates(hapd, sta, &elems);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+
+ resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
#ifdef CONFIG_IEEE80211N
resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
if (resp != WLAN_STATUS_SUCCESS)
@@ -2996,6 +3068,9 @@ static u16 send_assoc_resp(struct hostap
}
#endif /* CONFIG_WPS */
+ if (sta && (sta->flags & WLAN_STA_MULTI_AP))
+ p = hostapd_eid_multi_ap(hapd, p);
+
#ifdef CONFIG_P2P
if (sta && sta->p2p_ie && hapd->p2p_group) {
struct wpabuf *p2p_resp_ie;
@@ -4236,7 +4311,7 @@ static void handle_assoc_cb(struct hosta
sta->flags |= WLAN_STA_WDS;
}
- if (sta->flags & WLAN_STA_WDS) {
+ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -166,7 +166,7 @@ void ap_free_sta(struct hostapd_data *ha
/* just in case */
ap_sta_set_authorized(hapd, sta, 0);
- if (sta->flags & WLAN_STA_WDS)
+ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP))
hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
if (sta->ipaddr)
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -36,6 +36,7 @@
#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
#define WLAN_STA_VENDOR_VHT BIT(21)
#define WLAN_STA_PENDING_FILS_ERP BIT(22)
+#define WLAN_STA_MULTI_AP BIT(23)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -126,6 +126,10 @@ static int ieee802_11_parse_vendor_speci
elems->roaming_cons_sel = pos;
elems->roaming_cons_sel_len = elen;
break;
+ case MULTI_AP_OUI_TYPE:
+ elems->multi_ap = pos;
+ elems->multi_ap_len = elen;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
@@ -1519,6 +1523,26 @@ size_t mbo_add_ie(u8 *buf, size_t len, c
}
+size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value)
+{
+ u8 *pos = buf;
+
+ if (len < 9)
+ return 0;
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 7; /* len */
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = MULTI_AP_OUI_TYPE;
+ *pos++ = MULTI_AP_SUB_ELEM_TYPE;
+ *pos++ = 1; /* len */
+ *pos++ = value;
+
+ return pos - buf;
+}
+
+
static const struct country_op_class us_op_class[] = {
{ 1, 115 },
{ 2, 118 },
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -84,6 +84,7 @@ struct ieee802_11_elems {
const u8 *power_capab;
const u8 *roaming_cons_sel;
const u8 *password_id;
+ const u8 *multi_ap;
u8 ssid_len;
u8 supp_rates_len;
@@ -130,6 +131,7 @@ struct ieee802_11_elems {
u8 power_capab_len;
u8 roaming_cons_sel_len;
u8 password_id_len;
+ u8 multi_ap_len;
struct mb_ies_info mb_ies;
};
@@ -189,6 +191,8 @@ const u8 * get_ie_ext(const u8 *ies, siz
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
+size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value);
+
struct country_op_class {
u8 country_op_class;
u8 global_op_class;
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1210,6 +1210,13 @@ struct ieee80211_ampe_ie {
#define MBO_OUI_TYPE 22
#define OWE_IE_VENDOR_TYPE 0x506f9a1c
#define OWE_OUI_TYPE 28
+#define MULTI_AP_OUI_TYPE 0x1B
+
+#define MULTI_AP_SUB_ELEM_TYPE 0x06
+#define MULTI_AP_TEAR_DOWN BIT(4)
+#define MULTI_AP_FRONTHAUL_BSS BIT(5)
+#define MULTI_AP_BACKHAUL_BSS BIT(6)
+#define MULTI_AP_BACKHAUL_STA BIT(7)
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0

@ -0,0 +1,311 @@
From 5abc7823bd01f69b8afbe1fd19f65fff86137c44 Mon Sep 17 00:00:00 2001
From: Venkateswara Naralasetty <vnaralas@codeaurora.org>
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 <vnaralas@codeaurora.org>
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
---
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(+)
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -4100,6 +4100,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);
};
/**
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -10728,6 +10728,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",
@@ -10856,4 +10899,5 @@ const struct wpa_driver_ops wpa_driver_n
.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,
};
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2416,6 +2416,7 @@ static const struct parse_data ssid_fiel
#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
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -950,6 +950,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 */
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -1046,4 +1046,12 @@ wpa_drv_send_external_auth_status(struct
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 */
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -324,6 +324,9 @@ void wpa_supplicant_mark_disassoc(struct
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_r
#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_associnf
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",
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1552,6 +1552,22 @@ void sme_associate(struct wpa_supplicant
}
#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;
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -2893,6 +2893,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;
@@ -3377,6 +3392,9 @@ void wpa_supplicant_deauthenticate(struc
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 */
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1399,6 +1399,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
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1242,6 +1242,7 @@ struct wpa_supplicant {
unsigned int disable_fils:1;
#endif /* CONFIG_FILS */
unsigned int ieee80211ac:1;
+ unsigned int enabled_4addr_mode:1;
};

@ -0,0 +1,100 @@
From 7488e0ade6dffb6df4c1fb6526a9f3ede0eb18ef Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Thu, 20 Dec 2018 12:41:00 +0200
Subject: [PATCH] tests: Multi-AP association
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
tests/hwsim/test_multi_ap.py | 73 ++++++++++++++++++++++++++++++++++++
tests/hwsim/wpasupplicant.py | 3 +-
2 files changed, 75 insertions(+), 1 deletion(-)
create mode 100644 tests/hwsim/test_multi_ap.py
--- /dev/null
+++ b/tests/hwsim/test_multi_ap.py
@@ -0,0 +1,73 @@
+# Test cases for Multi-AP
+# Copyright (c) 2018, The Linux Foundation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hostapd
+
+def test_multi_ap_association(dev, apdev):
+ """Multi-AP association in backhaul BSS"""
+ run_multi_ap_association(dev, apdev, 1)
+ dev[1].connect("multi-ap", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[1].wait_event([ "CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT" ],
+ timeout=5)
+ dev[1].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Connection result not reported")
+ if "CTRL-EVENT-ASSOC-REJECT" not in ev:
+ raise Exception("Association rejection not reported")
+ if "status_code=12" not in ev:
+ raise Exception("Unexpected association status code: " + ev)
+
+def test_multi_ap_association_shared_bss(dev, apdev):
+ """Multi-AP association in backhaul BSS (with fronthaul BSS enabled)"""
+ run_multi_ap_association(dev, apdev, 3)
+ dev[1].connect("multi-ap", psk="12345678", scan_freq="2412")
+
+def run_multi_ap_association(dev, apdev, multi_ap):
+ params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
+ params["multi_ap"] = str(multi_ap)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("multi-ap", psk="12345678", multi_ap_backhaul_sta="1",
+ scan_freq="2412")
+
+def test_multi_ap_disabled_on_ap(dev, apdev):
+ """Multi-AP association attempt when disabled on AP"""
+ params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("multi-ap", psk="12345678", multi_ap_backhaul_sta="1",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event([ "CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-CONNECTED" ],
+ timeout=5)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Connection result not reported")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection result")
+
+def test_multi_ap_fronthaul_on_ap(dev, apdev):
+ """Multi-AP association attempt when only fronthaul BSS on AP"""
+ params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
+ params["multi_ap"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("multi-ap", psk="12345678", multi_ap_backhaul_sta="1",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event([ "CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT" ],
+ timeout=5)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Connection result not reported")
+ if "CTRL-EVENT-ASSOC-REJECT" not in ev:
+ raise Exception("Association rejection not reported")
+ if "status_code=12" not in ev:
+ raise Exception("Unexpected association status code: " + ev)
--- a/tests/hwsim/wpasupplicant.py
+++ b/tests/hwsim/wpasupplicant.py
@@ -1031,7 +1031,8 @@ class WpaSupplicant:
"dpp_csign", "dpp_csign_expiry",
"dpp_netaccesskey", "dpp_netaccesskey_expiry",
"group_mgmt", "owe_group",
- "roaming_consortium_selection" ]
+ "roaming_consortium_selection", "multi_ap_backhaul_sta" ]
+
for field in not_quoted:
if field in kwargs and kwargs[field]:
self.set_network(id, field, kwargs[field])

@ -0,0 +1,72 @@
From 0f5029ff41ef286aa7b3e4a3efd3f1a16be925e8 Mon Sep 17 00:00:00 2001
From: "Arnout Vandecappelle (Essensium/Mind)" <arnout@mind.be>
Date: Wed, 9 Jan 2019 18:41:08 +0100
Subject: [PATCH] tests: refactor test_multi_ap
With just one additional argument, the run_multi_ap_association function
can be used for all tests.
While we're at it, also move it to the top of the file.
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
---
v4: new patch
---
tests/hwsim/test_multi_ap.py | 30 +++++++++++-------------------
1 file changed, 11 insertions(+), 19 deletions(-)
--- a/tests/hwsim/test_multi_ap.py
+++ b/tests/hwsim/test_multi_ap.py
@@ -6,6 +6,15 @@
import hostapd
+def run_multi_ap_association(dev, apdev, multi_ap, wait_connect=True):
+ params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
+ if multi_ap:
+ params["multi_ap"] = str(multi_ap)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("multi-ap", psk="12345678", scan_freq="2412",
+ multi_ap_backhaul_sta="1", wait_connect=wait_connect)
+
def test_multi_ap_association(dev, apdev):
"""Multi-AP association in backhaul BSS"""
run_multi_ap_association(dev, apdev, 1)
@@ -28,21 +37,9 @@ def test_multi_ap_association_shared_bss
run_multi_ap_association(dev, apdev, 3)
dev[1].connect("multi-ap", psk="12345678", scan_freq="2412")
-def run_multi_ap_association(dev, apdev, multi_ap):
- params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
- params["multi_ap"] = str(multi_ap)
- hapd = hostapd.add_ap(apdev[0], params)
-
- dev[0].connect("multi-ap", psk="12345678", multi_ap_backhaul_sta="1",
- scan_freq="2412")
-
def test_multi_ap_disabled_on_ap(dev, apdev):
"""Multi-AP association attempt when disabled on AP"""
- params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
- hapd = hostapd.add_ap(apdev[0], params)
-
- dev[0].connect("multi-ap", psk="12345678", multi_ap_backhaul_sta="1",
- scan_freq="2412", wait_connect=False)
+ run_multi_ap_association(dev, apdev, 0, wait_connect=False)
ev = dev[0].wait_event([ "CTRL-EVENT-DISCONNECTED",
"CTRL-EVENT-CONNECTED" ],
timeout=5)
@@ -54,12 +51,7 @@ def test_multi_ap_disabled_on_ap(dev, ap
def test_multi_ap_fronthaul_on_ap(dev, apdev):
"""Multi-AP association attempt when only fronthaul BSS on AP"""
- params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
- params["multi_ap"] = "2"
- hapd = hostapd.add_ap(apdev[0], params)
-
- dev[0].connect("multi-ap", psk="12345678", multi_ap_backhaul_sta="1",
- scan_freq="2412", wait_connect=False)
+ run_multi_ap_association(dev, apdev, 2, wait_connect=False)
ev = dev[0].wait_event([ "CTRL-EVENT-DISCONNECTED",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-ASSOC-REJECT" ],

@ -0,0 +1,106 @@
From 71b061b8a13791a1ed858d924e401541c8584030 Mon Sep 17 00:00:00 2001
From: "Arnout Vandecappelle (Essensium/Mind)" <arnout@mind.be>
Date: Wed, 9 Jan 2019 19:08:00 +0100
Subject: [PATCH] multi_ap: don't reject backhaul STA on fronhaul BSS
The Multi-AP specification only specifies that information elements have
to be added to the association requests and responses; it doesn't
specify anything about what should be done in case they are missing.
Currently, we reject non-backhaul associations on a backhaul-only BSS,
and non-fronthaul associations on a fronthaul-only BSS.
However, this makes WPS fail when fronthaul and backhaul are separate
SSIDs. Indeed, WPS for the backhaul link is performed on the *fronthaul*
SSID. Thus, the association request used for WPS *will* contain the
Multi-AP IE indicating a backhaul STA. Rejecting that association makes
WPS fail.
Therefore, accept a multi-AP backhaul STA association request on a
fronthaul-only BSS. Still issue a warning about it, but only at level
DEBUG intead of INFO. Also change the condition checking to make it
clearer.
While we're at it, also fix the handling of unexpected bits in the
Multi-AP IE. 4 bits are reserved in the specification, so these
certainly have to be ignored. The specification also doesn't say that
setting one of the other bits is not allowed. Therefore, only report
unexpected values in the Multi-AP IE, don't reject because of it.
Note that a malformed IE (containing more than one byte) still triggers
a rejection.
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
---
v4: new patch
Cfr. discussion on http://lists.infradead.org/pipermail/hostap/2019-January/039232.html
and follow-ups.
---
src/ap/ieee802_11.c | 38 +++++++++++++++++++-----------------
tests/hwsim/test_multi_ap.py | 6 ++----
2 files changed, 22 insertions(+), 22 deletions(-)
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2253,28 +2253,30 @@ static u16 check_multi_ap(struct hostapd
}
}
- if (multi_ap_value == MULTI_AP_BACKHAUL_STA)
- sta->flags |= WLAN_STA_MULTI_AP;
-
- if ((hapd->conf->multi_ap & BACKHAUL_BSS) &&
- multi_ap_value == MULTI_AP_BACKHAUL_STA)
- return WLAN_STATUS_SUCCESS;
-
- if (hapd->conf->multi_ap & FRONTHAUL_BSS) {
- if (multi_ap_value == MULTI_AP_BACKHAUL_STA) {
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO,
- "Backhaul STA tries to associate with fronthaul-only BSS");
- return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
- }
- return WLAN_STATUS_SUCCESS;
+ if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP IE with unexpected value 0x%02x",
+ multi_ap_value);
+
+ if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) {
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+ return WLAN_STATUS_SUCCESS;
+
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Non-Multi-AP STA tries to associate with backhaul-only BSS");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
}
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO,
- "Non-Multi-AP STA tries to associate with backhaul-only BSS");
- return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ if (!(hapd->conf->multi_ap & BACKHAUL_BSS))
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Backhaul STA tries to associate with fronthaul-only BSS");
+
+ sta->flags |= WLAN_STA_MULTI_AP;
+ return WLAN_STATUS_SUCCESS;
}
--- a/tests/hwsim/test_multi_ap.py
+++ b/tests/hwsim/test_multi_ap.py
@@ -59,7 +59,5 @@ def test_multi_ap_fronthaul_on_ap(dev, a
dev[0].request("DISCONNECT")
if ev is None:
raise Exception("Connection result not reported")
- if "CTRL-EVENT-ASSOC-REJECT" not in ev:
- raise Exception("Association rejection not reported")
- if "status_code=12" not in ev:
- raise Exception("Unexpected association status code: " + ev)
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection result")

@ -0,0 +1,342 @@
From ad3c6faca118c23cdafef418dc27b3cee7d0e06e Mon Sep 17 00:00:00 2001
From: "Arnout Vandecappelle (Essensium/Mind)" <arnout@mind.be>
Date: Wed, 9 Jan 2019 19:19:26 +0100
Subject: [PATCH] WPS: wps_build_wfa_ext(): add multi_ap_subelem parameter
The Multi-AP specification adds a new subelement to the WFA extension
element in the WPS exchange. Add an additional parameter to
wps_build_wfa_ext() to add this subelement. The subelement is only added
if the parameter is non-0. Note that we don't reuse the existing
MULTI_AP_SUB_ELEM_TYPE definition here, but rather define a new
WFA_ELEM_MULTI_AP, to make sure the enum of WFA subelement types remains
complete.
For now, all callers set the multi_ap_subelem parameter to 0.
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
---
v4: Split off from supplicant WPS patch
Since the original patch from Davina Lyu didn't have this extra
argument, I kept myself as the author of this patch.
---
src/p2p/p2p_build.c | 2 +-
src/wps/wps.c | 6 +++---
src/wps/wps_attr_build.c | 11 ++++++++++-
src/wps/wps_common.c | 16 ++++++++--------
src/wps/wps_defs.h | 3 ++-
src/wps/wps_enrollee.c | 10 +++++-----
src/wps/wps_er.c | 4 ++--
src/wps/wps_i.h | 3 ++-
src/wps/wps_registrar.c | 14 +++++++-------
src/wps/wps_upnp.c | 2 +-
10 files changed, 41 insertions(+), 30 deletions(-)
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -802,7 +802,7 @@ int p2p_build_wps_ie(struct p2p_data *p2
wpabuf_put_be16(buf, p2p->cfg->config_methods);
}
- if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0)
+ if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0)
return -1;
if (all_attr && p2p->cfg->num_sec_dev_types) {
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -430,7 +430,7 @@ struct wpabuf * wps_build_assoc_req_ie(e
if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) ||
- wps_build_wfa_ext(ie, 0, NULL, 0)) {
+ wps_build_wfa_ext(ie, 0, NULL, 0, 0)) {
wpabuf_free(ie);
return NULL;
}
@@ -464,7 +464,7 @@ struct wpabuf * wps_build_assoc_resp_ie(
if (wps_build_version(ie) ||
wps_build_resp_type(ie, WPS_RESP_AP) ||
- wps_build_wfa_ext(ie, 0, NULL, 0)) {
+ wps_build_wfa_ext(ie, 0, NULL, 0, 0)) {
wpabuf_free(ie);
return NULL;
}
@@ -516,7 +516,7 @@ struct wpabuf * wps_build_probe_req_ie(u
wps_build_model_name(dev, ie) ||
wps_build_model_number(dev, ie) ||
wps_build_dev_name(dev, ie) ||
- wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
+ wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0, 0) ||
wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
||
wps_build_secondary_dev_type(dev, ie)
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -203,7 +203,8 @@ int wps_build_version(struct wpabuf *msg
int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
- const u8 *auth_macs, size_t auth_macs_count)
+ const u8 *auth_macs, size_t auth_macs_count,
+ u8 multi_ap_subelem)
{
u8 *len;
@@ -244,6 +245,14 @@ int wps_build_wfa_ext(struct wpabuf *msg
MAC2STR(&auth_macs[i * ETH_ALEN]));
}
+ if (multi_ap_subelem) {
+ wpa_printf(MSG_DEBUG, "WPS: * Multi-AP (0x%x)",
+ multi_ap_subelem);
+ wpabuf_put_u8(msg, WFA_ELEM_MULTI_AP);
+ wpabuf_put_u8(msg, 1); /* length */
+ wpabuf_put_u8(msg, multi_ap_subelem);
+ }
+
WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
#ifdef CONFIG_WPS_TESTING
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -374,7 +374,7 @@ struct wpabuf * wps_get_oob_cred(struct
(rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
(channel && wps_build_ap_channel(plain, channel)) ||
wps_build_mac_addr(plain, wps->dev.mac_addr) ||
- wps_build_wfa_ext(plain, 0, NULL, 0)) {
+ wps_build_wfa_ext(plain, 0, NULL, 0, 0)) {
os_free(data.new_psk);
wpabuf_clear_free(plain);
return NULL;
@@ -421,7 +421,7 @@ struct wpabuf * wps_build_nfc_pw_token(u
if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
- wps_build_wfa_ext(data, 0, NULL, 0)) {
+ wps_build_wfa_ext(data, 0, NULL, 0, 0)) {
wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
"token");
wpabuf_clear_free(data);
@@ -586,7 +586,7 @@ struct wpabuf * wps_build_wsc_ack(struct
wps_build_msg_type(msg, WPS_WSC_ACK) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -610,7 +610,7 @@ struct wpabuf * wps_build_wsc_nack(struc
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_config_error(msg, wps->config_error) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -726,7 +726,7 @@ struct wpabuf * wps_build_nfc_handover_r
if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
nfc_dh_pubkey, NULL, 0) ||
wps_build_uuid_e(msg, ctx->uuid) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -809,7 +809,7 @@ struct wpabuf * wps_build_nfc_handover_s
wps_build_ssid(msg, ctx) ||
wps_build_ap_freq(msg, freq) ||
(bssid && wps_build_mac_addr(msg, bssid)) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -848,7 +848,7 @@ struct wpabuf * wps_build_nfc_handover_r
wps_build_rf_bands(&ctx->dev, msg, 0) ||
wps_build_serial_number(&ctx->dev, msg) ||
wps_build_uuid_e(msg, ctx->uuid) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -900,7 +900,7 @@ struct wpabuf * wps_build_nfc_handover_s
wps_build_rf_bands(&ctx->dev, msg, 0) ||
wps_build_serial_number(&ctx->dev, msg) ||
wps_build_uuid_e(msg, ctx->uuid) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -152,7 +152,8 @@ enum {
WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
- WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
+ WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05,
+ WFA_ELEM_MULTI_AP = 0x06
};
/* Device Password ID */
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -152,7 +152,7 @@ static struct wpabuf * wps_build_m1(stru
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_os_version(&wps->wps->dev, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
wpabuf_free(msg);
return NULL;
@@ -190,7 +190,7 @@ static struct wpabuf * wps_build_m3(stru
wps_build_msg_type(msg, WPS_M3) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_e_hash(wps, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(msg);
return NULL;
@@ -223,7 +223,7 @@ static struct wpabuf * wps_build_m5(stru
wps_build_e_snonce1(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
@@ -393,7 +393,7 @@ static struct wpabuf * wps_build_m7(stru
(wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
@@ -430,7 +430,7 @@ static struct wpabuf * wps_build_wsc_don
wps_build_msg_type(msg, WPS_WSC_DONE) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1530,7 +1530,7 @@ void wps_er_set_sel_reg(struct wps_er *e
wps_er_build_selected_registrar(msg, sel_reg) ||
wps_er_build_dev_password_id(msg, dev_passwd_id) ||
wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) ||
- wps_build_wfa_ext(msg, 0, auth_macs, count) ||
+ wps_build_wfa_ext(msg, 0, auth_macs, count, 0) ||
wps_er_build_uuid_r(msg, er->wps->uuid)) {
wpabuf_free(msg);
return;
@@ -2048,7 +2048,7 @@ struct wpabuf * wps_er_config_token_from
data.wps = wps;
data.use_cred = cred;
if (wps_build_cred(&data, ret) ||
- wps_build_wfa_ext(ret, 0, NULL, 0)) {
+ wps_build_wfa_ext(ret, 0, NULL, 0, 0)) {
wpabuf_free(ret);
return NULL;
}
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -163,7 +163,8 @@ int wps_build_encr_settings(struct wps_d
struct wpabuf *plain);
int wps_build_version(struct wpabuf *msg);
int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
- const u8 *auth_macs, size_t auth_macs_count);
+ const u8 *auth_macs, size_t auth_macs_count,
+ u8 multi_ap_subelem);
int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1281,7 +1281,7 @@ static int wps_set_ie(struct wps_registr
wps_build_sel_reg_config_methods(reg, beacon) ||
wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
(reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon, 0)) ||
- wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+ wps_build_wfa_ext(beacon, 0, auth_macs, count, 0) ||
wps_build_vendor_ext(&reg->wps->dev, beacon)) {
wpabuf_free(beacon);
wpabuf_free(probe);
@@ -1311,7 +1311,7 @@ static int wps_set_ie(struct wps_registr
wps_build_device_attrs(&reg->wps->dev, probe) ||
wps_build_probe_config_methods(reg, probe) ||
(reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe, 0)) ||
- wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+ wps_build_wfa_ext(probe, 0, auth_macs, count, 0) ||
wps_build_vendor_ext(&reg->wps->dev, probe)) {
wpabuf_free(beacon);
wpabuf_free(probe);
@@ -1845,7 +1845,7 @@ static struct wpabuf * wps_build_m2(stru
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_os_version(&wps->wps->dev, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -1913,7 +1913,7 @@ static struct wpabuf * wps_build_m2d(str
wps_build_assoc_state(wps, msg) ||
wps_build_config_error(msg, err) ||
wps_build_os_version(&wps->wps->dev, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -1949,7 +1949,7 @@ static struct wpabuf * wps_build_m4(stru
wps_build_r_snonce1(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
@@ -1984,7 +1984,7 @@ static struct wpabuf * wps_build_m6(stru
wps_build_r_snonce2(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
@@ -2021,7 +2021,7 @@ static struct wpabuf * wps_build_m8(stru
(!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_clear_free(msg);
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -599,7 +599,7 @@ static struct wpabuf * build_fake_wsc_ac
wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put(msg, WPS_NONCE_LEN);
- if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ if (wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}

@ -0,0 +1,217 @@
From 6c4c98db9420a3321bbf091cfc254de5eba4b404 Mon Sep 17 00:00:00 2001
From: Davina Lu <ylu@quantenna.com>
Date: Tue, 15 Jan 2019 19:17:51 +0100
Subject: [PATCH] wpa_supplicant: support Multi-AP backhaul STA onboarding
The Wi-Fi Alliance Multi-AP Specification v1.0 allows onboarding of a
backhaul STA through WPS. To enable this, the backhaul STA needs to add
a Multi-AP IE to the WFA vendor extension element in the WSC M1 message
that indicates it supports the Multi-AP backhaul STA role. The registrar
(if it support Multi-AP onboarding) will respond to that with a WSC M8
message that also contains the Multi-AP IE, and that contains the
credentials for the backhaul SSID (which may be different from the SSID
on which WPS is performed).
Introduce a new parameter to wpas_wps_start_pbc() and allow it to be
set via control interface's new multi_ap=1 parameter of WPS_PBC call.
multi_ap_backhaul_sta is set to 1 in the automatically created SSID.
Thus, if the AP does not support Multi-AP, association will fail and
WPS will be terminated.
Only wps_pbc is supported.
The multi_ap argument is only added to the socket interface, not to the
dbus interface.
Signed-off-by: Davina Lu <ylu@quantenna.com>
Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v4: use argument to wps_pbc instead of a global configuration option
(requested by Jouni)
---
src/eap_peer/eap_wsc.c | 3 +++
src/wps/wps.h | 6 ++++++
src/wps/wps_enrollee.c | 6 +++++-
wpa_supplicant/ctrl_iface.c | 5 ++++-
wpa_supplicant/dbus/dbus_new_handlers_wps.c | 2 +-
wpa_supplicant/dbus/dbus_old_handlers_wps.c | 4 ++--
wpa_supplicant/events.c | 2 +-
wpa_supplicant/p2p_supplicant.c | 2 +-
wpa_supplicant/wps_supplicant.c | 9 +++++++--
wpa_supplicant/wps_supplicant.h | 2 +-
10 files changed, 31 insertions(+), 10 deletions(-)
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -274,6 +274,9 @@ static void * eap_wsc_init(struct eap_sm
cfg.pin, cfg.pin_len, 0);
}
+ if (os_strstr(phase1, "multi_ap=1"))
+ wps->multi_ap_backhaul_sta = 1;
+
/* Use reduced client timeout for WPS to avoid long wait */
if (sm->ClientTimeout > 30)
sm->ClientTimeout = 30;
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -613,6 +613,12 @@ struct wps_context {
int ap_setup_locked;
/**
+ * multi_ap_backhaul_sta - Whether this is a Multi-AP backhaul STA
+ * enrollee
+ */
+ int multi_ap_backhaul_sta;
+
+ /**
* uuid - Own UUID
*/
u8 uuid[16];
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -105,6 +105,7 @@ static struct wpabuf * wps_build_m1(stru
{
struct wpabuf *msg;
u16 config_methods;
+ u8 multi_ap_backhaul_sta = 0;
if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
return NULL;
@@ -134,6 +135,9 @@ static struct wpabuf * wps_build_m1(stru
WPS_CONFIG_PHY_PUSHBUTTON);
}
+ if (wps->wps->multi_ap_backhaul_sta)
+ multi_ap_backhaul_sta = MULTI_AP_BACKHAUL_STA;
+
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_M1) ||
wps_build_uuid_e(msg, wps->uuid_e) ||
@@ -152,7 +156,7 @@ static struct wpabuf * wps_build_m1(stru
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_os_version(&wps->wps->dev, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, multi_ap_backhaul_sta) ||
wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
wpabuf_free(msg);
return NULL;
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1167,6 +1167,7 @@ static int wpa_supplicant_ctrl_iface_wps
#ifdef CONFIG_AP
u8 *_p2p_dev_addr = NULL;
#endif /* CONFIG_AP */
+ int multi_ap = 0;
if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
_bssid = NULL;
@@ -1184,6 +1185,8 @@ static int wpa_supplicant_ctrl_iface_wps
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'",
cmd);
return -1;
+ } else if (os_strncmp(cmd, "multi_ap=", 9) == 0) {
+ multi_ap = atoi(cmd + 9);
}
#ifdef CONFIG_AP
@@ -1191,7 +1194,7 @@ static int wpa_supplicant_ctrl_iface_wps
return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr);
#endif /* CONFIG_AP */
- return wpas_wps_start_pbc(wpa_s, _bssid, 0);
+ return wpas_wps_start_pbc(wpa_s, _bssid, 0, multi_ap);
}
--- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -289,7 +289,7 @@ DBusMessage * wpas_dbus_handler_wps_star
if (ret > 0)
os_snprintf(npin, sizeof(npin), "%08d", ret);
} else {
- ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0);
+ ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0, 0);
}
if (ret < 0) {
--- a/wpa_supplicant/dbus/dbus_old_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers_wps.c
@@ -37,9 +37,9 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DB
return wpas_dbus_new_invalid_opts_error(message, NULL);
if (os_strcmp(arg_bssid, "any") == 0)
- ret = wpas_wps_start_pbc(wpa_s, NULL, 0);
+ ret = wpas_wps_start_pbc(wpa_s, NULL, 0, 0);
else if (!hwaddr_aton(arg_bssid, bssid))
- ret = wpas_wps_start_pbc(wpa_s, bssid, 0);
+ ret = wpas_wps_start_pbc(wpa_s, bssid, 0, 0);
else {
return wpas_dbus_new_invalid_opts_error(message,
"Invalid BSSID");
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -4816,7 +4816,7 @@ void supplicant_event(void *ctx, enum wp
break;
case EVENT_WPS_BUTTON_PUSHED:
#ifdef CONFIG_WPS
- wpas_wps_start_pbc(wpa_s, NULL, 0);
+ wpas_wps_start_pbc(wpa_s, NULL, 0, 0);
#endif /* CONFIG_WPS */
break;
case EVENT_AVOID_FREQUENCIES:
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1649,7 +1649,7 @@ static void wpas_start_wps_enrollee(stru
wpa_supplicant_ap_deinit(wpa_s);
wpas_copy_go_neg_results(wpa_s, res);
if (res->wps_method == WPS_PBC) {
- wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
+ wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1, 0);
#ifdef CONFIG_WPS_NFC
} else if (res->wps_method == WPS_NFC) {
wpas_wps_start_nfc(wpa_s, res->peer_device_addr,
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1137,9 +1137,10 @@ static void wpas_wps_reassoc(struct wpa_
int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
- int p2p_group)
+ int p2p_group, int multi_ap_backhaul_sta)
{
struct wpa_ssid *ssid;
+ char phase1[32];
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
@@ -1177,10 +1178,14 @@ int wpas_wps_start_pbc(struct wpa_suppli
}
}
#endif /* CONFIG_P2P */
- if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0)
+ if (os_snprintf(phase1, sizeof(phase1), "pbc=1%s",
+ multi_ap_backhaul_sta ? " multi_ap=1" : "") ||
+ wpa_config_set_quoted(ssid, "phase1", phase1) < 0)
return -1;
if (wpa_s->wps_fragment_size)
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
+ if (multi_ap_backhaul_sta)
+ ssid->multi_ap_backhaul_sta = 1;
wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -30,7 +30,7 @@ void wpas_wps_deinit(struct wpa_supplica
int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s);
enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid);
int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
- int p2p_group);
+ int p2p_group, int multi_ap_backhaul_sta);
int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin, int p2p_group, u16 dev_pw_id);
void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s);

@ -0,0 +1,339 @@
From 8b04a4cddbd6dbadb24279713af7ac677e80d342 Mon Sep 17 00:00:00 2001
From: Davina Lu <ylu@quantenna.com>
Date: Tue, 2 Oct 2018 18:34:14 -0700
Subject: [PATCH] hostapd: support Multi-AP backhaul STA onboarding
The Wi-Fi Alliance Multi-AP Specification v1.0 allows onboarding of a
backhaul STA through WPS. To enable this, the WPS registrar offers a
different set of credentials (backhaul credentials instead of fronthaul
credentials) when the Multi-AP subelement is present in the WFA vendor
extension element of the WSC M1 message.
Add 3 new configuration options to specify the backhaul credentials for
the hostapd internal registrar: multi_ap_backhaul_ssid,
multi_ap_backhaul_wpa_psk, multi_ap_backhaul_wpa_passphrase. These are
only relevant for a fronthaul SSID, i.e. where multi_ap is set to 2 or
3. When these options are set, pass the backhaul credentials instead of
the normal credentials when the Multi-AP subelement is present.
Ignore the Multi-AP subelement if the backhaul config options are not
set. Note that for an SSID which is fronthaul and backhaul at the same
time (i.e., multi_ap == 3), this results in the correct credentials
being sent anyway.
The security to be used for the backaul BSS is fixed to WPA2PSK. The
Multi-AP Specification only allows Open and WPA2PSK networks to be
configured. Although not stated explicitly, the backhaul link is
intended to be always encrypted, hence WPA2PSK.
To build the credentials, the credential-building code is essentially
copied and simplified. Indeed, the backhaul credentials are always
WPA2PSK and never use per-device PSK. All the options set for the
fronthaul BSS WPS are simply ignored.
Signed-off-by: Davina Lu <ylu@quantenna.com>
Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
---
v4: no change
---
hostapd/config_file.c | 47 ++++++++++++++++++++++++++++++++++++++++
hostapd/hostapd.conf | 9 ++++++++
src/ap/ap_config.c | 2 ++
src/ap/ap_config.h | 1 +
src/ap/wps_hostapd.c | 26 ++++++++++++++++++++++
src/wps/wps.h | 32 +++++++++++++++++++++++++++
src/wps/wps_attr_parse.c | 11 ++++++++++
src/wps/wps_attr_parse.h | 1 +
src/wps/wps_dev_attr.c | 5 +++++
src/wps/wps_dev_attr.h | 1 +
src/wps/wps_registrar.c | 25 ++++++++++++++++++++-
11 files changed, 159 insertions(+), 1 deletion(-)
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3479,6 +3479,53 @@ static int hostapd_config_fill(struct ho
line, pos);
return 1;
}
+ } else if (os_strcmp(buf, "multi_ap_backhaul_ssid") == 0) {
+ size_t slen;
+ char *str = wpa_config_parse_string(pos, &slen);
+
+ if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+ line, pos);
+ os_free(str);
+ return 1;
+ }
+ os_memcpy(bss->multi_ap_backhaul_ssid.ssid, str, slen);
+ bss->multi_ap_backhaul_ssid.ssid_len = slen;
+ bss->multi_ap_backhaul_ssid.ssid_set = 1;
+ os_free(str);
+ } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_passphrase") == 0) {
+ int len = os_strlen(pos);
+
+ if (len < 8 || len > 63) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid WPA passphrase length %d (expected 8..63)",
+ line, len);
+ return 1;
+ }
+ os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
+ bss->multi_ap_backhaul_ssid.wpa_passphrase = os_strdup(pos);
+ if (bss->multi_ap_backhaul_ssid.wpa_passphrase) {
+ hostapd_config_clear_wpa_psk(&bss->multi_ap_backhaul_ssid.wpa_psk);
+ bss->multi_ap_backhaul_ssid.wpa_passphrase_set = 1;
+ }
+ } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_psk") == 0) {
+ hostapd_config_clear_wpa_psk(&bss->multi_ap_backhaul_ssid.wpa_psk);
+ bss->multi_ap_backhaul_ssid.wpa_psk =
+ os_zalloc(sizeof(struct hostapd_wpa_psk));
+ if (bss->multi_ap_backhaul_ssid.wpa_psk == NULL)
+ return 1;
+ if (hexstr2bin(pos, bss->multi_ap_backhaul_ssid.wpa_psk->psk,
+ PMK_LEN) ||
+ pos[PMK_LEN * 2] != '\0') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+ line, pos);
+ hostapd_config_clear_wpa_psk(&bss->multi_ap_backhaul_ssid.wpa_psk);
+ return 1;
+ }
+ bss->multi_ap_backhaul_ssid.wpa_psk->group = 1;
+ os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
+ bss->multi_ap_backhaul_ssid.wpa_passphrase = NULL;
+ bss->multi_ap_backhaul_ssid.wpa_psk_set = 1;
} else if (os_strcmp(buf, "upnp_iface") == 0) {
os_free(bss->upnp_iface);
bss->upnp_iface = os_strdup(pos);
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1852,6 +1852,15 @@ own_ip_addr=127.0.0.1
# attribute.
#ap_settings=hostapd.ap_settings
+# Multi-AP backhaul BSS config
+# Used in WPS when multi_ap=2 or 3. Defines "backhaul BSS" credentials.
+# These are passed in WPS M8 instead of the normal (fronthaul) credentials
+# if the enrollee has the Multi-AP subelement set. Backhaul SSID is formatted
+# like ssid2. The key is set like wpa_psk or wpa_passphrase.
+#multi_ap_backhaul_ssid="backhaul"
+#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+#multi_ap_backhaul_wpa_passphrase=secret passphrase
+
# WPS UPnP interface
# If set, support for external Registrars is enabled.
#upnp_iface=br0
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -582,6 +582,8 @@ void hostapd_config_free_bss(struct host
os_free(conf->ap_pin);
os_free(conf->extra_cred);
os_free(conf->ap_settings);
+ hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk);
+ str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase);
os_free(conf->upnp_iface);
os_free(conf->friendly_name);
os_free(conf->manufacturer_url);
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -456,6 +456,7 @@ struct hostapd_bss_config {
int force_per_enrollee_psk;
u8 *ap_settings;
size_t ap_settings_len;
+ struct hostapd_ssid multi_ap_backhaul_ssid;
char *upnp_iface;
char *friendly_name;
char *manufacturer_url;
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -962,6 +962,7 @@ static void hostapd_free_wps(struct wps_
wpabuf_free(wps->dev.vendor_ext[i]);
wps_device_data_free(&wps->dev);
os_free(wps->network_key);
+ os_free(wps->multi_ap_backhaul_network_key);
hostapd_wps_nfc_clear(wps);
wpabuf_free(wps->dh_pubkey);
wpabuf_free(wps->dh_privkey);
@@ -1131,6 +1132,31 @@ int hostapd_init_wps(struct hostapd_data
wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP;
}
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS &&
+ hapd->conf->multi_ap_backhaul_ssid.ssid_len) {
+ wps->multi_ap_backhaul_ssid_len =
+ hapd->conf->multi_ap_backhaul_ssid.ssid_len;
+ os_memcpy(wps->multi_ap_backhaul_ssid,
+ hapd->conf->multi_ap_backhaul_ssid.ssid,
+ wps->multi_ap_backhaul_ssid_len);
+ if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
+ wps->multi_ap_backhaul_network_key =
+ (u8 *) os_strdup(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+ wps->multi_ap_backhaul_network_key_len =
+ os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+ } else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
+ wps->multi_ap_backhaul_network_key =
+ os_malloc(2 * PMK_LEN + 1);
+ if (wps->multi_ap_backhaul_network_key == NULL)
+ goto fail;
+ wpa_snprintf_hex((char *) wps->multi_ap_backhaul_network_key,
+ 2 * PMK_LEN + 1,
+ conf->multi_ap_backhaul_ssid.wpa_psk->psk,
+ PMK_LEN);
+ wps->multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
+ }
+ }
+
wps->ap_settings = conf->ap_settings;
wps->ap_settings_len = conf->ap_settings_len;
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -100,6 +100,7 @@ struct wps_device_data {
struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
int p2p;
+ u8 multi_ap_ext;
};
/**
@@ -730,6 +731,37 @@ struct wps_context {
int psk_set;
/**
+ * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul
+ * enrollee
+ *
+ * This SSID is used by the Registrar to fill in information for
+ * Credentials when the enrollee advertises it is a Multi-AP backhaul
+ * STA.
+ */
+ u8 multi_ap_backhaul_ssid[SSID_MAX_LEN];
+
+ /**
+ * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in
+ * octets
+ */
+ size_t multi_ap_backhaul_ssid_len;
+
+ /**
+ * multi_ap_backhaul_network_key - The Network Key (PSK) for the
+ * Multi-AP backhaul enrollee.
+ *
+ * This key can be either the ASCII passphrase (8..63 characters) or the
+ * 32-octet PSK (64 hex characters).
+ */
+ u8 *multi_ap_backhaul_network_key;
+
+ /**
+ * multi_ap_backhaul_network_key_len - Length of
+ * multi_ap_backhaul_network_key in octets
+ */
+ size_t multi_ap_backhaul_network_key_len;
+
+ /**
* ap_settings - AP Settings override for M7 (only used at AP)
*
* If %NULL, AP Settings attributes will be generated based on the
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -67,6 +67,17 @@ static int wps_set_vendor_ext_wfa_subele
}
attr->registrar_configuration_methods = pos;
break;
+ case WFA_ELEM_MULTI_AP:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Invalid Multi-AP Extension length %u",
+ len);
+ return -1;
+ }
+ attr->multi_ap_ext = *pos;
+ wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x",
+ attr->multi_ap_ext);
+ break;
default:
wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
"Extension subelement %u", id);
--- a/src/wps/wps_attr_parse.h
+++ b/src/wps/wps_attr_parse.h
@@ -97,6 +97,7 @@ struct wps_parse_attr {
const u8 *cred[MAX_CRED_COUNT];
const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+ u8 multi_ap_ext;
};
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -389,6 +389,11 @@ int wps_process_os_version(struct wps_de
return 0;
}
+void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext)
+{
+ dev->multi_ap_ext = ext;
+ wpa_printf(MSG_DEBUG, "WPS: Multi-AP extension value %02x", dev->multi_ap_ext);
+}
int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
{
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -29,6 +29,7 @@ int wps_build_dev_name(struct wps_device
int wps_process_device_attrs(struct wps_device_data *dev,
struct wps_parse_attr *attr);
int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext);
int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
void wps_device_data_free(struct wps_device_data *dev);
int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1588,7 +1588,6 @@ int wps_build_credential_wrap(struct wpa
return 0;
}
-
int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
{
struct wpabuf *cred;
@@ -1603,6 +1602,29 @@ int wps_build_cred(struct wps_data *wps,
}
os_memset(&wps->cred, 0, sizeof(wps->cred));
+ if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA &&
+ wps->wps->multi_ap_backhaul_ssid_len) {
+ wpa_printf(MSG_DEBUG, "WPS: Use backhaul STA credentials");
+ os_memcpy(wps->cred.ssid, wps->wps->multi_ap_backhaul_ssid,
+ wps->wps->multi_ap_backhaul_ssid_len);
+ wps->cred.ssid_len = wps->wps->multi_ap_backhaul_ssid_len;
+ /* Backhaul is always WPA2PSK */
+ wps->cred.auth_type = WPS_AUTH_WPA2PSK;
+ wps->cred.encr_type = WPS_ENCR_AES;
+ /* Set MAC address in the Credential to be the Enrollee's MAC
+ * address
+ */
+ os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
+ if (wps->wps->multi_ap_backhaul_network_key) {
+ os_memcpy(wps->cred.key,
+ wps->wps->multi_ap_backhaul_network_key,
+ wps->wps->multi_ap_backhaul_network_key_len);
+ wps->cred.key_len =
+ wps->wps->multi_ap_backhaul_network_key_len;
+ }
+ goto use_provided;
+ }
+
os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
wps->cred.ssid_len = wps->wps->ssid_len;
@@ -2705,6 +2727,7 @@ static enum wps_process_res wps_process_
wps->use_psk_key = 1;
}
#endif /* WPS_WORKAROUNDS */
+ wps_process_vendor_ext_m1(&wps->peer_dev, attr->multi_ap_ext);
wps->state = SEND_M2;
return WPS_CONTINUE;

@ -0,0 +1,181 @@
From bd733055a22c8ca3bcd7648bf716da2713b3d9f1 Mon Sep 17 00:00:00 2001
From: "Arnout Vandecappelle (Essensium/Mind)" <arnout@mind.be>
Date: Mon, 21 Jan 2019 16:44:06 +0100
Subject: [PATCH] hostapd: add README-MULTI-AP
Document what hostapd and wpa_supplicant do for Multi-AP.
This is only included in hostapd, since a Multi-AP device is always an
access point so it should have hostapd.
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
---
v4: wps_pbc has multi_ap as a parameter instead of config option.
---
hostapd/README-MULTI-AP | 160 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 160 insertions(+)
create mode 100644 hostapd/README-MULTI-AP
--- /dev/null
+++ b/hostapd/README-MULTI-AP
@@ -0,0 +1,160 @@
+hostapd, wpa_supplicant and the Multi-AP Specification
+======================================================
+
+This document describes how hostapd and wpa_supplicant can be configured to
+support the Multi-AP Specification.
+
+Introduction to Multi-AP
+------------------------
+
+The Wi-Fi Alliance Multi-AP Specification is the technical specification for
+Wi-Fi CERTIFIED EasyMesh(TM) [1], the Wi-Fi Alliance® certification program for
+Multi-AP. It defines control protocols between Wi-Fi® access points (APs) to
+join them into a network with centralized control and operation. It is targeted
+only at routers (repeaters, gateways, ...), not at clients. Clients are not
+involved at all in the protocols.
+
+Most of the Multi-AP specification falls outside of the scope of
+hostapd/wpa_supplicant. hostapd/wpa_supplicant is only involved for the items
+summarized below. The rest of the protocol must be implemented by a separate
+daemon, e.g. prplMesh [2]. That daemon also needs to communicate with hostapd,
+e.g. to get a list of associated clients, but this can be done using the normal
+hostapd interfaces.
+
+hostapd/wpa_supplicant needs to be configured specifically to support:
+- the WPS onboarding process;
+- configuring backhaul links.
+
+The text below refers to "Multi-AP Specification v1.0" [3].
+
+
+Fronthaul and backhaul links
+----------------------------
+
+In a Multi-AP network, the central controller can configure the SSIDs on the
+devices that are joined into the network. These are called fronthaul SSIDs.
+From the point of view of hostapd, there is nothing special about these
+fronthaul SSIDs.
+
+In addition to fronthaul SSIDs, the controller can also configure backhaul
+links. A backhaul link is a link between two access point devices, giving
+internet access to access point devices that don't have a wired link. The
+Multi-AP specification doesn't dictate this, but typically the backhaul link
+will be bridged into a LAN together with (one of) the fronthaul SSID(s) and the
+wired Ethernet ports.
+
+A backhaul link must be treated specially by hostapd and wpa_supplicant. One
+side of the backhaul link is configured through the Multi-AP protocol as the
+"backhaul STA", i.e. the client side of the link. A backhaul STA is like any
+station and is handled appropriately by wpa_supplicant, but two additional
+features are required. It must send an additional information element in each
+(Re-)Association Request ([3], section 5.2, paragraph 4). In addition, it must
+use 4-address mode for all frames sent over this link ([3], section 14).
+Therefore, wpa_supplicant must be configured explicitly as the backhaul STA
+role, by setting 'multi_ap_backhaul_sta=1' in the network configuration block
+or when configuring the SSID through the client socket. When
+'multi_ap_backhaul_sta=1', wpa_supplicant includes the Multi-AP IE in
+(Re-)Association Request messages and verifies that it is included in the
+(Re-)Association Response. If it is not, association fails. If it is,
+wpa_supplicant sets 4-address mode for this interface through a driver
+callback.
+
+The AP side of the backhaul link is called a "backhaul SSID". Such an SSID must
+be handled specially by hostapd, because it must add an additional information
+element in each (Re-)Association Response, but only to stations that have
+identified themselves as backhaul stations ([3], section 5.2, paragraph 5-6).
+This is important because it is possible to use the same BSS and SSID for
+fronthaul and backhaul at the same time. The additional information element must
+only be used for frames sent to a backhaul STA, not to a normal STA. Also,
+frames sent to a backhaul STA must use 4-address mode, while frames sent to a
+normal STA (fronthaul, when it's a fronthaul and backhaul SSID) must use
+3-address mode.
+
+An SSID is configured in Multi-AP mode in hostapd by setting the 'multi_ap'
+configuration option to 1 (backhaul SSID), 2 (fronthaul SSID) or 3
+(simultaneous backhaul and fronthaul SSID). If this option is set, hostapd
+parses the Multi-AP information element in the Association Request. If the
+station is a backhaul STA and the SSID is configured as a backhaul SSID,
+hostapd sets up 4-address mode. Since there may be multiple stations connected
+simultaneously, and each of them has a different RA (receiver address), a VLAN
+is created for each backhaul STA and it is automatically added to a bridge.
+This is the same behavior as for WDS, and the relevant option ('bridge' or
+'wds_bridge') applies here as well.
+
+If 'multi_ap' is 1 (backhaul SSID only), any station that tries to associate
+without the Multi-AP information element will be denied.
+
+If 'multi_ap' is 2 (fronthaul SSID only), any station that tries to associate
+with the Multi-AP information element will be denied. That is also the only
+difference with 'multi_ap' set to 0: in the latter case, the Multi-AP
+information element is simply ignored.
+
+In summary, this is the end-to-end behaviour for a backhaul BSS (i.e.,
+multi_ap_backhaul_sta=1 in wpa_supplicant on STA, and multi_ap=1 or 3 in
+hostapd on AP). Note that point 1 means that hostapd must not be configured
+with WPS support on the backhaul BSS (multi_ap=1). hostapd does not check for
+that.
+
+1. Backhaul BSS beacons do not advertise WPS support (other than that, nothing
+ multi-ap specific).
+2. STA sends authentication req (nothing multi-ap specific).
+3. AP sends authentication resp (nothing multi-ap specific).
+4. STA sends association req with Multi-AP IE.
+5. AP send association resp with Multi-AP IE.
+6. STA and AP both use 4-address mode for data.
+
+
+WPS support
+-----------
+
+WPS requires more special handling. WPS must only be advertised on fronthaul
+SSIDs, not on backhaul SSIDs, so WPS should not be enabled on a backhaul-only
+SSID in hostapd.conf. The WPS configuration purely works on the fronthaul SSID.
+When a WPS M1 message has an additional subelement that indicates a request for
+a multi-AP backhaul link, hostapd must not respond with the normal fronthaul
+SSID credentials; instead, it should respond with the (potentially different)
+backhaul SSID credentials.
+
+To support this, hostapd has the 'multi_ap_backhaul_ssid',
+'multi_ap_backhaul_wpa_psk' and 'multi_ap_backhaul_wpa_passphrase' options.
+When these are set on an SSID with WPS, they are used instead of the normal
+credentials when hostapd receives a WPS M1 message with the Multi-AP IE. Only
+WPA2 Personal is supported in the Multi-AP specification, so there is no need
+to specify authentication or encryption options. For the backhaul credentials,
+per-device PSK is not supported.
+
+If the SSID is a simultaneous backhaul and fronthaul SSID, there is no need to
+specify the backhaul credentials, since the backhaul and fronthaul credentials
+are identical.
+
+To enable the Multi-AP backhaul STA feature when it performs WPS, a new
+parameter has been introduced to the WPS_PBC control interface call.
+When this option is set, it adds the multi-AP backhaul subelement to
+the association and M1 messages. It then configures the new SSID with
+'multi_ap_backhaul_sta=1'. Note that this means that if the AP does not
+follow the Multi-AP specification, wpa_supplicant will fail to
+associate.
+
+In summary, this is the end-to-end behaviour for WPS of a backhaul link (i.e.,
+multi_ap=1 option is given in the wps_pbc call on the STA side, and multi_ap=2
+and multi_ap_backhaul_ssid and either multi_ap_backhaul_wpa_psk or
+multi_ap_backhaul_wpa_passphrase are set to the credentials of a backhaul SSID
+in hostapd on registrar AP).
+
+1. Fronthaul BSS beacons advertise WPS support (nothing multi-ap specific).
+2. Enrollee sends authentication req (nothing multi-ap specific).
+3. AP sends authentication resp (nothing multi-ap specific).
+4. Enrollee sends association req with Multi-AP IE.
+5. AP send association resp with Multi-AP IE.
+6. Enrollee sends M1 with additional Multi-AP subelement
+7. AP sends M8 with backhaul instead of fronthaul credentials.
+8. Enrollee sends Deauth.
+
+
+References
+----------
+
+[1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh
+[2] https://github.com/prplfoundation/prplMesh
+[3] https://www.wi-fi.org/file/multi-ap-specification-v10
+ (requires registration)

@ -0,0 +1,182 @@
From 0729e01f5830ebf4701f0b1b7ff1bd2a2eedae40 Mon Sep 17 00:00:00 2001
From: "Arnout Vandecappelle (Essensium/Mind)" <arnout@mind.be>
Date: Tue, 12 Feb 2019 11:02:42 +0100
Subject: [PATCH] tests: add WPS tests to multi_ap hwsim tests
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
---
v4: new patch
---
tests/hwsim/test_multi_ap.py | 164 +++++++++++++++++++++++++++++++++++
1 file changed, 164 insertions(+)
--- a/tests/hwsim/test_multi_ap.py
+++ b/tests/hwsim/test_multi_ap.py
@@ -61,3 +61,167 @@ def test_multi_ap_fronthaul_on_ap(dev, a
raise Exception("Connection result not reported")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Unexpected connection result")
+
+def run_multi_ap_wps(dev, apdev, params, multi_ap_bssid = None):
+ """Helper for running Multi-AP WPS tests
+
+ dev[0] does multi_ap WPS, dev[1] does normal WPS. apdev[0] is the fronthaul
+ BSS. If there is a separate backhaul BSS, it must have been set up by the
+ caller. params are the normal SSID parameters, they will be extended with
+ the WPS parameters. multi_ap_bssid must be given if it is not equal to the
+ fronthaul BSSID."""
+
+ if multi_ap_bssid is None:
+ multi_ap_bssid = apdev[0]['bssid']
+ params.update({"wps_state": "2", "eap_server": "1"})
+
+ # WPS with multi-ap station dev[0]
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("WPS_PBC")
+ if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+
+ dev[0].request("WPS_PBC multi_ap=1")
+ dev[0].wait_connected(timeout=20)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != multi_ap_bssid:
+ raise Exception("Not fully connected")
+ if status['ssid'] != params['multi_ap_backhaul_ssid'].strip('"'):
+ raise Exception("Unexpected SSID %s != %s" % (status['ssid'], params["multi_ap_backhaul_ssid"]))
+ if status['pairwise_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration %s" % status['pairwise_cipher'])
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+ status = hapd.request("WPS_GET_STATUS")
+ if "PBC Status: Disabled" not in status:
+ raise Exception("PBC status not shown correctly")
+ if "Last WPS result: Success" not in status:
+ raise Exception("Last WPS result not shown correctly")
+ if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
+ raise Exception("Peer address not shown correctly")
+
+ if len(dev[0].list_networks()) != 1:
+ raise Exception("Unexpected number of network blocks")
+
+ # WPS with non-multi-ap station dev[1]
+ hapd.request("WPS_PBC")
+ if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+
+ dev[1].request("WPS_PBC")
+ dev[1].wait_connected(timeout=20)
+ status = dev[1].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+ if status['ssid'] != params["ssid"]:
+ raise Exception("Unexpected SSID")
+ # Fronthaul may be something else than WPA2-PSK so don't test it.
+
+ status = hapd.request("WPS_GET_STATUS")
+ if "PBC Status: Disabled" not in status:
+ raise Exception("PBC status not shown correctly")
+ if "Last WPS result: Success" not in status:
+ raise Exception("Last WPS result not shown correctly")
+ if "Peer Address: " + dev[1].p2p_interface_addr() not in status:
+ raise Exception("Peer address not shown correctly")
+
+ if len(dev[1].list_networks()) != 1:
+ raise Exception("Unexpected number of network blocks")
+
+
+def test_multi_ap_wps_shared(dev, apdev):
+ """WPS on shared fronthaul/backhaul AP"""
+ ssid = "multi-ap-wps"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params.update({"multi_ap": "3",
+ "multi_ap_backhaul_ssid": '"%s"' % ssid,
+ "multi_ap_backhaul_wpa_passphrase": passphrase})
+ run_multi_ap_wps(dev, apdev, params)
+
+def test_multi_ap_wps_shared_psk(dev, apdev):
+ """WPS on shared fronthaul/backhaul AP using PSK"""
+ ssid = "multi-ap-wps"
+ psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+ params = hostapd.wpa2_params(ssid=ssid)
+ params.update({"wpa_psk": psk, "multi_ap": "3",
+ "multi_ap_backhaul_ssid": '"%s"' % ssid,
+ "multi_ap_backhaul_wpa_psk": psk})
+ run_multi_ap_wps(dev, apdev, params)
+
+def test_multi_ap_wps_split(dev, apdev):
+ """WPS on split fronthaul and backhaul AP"""
+ backhaul_ssid = "multi-ap-backhaul-wps"
+ backhaul_passphrase = "87654321"
+ params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps", passphrase="12345678")
+ params.update({"multi_ap": "2",
+ "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
+ "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase})
+ params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, passphrase=backhaul_passphrase)
+ params_backhaul.update({"multi_ap": "1"})
+ hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul)
+
+ run_multi_ap_wps(dev, apdev, params, hapd_backhaul.own_addr())
+
+def test_multi_ap_wps_split_psk(dev, apdev):
+ """WPS on split fronthaul and backhaul AP"""
+ backhaul_ssid = "multi-ap-backhaul-wps"
+ backhaul_psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+ params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps", passphrase="12345678")
+ params.update({"multi_ap": "2",
+ "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
+ "multi_ap_backhaul_wpa_psk": backhaul_psk})
+ params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid)
+ params_backhaul.update({"multi_ap": "1", "wpa_psk": backhaul_psk})
+ hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul)
+
+ run_multi_ap_wps(dev, apdev, params, hapd_backhaul.own_addr())
+
+def test_multi_ap_wps_split_mixed(dev, apdev):
+ """WPS on split fronthaul and backhaul AP with mixed-mode fronthaul"""
+ backhaul_ssid = "multi-ap-backhaul-wps"
+ backhaul_passphrase = "87654321"
+ params = hostapd.wpa_mixed_params(ssid="multi-ap-fronthaul-wps", passphrase="12345678")
+ params.update({"multi_ap": "2",
+ "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
+ "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase})
+ params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, passphrase=backhaul_passphrase)
+ params_backhaul.update({"multi_ap": "1"})
+ hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul)
+
+ run_multi_ap_wps(dev, apdev, params, hapd_backhaul.own_addr())
+
+def test_multi_ap_wps_split_open(dev, apdev):
+ """WPS on split fronthaul and backhaul AP with open fronthaul"""
+ backhaul_ssid = "multi-ap-backhaul-wps"
+ backhaul_passphrase = "87654321"
+ params = {"ssid": "multi-ap-wps-fronthaul", "multi_ap": "2",
+ "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
+ "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase}
+ params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, passphrase=backhaul_passphrase)
+ params_backhaul.update({"multi_ap": "1"})
+ hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul)
+
+ run_multi_ap_wps(dev, apdev, params, hapd_backhaul.own_addr())
+
+def test_multi_ap_wps_fail_non_multi_ap(dev, apdev):
+ """Multi-AP WPS on non-WPS AP fails"""
+
+ params = hostapd.wpa2_params(ssid="non-multi-ap-wps", passphrase="12345678")
+ params.update({"wps_state": "2", "eap_server": "1"})
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("WPS_PBC")
+ if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+
+ dev[0].request("WPS_PBC multi_ap=1")
+ # Since we will fail to associate and WPS doesn't even get started, there
+ # isn't much we can do except wait for timeout. For PBC, it is not possible
+ # to change the timeout from 2 minutes. Instead of waiting for the timeout,
+ # just check that WPS doesn't finish within reasonable time.
+ ev = dev[0].wait_event(["WPS-SUCCESS", "WPS-FAIL"], timeout=20)
+ if ev:
+ raise Exception("WPS operation completed: " + ev)
+ dev[0].request("WPS_CANCEL")

@ -92,7 +92,7 @@
__func__, driver, drv_priv);
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1743,12 +1743,13 @@ ieee802_11_set_radius_info(struct hostap
@@ -1759,12 +1759,13 @@ ieee802_11_set_radius_info(struct hostap
static void handle_auth(struct hostapd_data *hapd,
@ -108,7 +108,7 @@
u16 fc;
const u8 *challenge = NULL;
u32 session_timeout, acct_interim_interval;
@@ -1759,6 +1760,11 @@ static void handle_auth(struct hostapd_d
@@ -1775,6 +1776,11 @@ static void handle_auth(struct hostapd_d
char *identity = NULL;
char *radius_cui = NULL;
u16 seq_ctrl;
@ -120,7 +120,7 @@
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
@@ -1919,6 +1925,13 @@ static void handle_auth(struct hostapd_d
@@ -1935,6 +1941,13 @@ static void handle_auth(struct hostapd_d
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@ -134,7 +134,7 @@
if (res == HOSTAPD_ACL_PENDING)
return;
@@ -3210,12 +3223,12 @@ void fils_hlp_timeout(void *eloop_ctx, v
@@ -3287,12 +3300,12 @@ void fils_hlp_timeout(void *eloop_ctx, v
static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
@ -149,7 +149,7 @@
struct sta_info *sta;
u8 *tmp = NULL;
struct hostapd_sta_wpa_psk_short *psk = NULL;
@@ -3224,6 +3237,11 @@ static void handle_assoc(struct hostapd_
@@ -3301,6 +3314,11 @@ static void handle_assoc(struct hostapd_
#ifdef CONFIG_FILS
int delay_assoc = 0;
#endif /* CONFIG_FILS */
@ -161,7 +161,7 @@
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
@@ -3395,6 +3413,14 @@ static void handle_assoc(struct hostapd_
@@ -3472,6 +3490,14 @@ static void handle_assoc(struct hostapd_
}
#endif /* CONFIG_MBO */
@ -176,7 +176,7 @@
/*
* sta->capability is used in check_assoc_ies() for RRM enabled
* capability element.
@@ -3608,6 +3634,7 @@ static void handle_disassoc(struct hosta
@@ -3685,6 +3711,7 @@ static void handle_disassoc(struct hosta
wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d",
MAC2STR(mgmt->sa),
le_to_host16(mgmt->u.disassoc.reason_code));
@ -184,7 +184,7 @@
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
@@ -3673,6 +3700,8 @@ static void handle_deauth(struct hostapd
@@ -3750,6 +3777,8 @@ static void handle_deauth(struct hostapd
" reason_code=%d",
MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
@ -193,7 +193,7 @@
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying "
@@ -4000,7 +4029,7 @@ int ieee802_11_mgmt(struct hostapd_data
@@ -4077,7 +4106,7 @@ int ieee802_11_mgmt(struct hostapd_data
if (stype == WLAN_FC_STYPE_PROBE_REQ) {
@ -202,7 +202,7 @@
return 1;
}
@@ -4020,17 +4049,17 @@ int ieee802_11_mgmt(struct hostapd_data
@@ -4097,17 +4126,17 @@ int ieee802_11_mgmt(struct hostapd_data
switch (stype) {
case WLAN_FC_STYPE_AUTH:
wpa_printf(MSG_DEBUG, "mgmt::auth");
@ -368,7 +368,7 @@
CFLAGS += -DCONFIG_WNM_AP
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -6062,6 +6062,8 @@ struct wpa_supplicant * wpa_supplicant_a
@@ -6080,6 +6080,8 @@ struct wpa_supplicant * wpa_supplicant_a
}
#endif /* CONFIG_P2P */
@ -377,7 +377,7 @@
return wpa_s;
}
@@ -6088,6 +6090,8 @@ int wpa_supplicant_remove_iface(struct w
@@ -6106,6 +6108,8 @@ int wpa_supplicant_remove_iface(struct w
struct wpa_supplicant *parent = wpa_s->parent;
#endif /* CONFIG_MESH */

@ -96,6 +96,15 @@ wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
}
#ifdef CONFIG_WPS
enum {
WPS_START_MULTI_AP,
__WPS_START_MAX
};
static const struct blobmsg_policy wps_start_policy[] = {
[WPS_START_MULTI_AP] = { "multi_ap", BLOBMSG_TYPE_BOOL },
};
static int
wpas_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
@ -103,8 +112,15 @@ wpas_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
{
int rc;
struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
struct blob_attr *tb[__WPS_START_MAX], *cur;
int multi_ap = 0;
blobmsg_parse(wps_start_policy, __WPS_START_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
if (tb[WPS_START_MULTI_AP])
multi_ap = blobmsg_get_bool(tb[WPS_START_MULTI_AP]);
rc = wpas_wps_start_pbc(wpa_s, NULL, 0);
rc = wpas_wps_start_pbc(wpa_s, NULL, 0, multi_ap);
if (rc != 0)
return UBUS_STATUS_NOT_SUPPORTED;

Loading…
Cancel
Save