All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] ap: Write extra frame IEs from the user
@ 2021-03-11 13:07 Andrew Zaborowski
  2021-03-11 13:07 ` [PATCH 2/4] ap: Handle most WSC IEs using ap_write_extra_ies Andrew Zaborowski
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Andrew Zaborowski @ 2021-03-11 13:07 UTC (permalink / raw)
  To: iwd

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

Add an API for the ap.h users to add extra IEs to outgoing management
frames: beacons, etc.
---
 src/ap.c | 109 +++++++++++++++++++++++++++++++++++++++++++++----------
 src/ap.h |  23 ++++++++++++
 2 files changed, 112 insertions(+), 20 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index eeab3408..1ee17e89 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -466,11 +466,48 @@ static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
 	rsn->group_cipher = ap->group_cipher;
 }
 
+static size_t ap_get_extra_ies_len(struct ap_state *ap,
+					enum mpdu_management_subtype type,
+					const struct mmpdu_header *client_frame,
+					size_t client_frame_len)
+{
+	size_t len = 0;
+
+	if (ap->ops->get_extra_ies_len)
+		len += ap->ops->get_extra_ies_len(type, client_frame,
+							client_frame_len,
+							ap->user_data);
+
+	return len;
+}
+
+static size_t ap_write_extra_ies(struct ap_state *ap,
+					enum mpdu_management_subtype type,
+					const struct mmpdu_header *client_frame,
+					size_t client_frame_len,
+					uint8_t *out_buf)
+{
+	size_t len = 0;
+
+	if (ap->ops->write_extra_ies)
+		len += ap->ops->write_extra_ies(type,
+						client_frame, client_frame_len,
+						out_buf + len, ap->user_data);
+
+	return len;
+}
+
 /*
  * Build a Beacon frame or a Probe Response frame's header and body until
  * the TIM IE.  Except for the optional TIM IE which is inserted by the
  * kernel when needed, our contents for both frames are the same.
  * See Beacon format in 8.3.3.2 and Probe Response format in 8.3.3.10.
+ *
+ * 802.11-2016, Section 9.4.2.1:
+ * "The frame body components specified for many management subtypes result
+ * in elements ordered by ascending values of the Element ID field and then
+ * the Element ID Extension field (when present), with the exception of the
+ * MIC Management element (9.4.2.55)."
  */
 static size_t ap_build_beacon_pr_head(struct ap_state *ap,
 					enum mpdu_management_subtype stype,
@@ -536,8 +573,10 @@ 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, bool pr,
-					uint8_t *out_buf)
+static size_t ap_build_beacon_pr_tail(struct ap_state *ap,
+					enum mpdu_management_subtype stype,
+					const struct mmpdu_header *req,
+					size_t req_len, uint8_t *out_buf)
 {
 	size_t len;
 	struct ie_rsn_info rsn;
@@ -555,7 +594,7 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap, bool pr,
 	len = 2 + out_buf[1];
 
 	/* WSC IE */
-	if (pr) {
+	if (stype == MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE) {
 		struct wsc_probe_response wsc_pr = {};
 
 		wsc_pr.version2 = true;
@@ -633,6 +672,7 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap, bool pr,
 	len += wsc_ie_size;
 	l_free(wsc_ie);
 
+	len += ap_write_extra_ies(ap, stype, req, req_len, out_buf + len);
 	return len;
 }
 
@@ -647,7 +687,11 @@ static void ap_set_beacon_cb(struct l_genl_msg *msg, void *user_data)
 void ap_update_beacon(struct ap_state *ap)
 {
 	struct l_genl_msg *cmd;
-	uint8_t head[256], tail[256];
+	uint8_t head[256];
+	L_AUTO_FREE_VAR(uint8_t *, tail) =
+		l_malloc(256 + ap_get_extra_ies_len(ap,
+						MPDU_MANAGEMENT_SUBTYPE_BEACON,
+						NULL, 0));
 	size_t head_len, tail_len;
 	uint64_t wdev_id = netdev_get_wdev_id(ap->netdev);
 	static const uint8_t bcast_addr[6] = {
@@ -659,7 +703,8 @@ void ap_update_beacon(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, false, tail);
+	tail_len = ap_build_beacon_pr_tail(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
+						NULL, 0, tail);
 	if (L_WARN_ON(!head_len || !tail_len))
 		return;
 
@@ -1197,10 +1242,15 @@ static void ap_fail_assoc_resp_cb(int err, void *user_data)
 static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 				const uint8_t *dest,
 				enum mmpdu_reason_code status_code,
-				bool reassoc, frame_xchg_cb_t callback)
+				bool reassoc, const struct mmpdu_header *req,
+				size_t req_len, frame_xchg_cb_t callback)
 {
 	const uint8_t *addr = netdev_get_address(ap->netdev);
-	uint8_t mpdu_buf[128];
+	enum mpdu_management_subtype stype = reassoc ?
+		MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE :
+		MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE;
+	L_AUTO_FREE_VAR(uint8_t *, mpdu_buf) =
+		l_malloc(128 + ap_get_extra_ies_len(ap, stype, req, req_len));
 	struct mmpdu_header *mpdu = (void *) mpdu_buf;
 	struct mmpdu_association_response *resp;
 	size_t ies_len = 0;
@@ -1212,9 +1262,7 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 	/* Header */
 	mpdu->fc.protocol_version = 0;
 	mpdu->fc.type = MPDU_TYPE_MANAGEMENT;
-	mpdu->fc.subtype = reassoc ?
-		MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE :
-		MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE;
+	mpdu->fc.subtype = stype;
 	memcpy(mpdu->address_1, dest, 6);	/* DA */
 	memcpy(mpdu->address_2, addr, 6);	/* SA */
 	memcpy(mpdu->address_3, addr, 6);	/* BSSID */
@@ -1276,6 +1324,9 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 		l_free(wsc_ie);
 	}
 
+	ies_len += ap_write_extra_ies(ap, stype, req, req_len,
+					resp->ies + ies_len);
+
 send_frame:
 	return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
 					callback, sta);
@@ -1351,8 +1402,9 @@ static int ap_parse_supported_rates(struct ie_tlv_iter *iter,
  */
 static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 				const struct mmpdu_field_capability *capability,
-				uint16_t listen_interval, const uint8_t *ies,
-				size_t ies_len)
+				uint16_t listen_interval,
+				const uint8_t *ies, size_t ies_len,
+				const struct mmpdu_header *req)
 {
 	struct ap_state *ap = sta->ap;
 	const char *ssid = NULL;
@@ -1541,6 +1593,8 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 	}
 
 	sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, 0, reassoc,
+						req, (void *) ies + ies_len -
+						(void *) req,
 						ap_success_assoc_resp_cb);
 	if (!sta->assoc_resp_cmd_id)
 		l_error("Sending success (Re)Association Response failed");
@@ -1572,6 +1626,7 @@ bad_frame:
 	l_free(wsc_data);
 
 	if (!ap_assoc_resp(ap, sta, sta->addr, err, reassoc,
+				req, (void *) ies + ies_len - (void *) req,
 				ap_fail_assoc_resp_cb))
 		l_error("Sending error (Re)Association Response failed");
 }
@@ -1596,7 +1651,8 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 	if (!sta) {
 		if (!ap_assoc_resp(ap, NULL, from,
 				MMPDU_REASON_CODE_STA_REQ_ASSOC_WITHOUT_AUTH,
-				false, ap_fail_assoc_resp_cb))
+				false, hdr, body + body_len - (void *) hdr,
+				ap_fail_assoc_resp_cb))
 			l_error("Sending error Association Response failed");
 
 		return;
@@ -1604,7 +1660,7 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 
 	ap_assoc_reassoc(sta, false, &req->capability,
 				L_LE16_TO_CPU(req->listen_interval),
-				req->ies, body_len - sizeof(*req));
+				req->ies, body_len - sizeof(*req), hdr);
 }
 
 /* 802.11-2016 9.3.3.8 */
@@ -1638,11 +1694,13 @@ static void ap_reassoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 
 	ap_assoc_reassoc(sta, true, &req->capability,
 				L_LE16_TO_CPU(req->listen_interval),
-				req->ies, body_len - sizeof(*req));
+				req->ies, body_len - sizeof(*req), hdr);
 	return;
 
 bad_frame:
-	if (!ap_assoc_resp(ap, NULL, from, err, true, ap_fail_assoc_resp_cb))
+	if (!ap_assoc_resp(ap, NULL, from, err, true,
+				hdr, body + body_len - (void *) hdr,
+				ap_fail_assoc_resp_cb))
 		l_error("Sending error Reassociation Response failed");
 }
 
@@ -1785,7 +1843,10 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
 	struct ie_tlv_iter iter;
 	const uint8_t *bssid = netdev_get_address(ap->netdev);
 	bool match = false;
-	uint8_t resp[512];
+	L_AUTO_FREE_VAR(uint8_t *, resp) =
+		l_malloc(512 + ap_get_extra_ies_len(ap,
+				MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE, hdr,
+				body + body_len - (void *) hdr));
 	uint8_t *wsc_data;
 	ssize_t wsc_data_len;
 
@@ -1875,7 +1936,10 @@ 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, true, resp + len);
+	len += ap_build_beacon_pr_tail(ap,
+					MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
+					hdr, body + body_len - (void *) hdr,
+					resp + len);
 
 	ap_send_mgmt_frame(ap, (struct mmpdu_header *) resp, len,
 				ap_probe_resp_cb, NULL);
@@ -2108,7 +2172,11 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
 {
 	struct l_genl_msg *cmd;
 
-	uint8_t head[256], tail[256];
+	uint8_t head[256];
+	L_AUTO_FREE_VAR(uint8_t *, tail) =
+		l_malloc(256 + ap_get_extra_ies_len(ap,
+						MPDU_MANAGEMENT_SUBTYPE_BEACON,
+						NULL, 0));
 	size_t head_len, tail_len;
 
 	uint32_t dtim_period = 3;
@@ -2138,7 +2206,8 @@ 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, false, tail);
+	tail_len = ap_build_beacon_pr_tail(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
+						NULL, 0, tail);
 
 	if (!head_len || !tail_len)
 		return NULL;
diff --git a/src/ap.h b/src/ap.h
index 0bdbf0bb..729a6648 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -22,6 +22,7 @@
 
 struct ap_state;
 struct iovec;
+enum mpdu_management_subtype;
 
 enum ap_event_type {
 	AP_EVENT_START_FAILED,
@@ -75,6 +76,28 @@ struct ap_config {
 struct ap_ops {
 	void (*handle_event)(enum ap_event_type type, const void *event_data,
 				void *user_data);
+	/*
+	 * If .write_extra_ies is provided, this callback must return an upper
+	 * bound on the buffer space needed for the extra IEs to be sent in
+	 * the frame of given type and, if it's not a beacon frame, in
+	 * response to a given client frame.
+	 */
+	size_t (*get_extra_ies_len)(enum mpdu_management_subtype type,
+					const struct mmpdu_header *client_frame,
+					size_t client_frame_len,
+					void *user_data);
+	/*
+	 * If not null, writes extra IEs to be added to the outgoing frame of
+	 * given type and, if it's not a beacon frame, in reponse to a given
+	 * client frame.  May also react to the extra IEs in that frame.
+	 * Returns the number of bytes written which must be less than or
+	 * equal to the number returned by .get_extra_ies_len when called
+	 * with the same parameters.
+	 */
+	size_t (*write_extra_ies)(enum mpdu_management_subtype type,
+					const struct mmpdu_header *client_frame,
+					size_t client_frame_len,
+					uint8_t *out_buf, void *user_data);
 };
 
 void ap_config_free(struct ap_config *config);
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread
* [PATCH 1/4] ap: Pass vendor IEs between frames and the user
@ 2021-02-05 23:46 Andrew Zaborowski
  2021-02-05 23:46 ` [PATCH 3/4] p2p: Parse P2P IEs and WFD IEs in Association Requests Andrew Zaborowski
  0 siblings, 1 reply; 6+ messages in thread
