Wireless Daemon for Linux
 help / color / mirror / Atom feed
From: Andrew Zaborowski <andrew.zaborowski@intel.com>
To: iwd@lists.01.org
Subject: [PATCH 5/8] ap: Push Button mode API and beacon changes
Date: Fri, 28 Aug 2020 14:46:46 +0200	[thread overview]
Message-ID: <20200828124649.78677-5-andrew.zaborowski@intel.com> (raw)
In-Reply-To: <20200828124649.78677-1-andrew.zaborowski@intel.com>

[-- Attachment #1: Type: text/plain, Size: 10630 bytes --]

This adds the API for putting the AP in Push Button mode, which we'll
need to P2P GO side but may be useful on its own too.  A WSC IE is added
to our beacons and probe responses indicating whether the PBC mode is
active.
---
 src/ap.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/ap.h |  15 ++++
 2 files changed, 217 insertions(+), 3 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index fa7d030a..ce776553 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -45,6 +45,7 @@
 #include "src/dbus.h"
 #include "src/nl80211util.h"
 #include "src/frame-xchg.h"
+#include "src/wscutil.h"
 #include "src/ap.h"
 
 struct ap_state {
@@ -64,6 +65,9 @@ struct ap_state {
 	uint32_t mlme_watch;
 	uint8_t gtk[CRYPTO_MAX_GTK_LEN];
 	uint8_t gtk_index;
+	struct l_timeout *wsc_pbc_timeout;
+	uint16_t wsc_dpid;
+	uint8_t wsc_uuid_r[16];
 
 	uint16_t last_aid;
 	struct l_queue *sta_states;
@@ -103,6 +107,7 @@ void ap_config_free(struct ap_config *config)
 	}
 
 	l_free(config->authorized_macs);
+	l_free(config->wsc_name);
 	l_free(config);
 }
 
@@ -358,10 +363,15 @@ static size_t ap_build_beacon_pr_head(struct ap_state *ap,
 }
 
 /* Beacon / Probe Response frame portion after the TIM IE */
-static size_t ap_build_beacon_pr_tail(struct ap_state *ap, uint8_t *out_buf)
+static size_t ap_build_beacon_pr_tail(struct ap_state *ap, bool pr,
+					uint8_t *out_buf)
 {
 	size_t len;
 	struct ie_rsn_info rsn;
+	uint8_t *wsc_data;
+	size_t wsc_data_size;
+	uint8_t *wsc_ie;
+	size_t wsc_ie_size;
 
 	/* TODO: Country IE between TIM IE and RSNE */
 
@@ -371,9 +381,140 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap, uint8_t *out_buf)
 		return 0;
 	len = 2 + out_buf[1];
 
+	/* WSC IE */
+	if (pr) {
+		struct wsc_probe_response wsc_pr = {};
+
+		wsc_pr.version2 = true;
+		wsc_pr.state = WSC_STATE_CONFIGURED;
+
+		if (ap->wsc_pbc_timeout) {
+			wsc_pr.selected_registrar = true;
+			wsc_pr.device_password_id = ap->wsc_dpid;
+			wsc_pr.selected_reg_config_methods =
+				WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
+		}
+
+		wsc_pr.response_type = WSC_RESPONSE_TYPE_AP;
+		memcpy(wsc_pr.uuid_e, ap->wsc_uuid_r, sizeof(wsc_pr.uuid_e));
+		wsc_pr.primary_device_type =
+			ap->config->wsc_primary_device_type;
+
+		if (ap->config->wsc_name)
+			l_strlcpy(wsc_pr.device_name, ap->config->wsc_name,
+					sizeof(wsc_pr.device_name));
+
+		wsc_pr.config_methods =
+			WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
+
+		if (ap->config->authorized_macs_num) {
+			size_t len;
+
+			len = ap->config->authorized_macs_num * 6;
+			if (len > sizeof(wsc_pr.authorized_macs))
+				len = sizeof(wsc_pr.authorized_macs);
+
+			memcpy(wsc_pr.authorized_macs,
+				ap->config->authorized_macs, len);
+		}
+
+		wsc_data = wsc_build_probe_response(&wsc_pr, &wsc_data_size);
+	} else {
+		struct wsc_beacon wsc_beacon = {};
+
+		wsc_beacon.version2 = true;
+		wsc_beacon.state = WSC_STATE_CONFIGURED;
+
+		if (ap->wsc_pbc_timeout) {
+			wsc_beacon.selected_registrar = true;
+			wsc_beacon.device_password_id = ap->wsc_dpid;
+			wsc_beacon.selected_reg_config_methods =
+				WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
+		}
+
+		if (ap->config->authorized_macs_num) {
+			size_t len;
+
+			len = ap->config->authorized_macs_num * 6;
+			if (len > sizeof(wsc_beacon.authorized_macs))
+				len = sizeof(wsc_beacon.authorized_macs);
+
+			memcpy(wsc_beacon.authorized_macs,
+				ap->config->authorized_macs, len);
+		}
+
+		wsc_data = wsc_build_beacon(&wsc_beacon, &wsc_data_size);
+	}
+
+	if (!wsc_data)
+		return 0;
+
+	wsc_ie = ie_tlv_encapsulate_wsc_payload(wsc_data, wsc_data_size,
+						&wsc_ie_size);
+	l_free(wsc_data);
+
+	if (!wsc_ie)
+		return 0;
+
+	memcpy(out_buf + len, wsc_ie, wsc_ie_size);
+	len += wsc_ie_size;
+	l_free(wsc_ie);
+
 	return len;
 }
 
