blob: 22e484134d8dc1dcfc73ad2ddbdf9059f43813a9 [file] [log] [blame]
developer683be522023-05-11 14:24:50 +08001From cb7b7459ac276c5713dae22b8fe19365c2895de1 Mon Sep 17 00:00:00 2001
2From: "howard.hsu" <howard-yh.hsu@mediatek.com>
3Date: Wed, 19 Jan 2022 19:18:07 +0800
4Subject: [PATCH 01/28] hostapd: mtk: Add neighbor report and BSS Termination
5 for MBO certification
6
71. Add hostapd_neighbor_count() and hostapd_neighbor_insert_buffer ()
8The first function can count the number of neighbor report in neighbore report
9database. The second can iterate neighbor report database to build up neighbor
10report data.
11
122. Support including neighbor report elements in ANQP response
133. Support including neignbor report elements in BTM response
144. Support configuring BSS Termination TSF by using hostapd_cli command
155. Disable interface if BSS Termination TSF is set
166. Add set_send_disassoc_frame_timer() to send disassociate frame
17Function set_disassoc_timer() may fail if key was deleted first. This new
18function will not ask to delete key as set_disassoc_timer() did.
197. Support including neighbor report elements in BTM request
208. Add hostapd_neighbor_set_own_report_pref()
219. Add hostapd_neighbor_set_pref_by_non_pref_chan()
22---
23 hostapd/ctrl_iface.c | 5 ++
24 src/ap/ap_config.c | 1 +
25 src/ap/ap_config.h | 1 +
26 src/ap/ctrl_iface_ap.c | 19 ++++++-
27 src/ap/gas_serv.c | 29 ++++++++++
28 src/ap/gas_serv.h | 2 +
29 src/ap/neighbor_db.c | 119 +++++++++++++++++++++++++++++++++++++++++
30 src/ap/neighbor_db.h | 9 ++++
31 src/ap/wnm_ap.c | 72 +++++++++++++++++++++++--
32 9 files changed, 252 insertions(+), 5 deletions(-)
33
34diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
35index a0e4ecd..0355e8b 100644
36--- a/hostapd/ctrl_iface.c
37+++ b/hostapd/ctrl_iface.c
38@@ -1347,6 +1347,11 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
39 #endif /* CONFIG_DPP */
40 } else if (os_strcasecmp(cmd, "setband") == 0) {
41 ret = hostapd_ctrl_iface_set_band(hapd, value);
42+ } else if (os_strcasecmp(cmd, "bss_termination_tsf") == 0) {
43+ int termination_sec = atoi(value);
44+ hapd->conf->bss_termination_tsf = termination_sec;
45+ wpa_printf(MSG_DEBUG, "BSS Termination TSF: value = %d",
46+ termination_sec);
47 } else {
48 ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
49 if (ret)
50diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
51index 2755146..9f450f6 100644
52--- a/src/ap/ap_config.c
53+++ b/src/ap/ap_config.c
54@@ -170,6 +170,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
55 /* comeback after 10 TUs */
56 bss->pasn_comeback_after = 10;
57 #endif /* CONFIG_PASN */
58+ bss->bss_termination_tsf = 0;
59 }
60
61
62diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
63index 7774360..af9bf92 100644
64--- a/src/ap/ap_config.h
65+++ b/src/ap/ap_config.h
66@@ -558,6 +558,7 @@ struct hostapd_bss_config {
67 int wnm_sleep_mode;
68 int wnm_sleep_mode_no_keys;
69 int bss_transition;
70+ unsigned int bss_termination_tsf;
71
72 /* IEEE 802.11u - Interworking */
73 int interworking;
74diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
75index d46de44..38fc8e4 100644
76--- a/src/ap/ctrl_iface_ap.c
77+++ b/src/ap/ctrl_iface_ap.c
78@@ -1265,6 +1265,10 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
79 wpa_printf(MSG_DEBUG, "Invalid bss_term data");
80 return -1;
81 }
82+ if (hapd->conf->bss_termination_tsf) {
83+ WPA_PUT_LE64(&bss_term_dur[2], hapd->conf->bss_termination_tsf);
84+ }
85+
86 end++;
87 WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
88 }
89@@ -1291,14 +1295,25 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
90 req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
91 }
92
93- if (os_strstr(cmd, " pref=1"))
94+ if (os_strstr(cmd, " pref=1")) {
95 req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
96+ if (nei_len == 0) {
97+ // Add neigibor report from neighbor report db to nei_rep buffer
98+ nei_len = hostapd_neighbor_insert_buffer (hapd, nei_rep, 1000);
99+ }
100+ }
101 if (os_strstr(cmd, " abridged=1"))
102 req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
103- if (os_strstr(cmd, " disassoc_imminent=1"))
104+ if (os_strstr(cmd, " disassoc_imminent=1")) {
105 req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
106+ /* Set own BSS neighbor report preference value as 0 */
107+ hostapd_neighbor_set_own_report_pref(hapd, nei_rep, nei_len, 0);
108+ }
109+
110
111 #ifdef CONFIG_MBO
112+ hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
113+
114 pos = os_strstr(cmd, "mbo=");
115 if (pos) {
116 unsigned int mbo_reason, cell_pref, reassoc_delay;
117diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
118index 4642e49..cce6df4 100644
119--- a/src/ap/gas_serv.c
120+++ b/src/ap/gas_serv.c
121@@ -19,6 +19,7 @@
122 #include "dpp_hostapd.h"
123 #include "sta_info.h"
124 #include "gas_serv.h"
125+#include "neighbor_db.h"
126
127
128 #ifdef CONFIG_DPP
129@@ -369,6 +370,24 @@ static void anqp_add_network_auth_type(struct hostapd_data *hapd,
130 }
131 }
132
133+static void anqp_add_neighbor_report(struct hostapd_data *hapd,
134+ struct wpabuf *buf)
135+{
136+ struct hostapd_neighbor_entry *nr;
137+ u8 *len_pos = gas_anqp_add_element(buf, ANQP_NEIGHBOR_REPORT);
138+ if (dl_list_empty(&hapd->nr_db)) {
139+ wpabuf_put_le16(buf, 0);
140+ }
141+ else {
142+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list ) {
143+ wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
144+ wpabuf_put_u8(buf, wpabuf_len(nr->nr));
145+ wpabuf_put_buf(buf, nr->nr);
146+ }
147+ }
148+ gas_anqp_set_element_len(buf, len_pos);
149+}
150+
151
152 static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
153 struct wpabuf *buf)
154@@ -986,6 +1005,9 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
155 len += 1000;
156 if (request & ANQP_REQ_ICON_REQUEST)
157 len += 65536;
158+ if (request & ANQP_REQ_NEIGHBOR_REPORT) {
159+ len += (40 * hostapd_neighbor_count(hapd));
160+ }
161 #ifdef CONFIG_FILS
162 if (request & ANQP_FILS_REALM_INFO)
163 len += 2 * dl_list_len(&hapd->conf->fils_realms);
164@@ -1028,6 +1050,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
165 anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
166 if (request & ANQP_REQ_EMERGENCY_NAI)
167 anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
168+ if (request & ANQP_REQ_NEIGHBOR_REPORT)
169+ anqp_add_neighbor_report(hapd, buf);
170
171 for (i = 0; i < num_extra_req; i++) {
172 #ifdef CONFIG_FILS
173@@ -1172,6 +1196,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
174 "Emergency NAI",
175 get_anqp_elem(hapd, info_id) != NULL, qi);
176 break;
177+ case ANQP_NEIGHBOR_REPORT:
178+ set_anqp_req(ANQP_REQ_NEIGHBOR_REPORT,
179+ "Neighbor Report",
180+ get_anqp_elem(hapd, info_id) != NULL, qi);
181+ break;
182 default:
183 #ifdef CONFIG_FILS
184 if (info_id == ANQP_FILS_REALM_INFO &&
185diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
186index 7646a98..ce492b5 100644
187--- a/src/ap/gas_serv.h
188+++ b/src/ap/gas_serv.h
189@@ -40,6 +40,8 @@
190 (1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
191 #define ANQP_REQ_EMERGENCY_NAI \
192 (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
193+#define ANQP_REQ_NEIGHBOR_REPORT \
194+ (1 << (ANQP_NEIGHBOR_REPORT - ANQP_QUERY_LIST))
195 /*
196 * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
197 * optimized bitmap.
198diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
199index 5b276e8..1c14b32 100644
200--- a/src/ap/neighbor_db.c
201+++ b/src/ap/neighbor_db.c
202@@ -89,6 +89,38 @@ int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
203 }
204
205
206+int hostapd_neighbor_count(struct hostapd_data *hapd)
207+{
208+ struct hostapd_neighbor_entry *nr;
209+ int count = 0;
210+
211+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
212+ list) {
213+ count++;
214+ }
215+ return count;
216+}
217+
218+
219+int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
220+ size_t buflen)
221+{
222+ struct hostapd_neighbor_entry *nr;
223+ char *pos = buf;
224+
225+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
226+ list) {
227+ /* For neighbor report IE, we only need bssid and nr*/
228+ *pos++ = WLAN_EID_NEIGHBOR_REPORT;
229+ *pos++ = wpabuf_len(nr->nr);
230+ os_memcpy(pos, wpabuf_head(nr->nr), wpabuf_len(nr->nr));
231+ pos += wpabuf_len(nr->nr);
232+ }
233+
234+ return pos - buf;
235+}
236+
237+
238 static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
239 {
240 wpabuf_free(nr->nr);
241@@ -325,3 +357,90 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
242 wpabuf_free(nr);
243 #endif /* NEED_AP_MLME */
244 }
245+
246+
247+void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
248+ size_t buflen, const int pref)
249+{
250+ struct hostapd_neighbor_entry *nr;
251+ char *pos, *next_nr;
252+
253+ pos = nei_buf;
254+ next_nr = nei_buf;
255+
256+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
257+ list) {
258+ pos = next_nr;
259+ next_nr = pos + 2 + wpabuf_len(nr->nr);
260+ /* Shift 2 bytes for Element ID and Neighbor report length */
261+ pos = pos + 2;
262+ if(os_memcmp(pos, hapd->own_addr, ETH_ALEN) == 0) {
263+ /* Shift for BSSID + BSSID info + Op_class + channel num + PHY type */
264+ pos = pos + 6 + 4 + 1 + 1 + 1;
265+
266+ /* Iterate Subelement */
267+ while (next_nr - pos > 0) {
268+ if (*pos == 3) {
269+ pos = pos + 2;
270+ *pos = pref;
271+ return;
272+ } else {
273+ pos++;
274+ int shift_len = *pos++;
275+ pos = pos + shift_len;
276+ }
277+ }
278+ }
279+ }
280+}
281+
282+#ifdef CONFIG_MBO
283+void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
284+ struct sta_info* sta, char *nei_buf, size_t buflen)
285+{
286+ struct hostapd_neighbor_entry *nr;
287+ struct mbo_non_pref_chan_info *info;
288+ u8 i;
289+
290+ for(info = sta->non_pref_chan; info; info = info->next) {
291+ /* Check OP_Class and Channel num */
292+ for(i = 0; i < info->num_channels; i++) {
293+ char *pos, *next_nr;
294+
295+ pos = nei_buf;
296+ next_nr = nei_buf;
297+
298+ /* Iterate Neighbor report database */
299+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
300+ list) {
301+ pos = next_nr;
302+ next_nr = pos + 2 + wpabuf_len(nr->nr);
303+ /**
304+ * Shift 12 bytes for Element ID, Neighbor report length,
305+ * BSSID and BSSID info.
306+ */
307+ pos = pos + 12;
308+ int nr_op_class = *pos++;
309+ int nr_channel = *pos;
310+ if(info->op_class == nr_op_class && info->channels[i] == nr_channel) {
311+ /* Shift for Channel Num + PHY type */
312+ pos = pos + 1 + 1;
313+
314+ // Iterate Subelement
315+ while(next_nr - pos > 0) {
316+ if(*pos == 3) {
317+ pos = pos + 2;
318+ *pos = info->pref;
319+ break;
320+ }else {
321+ pos++;
322+ int shift_len = *pos++;
323+ pos = pos + shift_len;
324+ }
325+ }
326+ }
327+ }
328+ }
329+ }
330+}
331+#endif
332diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
333index 992671b..a1ddc07 100644
334--- a/src/ap/neighbor_db.h
335+++ b/src/ap/neighbor_db.h
336@@ -24,4 +24,13 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
337 const struct wpa_ssid_value *ssid);
338 void hostapd_free_neighbor_db(struct hostapd_data *hapd);
339
340+int hostapd_neighbor_count(struct hostapd_data *hapd);
341+int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
342+ size_t buflen);
343+void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
344+ size_t buflen, const int pref);
345+#ifdef CONFIG_MBO
346+void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
347+ struct sta_info* sta, char *nei_buf, size_t buflen);
348+#endif
349 #endif /* NEIGHBOR_DB_H */
350diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
351index ba1dd2e..32ccf14 100644
352--- a/src/ap/wnm_ap.c
353+++ b/src/ap/wnm_ap.c
354@@ -20,6 +20,7 @@
355 #include "ap/wpa_auth.h"
356 #include "mbo_ap.h"
357 #include "wnm_ap.h"
358+#include "ap/neighbor_db.h"
359
360 #define MAX_TFS_IE_LEN 1024
361
362@@ -370,9 +371,21 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
363 u8 *pos;
364 int res;
365
366- mgmt = os_zalloc(sizeof(*mgmt));
367- if (mgmt == NULL)
368+ int nr_num = hostapd_neighbor_count(hapd);
369+ int nr_size = ETH_ALEN + 4 + 1 + 1 + 1 + 5;
370+ int total_nr_size = nr_num * nr_size;
371+ u8 *nr_data = os_malloc(total_nr_size);
372+ int nr_data_len = 0;
373+ if(nr_data == NULL) {
374+ wpa_printf (MSG_ERROR, "Failed to allocate memory");
375+ } else {
376+ nr_data_len = hostapd_neighbor_insert_buffer(hapd, nr_data, total_nr_size);
377+ }
378+ mgmt = os_zalloc(sizeof(*mgmt) + nr_data_len);
379+ if (mgmt == NULL) {
380+ wpa_printf (MSG_ERROR, "Failed to allocate memory for mgmt frame");
381 return -1;
382+ }
383 os_memcpy(mgmt->da, addr, ETH_ALEN);
384 os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
385 os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
386@@ -382,10 +395,18 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
387 mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
388 mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
389 mgmt->u.action.u.bss_tm_req.req_mode = 0;
390+ if(nr_num) {
391+ mgmt->u.action.u.bss_tm_req.req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
392+ }
393 mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
394 mgmt->u.action.u.bss_tm_req.validity_interval = 1;
395 pos = mgmt->u.action.u.bss_tm_req.variable;
396
397+ if(nr_num) {
398+ os_memcpy(pos, nr_data, nr_data_len);
399+ pos += nr_data_len;
400+ }
401+
402 hapd->openwrt_stats.wnm.bss_transition_request_tx++;
403 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
404 MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
405@@ -890,6 +911,50 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
406 }
407
408
409+static void set_send_disassoc_frame_timer(struct hostapd_data *hapd, struct sta_info *sta,
410+ int disassoc_timer)
411+{
412+ int timeout, beacon_int;
413+
414+ /*
415+ * Prevent STA from reconnecting using cached PMKSA to force
416+ * full authentication with the authentication server (which may
417+ * decide to reject the connection),
418+ */
419+ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
420+
421+ beacon_int = hapd->iconf->beacon_int;
422+ if (beacon_int < 1)
423+ beacon_int = 100; /* best guess */
424+ /* Calculate timeout in ms based on beacon_int in TU */
425+ timeout = disassoc_timer * beacon_int * 128 / 125;
426+ wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
427+ " set to %d ms", MAC2STR(sta->addr), timeout);
428+
429+ u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
430+
431+ hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
432+ if (sta)
433+ ap_sta_disassociate(hapd, sta, reason);
434+}
435+
436+
437+void bss_termination_disable_iface(void *eloop_ctx, void *timeout_ctx)
438+{
439+ struct hostapd_data *hapd = eloop_ctx;
440+ hostapd_disable_iface(hapd->iface);
441+}
442+
443+
444+static void set_disable_iface_timer(struct hostapd_data *hapd, struct sta_info *sta,
445+ int disable_iface_timer)
446+{
447+ wpa_printf(MSG_DEBUG, "Disable interface timer set to %d secs", disable_iface_timer);
448+ eloop_register_timeout(disable_iface_timer, 0,
449+ bss_termination_disable_iface, hapd, NULL);
450+}
451+
452+
453 int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
454 struct sta_info *sta, const char *url,
455 int disassoc_timer)
456@@ -934,7 +999,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
457 hapd->openwrt_stats.wnm.bss_transition_request_tx++;
458 if (disassoc_timer) {
459 /* send disassociation frame after time-out */
460- set_disassoc_timer(hapd, sta, disassoc_timer);
461+ set_send_disassoc_frame_timer(hapd, sta, disassoc_timer);
462 }
463
464 return 0;
465@@ -979,6 +1044,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
466 bss_term_dur) {
467 os_memcpy(pos, bss_term_dur, 12);
468 pos += 12;
469+ set_disable_iface_timer(hapd, sta, hapd->conf->bss_termination_tsf);
470 }
471
472 if (url) {
473--
4742.18.0
475