From: Andrew Zaborowski @ 2021-02-05 23:46 UTC (permalink / raw)
  To: iwd

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

Add API for the ap.h users to add extra vendor IEs to outgoing
management frames (beacons, etc.).  Pass the string IEs from the
incoming STA association frames to the user in the AP event data.

I drop ap_event_station_added_data.rsn_ie because that probably
wasn't going to ever be useful and the RSN IE is included in the
assoc_ies array in any case.
---
 src/ap.c | 233 ++++++++++++++++++++++++++++++++++++++++---------------
 src/ap.h |  15 +++-
 2 files changed, 186 insertions(+), 62 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index 8ea26a0d..dbc624c2 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -75,6 +75,8 @@ struct ap_state {
 	uint16_t wsc_dpid;
 	uint8_t wsc_uuid_r[16];
 
+	struct l_queue *vendor_ie_writers;
+
 	uint16_t last_aid;
 	struct l_queue *sta_states;
 
@@ -99,6 +101,8 @@ struct sta_state {
 	struct l_uintset *rates;
 	uint32_t assoc_resp_cmd_id;
 	struct ap_state *ap;
+	uint8_t *assoc_ies;
+	size_t assoc_ies_len;
 	uint8_t *assoc_rsne;
 	struct eapol_sm *sm;
 	struct handshake_state *hs;
@@ -127,6 +131,12 @@ struct ap_ip_pool {
 	struct l_uintset *used;
 };
 
+struct ap_vendor_ie_writer {
+	ap_get_vendor_ie_len get_len_func;
+	ap_write_vendor_ie write_func;
+	void *user_data;
+};
+
 struct ap_ip_pool pool;
 static uint32_t netdev_watch;
 struct l_netlink *rtnl;
@@ -279,7 +289,7 @@ static void ap_sta_free(void *data)
 	struct ap_state *ap = sta->ap;
 
 	l_uintset_free(sta->rates);
-	l_free(sta->assoc_rsne);
+	l_free(sta->assoc_ies);
 
 	if (sta->assoc_resp_cmd_id)
 		l_genl_family_cancel(ap->nl80211, sta->assoc_resp_cmd_id);
@@ -411,7 +421,8 @@ static void ap_new_rsna(struct sta_state *sta)
 	if (ap->ops->handle_event) {
 		struct ap_event_station_added_data event_data = {};
 		event_data.mac = sta->addr;
-		event_data.rsn_ie = sta->assoc_rsne;
+		event_data.assoc_ies = sta->assoc_ies;
+		event_data.assoc_ies_len = sta->assoc_ies_len;
 		ap->ops->handle_event(AP_EVENT_STATION_ADDED, &event_data,
 					ap->user_data);
 	}
@@ -463,11 +474,51 @@ static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
 	rsn->group_cipher = ap->group_cipher;
 }
 
+static size_t ap_get_vendor_ies_len(struct ap_state *ap,
+					enum mpdu_management_subtype type)
+{
+	const struct l_queue_entry *entry;
+	size_t len = 0;
+
+	for (entry = l_queue_get_entries(ap->sta_states); entry;
+			entry = entry->next) {
+		struct ap_vendor_ie_writer *writer = entry->data;
+
+		len += writer->get_len_func(type, writer->user_data);
+	}
+
+	return len;
+}
+
+static size_t ap_write_vendor_ies(struct ap_state *ap,
+					enum mpdu_management_subtype type,
+					uint8_t *out_buf)
+{
+	const struct l_queue_entry *entry;
+	size_t len = 0;
+
+	for (entry = l_queue_get_entries(ap->sta_states); entry;
+			entry = entry->next) {
+		struct ap_vendor_ie_writer *writer = entry->data;
+
+		len += writer->write_func(type, out_buf + len,
+						writer->user_data);
+	}
+
+	return len;
+}
+
 /*
  * Build a Beacon frame or a Probe Response frame's header and body until
  * the TIM IE.  Except for the optional TIM IE which is inserted by the
  * kernel when needed, our contents for both frames are the same.
  * See Beacon format in 8.3.3.2 and Probe Response format in 8.3.3.10.
+ *
+ * 802.11-2016, Section 9.4.2.1:
+ * "The frame body components specified for many management subtypes result
+ * in elements ordered by ascending values of the Element ID field and then
+ * the Element ID Extension field (when present), with the exception of the
+ * MIC Management element (9.4.2.55)."
  */
 static size_t ap_build_beacon_pr_head(struct ap_state *ap,
 					enum mpdu_management_subtype stype,
@@ -533,15 +584,12 @@ 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, bool pr,
+static size_t ap_build_beacon_pr_tail(struct ap_state *ap,
+					enum mpdu_management_subtype stype,
 					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 */
 
@@ -551,8 +599,71 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap, bool pr,
 		return 0;
 	len = 2 + out_buf[1];
 
+	len += ap_write_vendor_ies(ap, stype, out_buf + len);
+	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];
+	uint8_t tail[256 + ap_get_vendor_ies_len(ap,
+					MPDU_MANAGEMENT_SUBTYPE_BEACON)];
+	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, MPDU_MANAGEMENT_SUBTYPE_BEACON,
+						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 size_t ap_get_wsc_ie_len(enum mpdu_management_subtype type,
+				void *user_data)
+{
+	return 256;
+}
+
+static size_t ap_write_wsc_ie(enum mpdu_management_subtype type,
+				uint8_t *out_buf, void *user_data)
+{
+	struct ap_state *ap = user_data;
+	uint8_t *wsc_data;
+	size_t wsc_data_size;
+	uint8_t *wsc_ie;
+	size_t wsc_ie_size;
+	size_t len = 0;
+
 	/* WSC IE */
-	if (pr) {
+	if (type == MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE) {
 		struct wsc_probe_response wsc_pr = {};
 
 		wsc_pr.version2 = true;
@@ -633,46 +744,6 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap, bool pr,
 	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)
@@ -1194,7 +1265,10 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 				bool reassoc, frame_xchg_cb_t callback)
 {
 	const uint8_t *addr = netdev_get_address(ap->netdev);
-	uint8_t mpdu_buf[128];
+	enum mpdu_management_subtype stype = reassoc ?
+		MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE :
+		MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE;
+	uint8_t mpdu_buf[128 + ap_get_vendor_ies_len(ap, stype)];
 	struct mmpdu_header *mpdu = (void *) mpdu_buf;
 	struct mmpdu_association_response *resp;
 	size_t ies_len = 0;
@@ -1206,9 +1280,7 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 	/* Header */
 	mpdu->fc.protocol_version = 0;
 	mpdu->fc.type = MPDU_TYPE_MANAGEMENT;
-	mpdu->fc.subtype = reassoc ?
-		MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE :
-		MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE;
+	mpdu->fc.subtype = stype;
 	memcpy(mpdu->address_1, dest, 6);	/* DA */
 	memcpy(mpdu->address_2, addr, 6);	/* SA */
 	memcpy(mpdu->address_3, addr, 6);	/* BSSID */
@@ -1270,6 +1342,8 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 		l_free(wsc_ie);
 	}
 
+	ies_len += ap_write_vendor_ies(ap, stype, resp->ies + ies_len);
+
 send_frame:
 	return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
 					callback, sta);
@@ -1471,6 +1545,8 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 		sta->wsc_v2 = wsc_req.version2;
 
 		event_data.mac = sta->addr;
+		event_data.assoc_ies = ies;
+		event_data.assoc_ies_len = ies_len;
 		ap->ops->handle_event(AP_EVENT_REGISTRATION_START, &event_data,
 					ap->user_data);
 
@@ -1521,13 +1597,16 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 
 	sta->rates = rates;
 
-	if (sta->assoc_rsne)
-		l_free(sta->assoc_rsne);
+	l_free(sta->assoc_ies);
 
-	if (rsn)
-		sta->assoc_rsne = l_memdup(rsn, rsn[1] + 2);
-	else
+	if (rsn) {
+		sta->assoc_ies = l_memdup(ies, ies_len);
+		sta->assoc_ies_len = ies_len;
+		sta->assoc_rsne = sta->assoc_ies + (rsn - ies);
+	} else {
+		sta->assoc_ies = NULL;
 		sta->assoc_rsne = NULL;
+	}
 
 	sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, 0, reassoc,
 						ap_success_assoc_resp_cb);
@@ -1774,7 +1853,8 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
 	struct ie_tlv_iter iter;
 	const uint8_t *bssid = netdev_get_address(ap->netdev);
 	bool match = false;
-	uint8_t resp[512];
+	uint8_t resp[512 + ap_get_vendor_ies_len(ap,
+				MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE)];
 	uint8_t *wsc_data;
 	ssize_t wsc_data_len;
 
@@ -1864,7 +1944,9 @@ 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, true, resp + len);
+	len += ap_build_beacon_pr_tail(ap,
+					MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
+					resp + len);
 
 	ap_send_mgmt_frame(ap, (struct mmpdu_header *) resp, len,
 				ap_probe_resp_cb, NULL);