+static void ap_set_beacon_cb(struct l_genl_msg *msg, void *user_data)
+{
+	int error = l_genl_msg_get_error(msg);
+
+	if (error < 0)
+		l_error("SET_BEACON failed: %s (%i)", strerror(-error), -error);
+}
+
+static void ap_update_beacon(struct ap_state *ap)
+{
+	struct l_genl_msg *cmd;
+	uint8_t head[256], tail[256];
+	size_t head_len, tail_len;
+	uint64_t wdev_id = netdev_get_wdev_id(ap->netdev);
+	static const uint8_t bcast_addr[6] = {
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+	};
+
+	head_len = ap_build_beacon_pr_head(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
+						bcast_addr, head, sizeof(head));
+	tail_len = ap_build_beacon_pr_tail(ap, false, tail);
+	if (L_WARN_ON(!head_len || !tail_len))
+		return;
+
+	cmd = l_genl_msg_new_sized(NL80211_CMD_SET_BEACON,
+					32 + head_len + tail_len);
+	l_genl_msg_append_attr(cmd, NL80211_ATTR_WDEV, 8, &wdev_id);
+	l_genl_msg_append_attr(cmd, NL80211_ATTR_BEACON_HEAD, head_len, head);
+	l_genl_msg_append_attr(cmd, NL80211_ATTR_BEACON_TAIL, tail_len, tail);
+	l_genl_msg_append_attr(cmd, NL80211_ATTR_IE, 0, "");
+	l_genl_msg_append_attr(cmd, NL80211_ATTR_IE_PROBE_RESP, 0, "");
+	l_genl_msg_append_attr(cmd, NL80211_ATTR_IE_ASSOC_RESP, 0, "");
+
+	if (l_genl_family_send(ap->nl80211, cmd, ap_set_beacon_cb, NULL, NULL))
+		return;
+
+	l_genl_msg_unref(cmd);
+	l_error("Issuing SET_BEACON failed");
+}
+
+static void ap_wsc_exit_pbc(struct ap_state *ap)
+{
+	if (!ap->wsc_pbc_timeout)
+		return;
+
+	l_timeout_remove(ap->wsc_pbc_timeout);
+	ap->wsc_dpid = 0;
+	ap_update_beacon(ap);
+
+	ap->event_func(AP_EVENT_PBC_MODE_EXIT, NULL, ap->user_data);
+}
+
 static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
 					const struct mmpdu_header *frame,
 					size_t frame_len, bool wait_ack,
@@ -1069,6 +1210,9 @@ bad_frame:
 		l_error("Sending error Reassociation Response failed");
 }
 
+#define AP_WSC_PBC_MONITOR_TIME	120
+#define AP_WSC_PBC_WALK_TIME	120
+
 static void ap_probe_resp_cb(struct l_genl_msg *msg, void *user_data)
 {
 	if (l_genl_msg_get_error(msg) < 0)
@@ -1167,7 +1311,7 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
 	len = ap_build_beacon_pr_head(ap,
 					MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
 					hdr->address_2, resp, sizeof(resp));
-	len += ap_build_beacon_pr_tail(ap, resp + len);
+	len += ap_build_beacon_pr_tail(ap, true, resp + len);
 
 	ap_send_mgmt_frame(ap, (struct mmpdu_header *) resp, len, false,
 				ap_probe_resp_cb, NULL);
@@ -1397,7 +1541,7 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
 
 	head_len = ap_build_beacon_pr_head(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
 						bcast_addr, head, sizeof(head));
-	tail_len = ap_build_beacon_pr_tail(ap, tail);
+	tail_len = ap_build_beacon_pr_tail(ap, false, tail);
 
 	if (!head_len || !tail_len)
 		return NULL;
@@ -1501,6 +1645,17 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 		/* TODO: Start a Get Survey to decide the channel */
 		config->channel = 6;
 
+	if (!config->wsc_name)
+		config->wsc_name = l_strdup(config->ssid);
+
+	if (!config->wsc_primary_device_type.category) {
+		/* Make ourselves a WFA standard PC by default */
+		config->wsc_primary_device_type.category = 1;
+		memcpy(config->wsc_primary_device_type.oui, wsc_wfa_oui, 3);
+		config->wsc_primary_device_type.oui_type = 0x04;
+		config->wsc_primary_device_type.subcategory = 1;
+	}
+
 	/* TODO: Add all ciphers supported by wiphy */
 	ap->ciphers = wiphy_select_cipher(wiphy, 0xffff);
 	ap->group_cipher = wiphy_select_cipher(wiphy, 0xffff);
@@ -1523,6 +1678,8 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 		l_uintset_put(ap->rates, 22); /* 11 Mbps*/
 	}
 