@@ -2102,7 +2184,9 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
 {
 	struct l_genl_msg *cmd;
 
-	uint8_t head[256], tail[256];
+	uint8_t head[256];
+	uint8_t tail[256 + ap_get_vendor_ies_len(ap,
+					MPDU_MANAGEMENT_SUBTYPE_BEACON)];
 	size_t head_len, tail_len;
 
 	uint32_t dtim_period = 3;
@@ -2132,7 +2216,8 @@ 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, false, tail);
+	tail_len = ap_build_beacon_pr_tail(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
+						tail);
 
 	if (!head_len || !tail_len)
 		return NULL;
@@ -2697,6 +2782,7 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 	}
 
 	wsc_uuid_from_addr(netdev_get_address(netdev), ap->wsc_uuid_r);
+	ap_add_vendor_ie_writer(ap, ap_get_wsc_ie_len, ap_write_wsc_ie, ap);
 
 	if (config->passphrase[0] &&
 			crypto_psk_from_passphrase(config->passphrase,
@@ -2876,9 +2962,34 @@ void ap_free(struct ap_state *ap)
 	l_genl_family_free(ap->nl80211);
 	if (ap->server)
 		l_dhcp_server_destroy(ap->server);
+
+	l_queue_destroy(ap->vendor_ie_writers, l_free);
 	l_free(ap);
 }
 
+void ap_add_vendor_ie_writer(struct ap_state *ap,
+				ap_get_vendor_ie_len get_len_func,
+				ap_write_vendor_ie write_func,
+				void *user_data)
+{
+	struct ap_vendor_ie_writer *writer =
+		l_new(struct ap_vendor_ie_writer, 1);
+
+	writer->get_len_func = get_len_func;
+	writer->write_func = write_func;
+	writer->user_data = user_data;
+
+	if (!ap->vendor_ie_writers)
+		ap->vendor_ie_writers = l_queue_new();
+
+	/*
+	 * It doesn't look like vendor IEs need to be ordered in a specific
+	 * way so just append at the end, but if needed we can take an OUI
+	 * parameter and switch to inserting at the right place in the queue.
+	 */
+	l_queue_push_tail(ap->vendor_ie_writers, writer);
+}
+
 bool ap_station_disconnect(struct ap_state *ap, const uint8_t *mac,
 				enum mmpdu_reason_code reason)
 {
diff --git a/src/ap.h b/src/ap.h
index dc57a0bb..eb4f96df 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -22,6 +22,7 @@
 
 struct ap_state;
 struct iovec;
+enum mpdu_management_subtype;
 
 enum ap_event_type {
 	AP_EVENT_START_FAILED,
@@ -36,7 +37,8 @@ enum ap_event_type {
 
 struct ap_event_station_added_data {
 	const uint8_t *mac;
-	const uint8_t *rsn_ie;
+	const uint8_t *assoc_ies;
+	size_t assoc_ies_len;
 };
 
 struct ap_event_station_removed_data {
@@ -46,6 +48,8 @@ struct ap_event_station_removed_data {
 
 struct ap_event_registration_start_data {
 	const uint8_t *mac;
+	const uint8_t *assoc_ies;
+	size_t assoc_ies_len;
 };
 
 struct ap_event_registration_success_data {
@@ -53,6 +57,10 @@ struct ap_event_registration_success_data {
 };
 
 typedef void (*ap_stopped_func_t)(void *user_data);
+typedef size_t (*ap_get_vendor_ie_len)(enum mpdu_management_subtype type,
+					void *user_data);
+typedef size_t (*ap_write_vendor_ie)(enum mpdu_management_subtype type,
+					uint8_t *out_buf, void *user_data);
 
 struct ap_config {
 	char *ssid;
@@ -83,6 +91,11 @@ void ap_shutdown(struct ap_state *ap, ap_stopped_func_t stopped_func,
 			void *user_data);
 void ap_free(struct ap_state *ap);
 
+void ap_add_vendor_ie_writer(struct ap_state *ap,
+				ap_get_vendor_ie_len get_len_func,
+				ap_write_vendor_ie write_func,
+				void *user_data);
+
 bool ap_station_disconnect(struct ap_state *ap, const uint8_t *mac,
 				enum mmpdu_reason_code reason);
 
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-03-12  3:51 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-11 13:07 [PATCH 1/4] ap: Write extra frame IEs from the user Andrew Zaborowski
2021-03-11 13:07 ` [PATCH 2/4] ap: Handle most WSC IEs using ap_write_extra_ies Andrew Zaborowski
2021-03-11 13:07 ` [PATCH 3/4] p2p: Parse P2P IEs and WFD IEs in Association Requests Andrew Zaborowski
2021-03-11 13:07 ` [PATCH 4/4] p2p: Build P2P and WFD IEs for group's management frames Andrew Zaborowski
2021-03-12  3:51 ` [PATCH 1/4] ap: Write extra frame IEs from the user Denis Kenzior
  -- strict thread matches above, loose matches on Subject: below --
2021-02-05 23:46 [PATCH 1/4] ap: Pass vendor IEs between frames and " Andrew Zaborowski
2021-02-05 23:46 ` [PATCH 3/4] p2p: Parse P2P IEs and WFD IEs in Association Requests Andrew Zaborowski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.