+	wsc_uuid_from_addr(netdev_get_address(netdev), ap->wsc_uuid_r);
+
 	if (crypto_psk_from_passphrase(config->psk, (uint8_t *) config->ssid,
 					strlen(config->ssid), ap->pmk) < 0)
 		goto error;
@@ -1702,6 +1859,45 @@ bool ap_station_disconnect(struct ap_state *ap, const uint8_t *mac,
 	return true;
 }
 
+static void ap_wsc_pbc_timeout_cb(struct l_timeout *timeout, void *user_data)
+{
+	struct ap_state *ap = user_data;
+
+	l_debug("PBC mode timeout");
+	ap_wsc_exit_pbc(ap);
+}
+
+static void ap_wsc_pbc_timeout_destroy(void *user_data)
+{
+	struct ap_state *ap = user_data;
+
+	ap->wsc_pbc_timeout = NULL;
+}
+
+bool ap_push_button(struct ap_state *ap)
+{
+	if (!ap->started)
+		return false;
+
+	/*
+	 * WSC v2.0.5 Section 11.3: "Multiple presses of the button are
+	 * permitted.  If a PBC button on an Enrollee or Registrar is
+	 * pressed again during Walk Time, the timers for that device are
+	 * restarted at that time [...]"
+	 */
+	if (ap->wsc_pbc_timeout) {
+		l_timeout_modify(ap->wsc_pbc_timeout, AP_WSC_PBC_WALK_TIME);
+		return true;
+	}
+
+	ap->wsc_pbc_timeout = l_timeout_create(AP_WSC_PBC_WALK_TIME,
+						ap_wsc_pbc_timeout_cb, ap,
+						ap_wsc_pbc_timeout_destroy);
+	ap->wsc_dpid = WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON;
+	ap_update_beacon(ap);
+	return true;
+}
+
 struct ap_if_data {
 	struct netdev *netdev;
 	struct ap_state *ap;
@@ -1747,6 +1943,9 @@ static void ap_if_event_func(enum ap_event_type type, const void *event_data,
 
 	case AP_EVENT_STATION_ADDED:
 	case AP_EVENT_STATION_REMOVED:
+	case AP_EVENT_REGISTRATION_START:
+	case AP_EVENT_REGISTRATION_SUCCESS:
+	case AP_EVENT_PBC_MODE_EXIT:
 		/* Ignored */
 		break;
 	}
diff --git a/src/ap.h b/src/ap.h
index 330ae011..cb5238d1 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -29,6 +29,9 @@ enum ap_event_type {
 	AP_EVENT_STOPPING,
 	AP_EVENT_STATION_ADDED,
 	AP_EVENT_STATION_REMOVED,
+	AP_EVENT_REGISTRATION_START,
+	AP_EVENT_REGISTRATION_SUCCESS,
+	AP_EVENT_PBC_MODE_EXIT,
 };
 
 struct ap_event_station_added_data {
@@ -41,6 +44,14 @@ struct ap_event_station_removed_data {
 	enum mmpdu_reason_code reason;
 };
 
+struct ap_event_registration_start_data {
+	const uint8_t *mac;
+};
+
+struct ap_event_registration_success_data {
+	const uint8_t *mac;
+};
+
 typedef void (*ap_event_func_t)(enum ap_event_type type, const void *event_data,
 				void *user_data);
 typedef void (*ap_stopped_func_t)(void *user_data);
@@ -51,6 +62,8 @@ struct ap_config {
 	uint8_t channel;
 	uint8_t *authorized_macs;
 	unsigned int authorized_macs_num;
+	char *wsc_name;
+	struct wsc_primary_device_type wsc_primary_device_type;
 	bool no_cck_rates : 1;
 };
 
@@ -64,3 +77,5 @@ void ap_free(struct ap_state *ap);
 
 bool ap_station_disconnect(struct ap_state *ap, const uint8_t *mac,
 				enum mmpdu_reason_code reason);
+
+bool ap_push_button(struct ap_state *ap);
-- 
2.25.1

  parent reply	other threads:[~2020-08-28 12:46 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 2/8] wscutil: Add wsc_build_beacon Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 3/8] eapol: Handle the use_eapol_start flag on authenticator Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 4/8] ap: Stop ongoing handshake on reassociation Andrew Zaborowski
2020-08-28 12:46 ` Andrew Zaborowski [this message]
2020-08-28 12:46 ` [PATCH 6/8] ap: WSC Probe Request processing logic Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 7/8] ap: Parse WSC PBC association request and build response Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 8/8] ap: Start EAP-WSC authentication with WSC enrollees Andrew Zaborowski
2020-08-28 15:55 ` [PATCH 1/8] wscutil: Use a utility for building authorized_macs Denis Kenzior

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200828124649.78677-5-andrew.zaborowski@intel.com \
    --to=andrew.zaborowski@intel.com \
    --cc=iwd@lists.01.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox