xref: /dragonfly/contrib/wpa_supplicant/src/ap/wnm_ap.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
1 /*
2  * hostapd - WNM
3  * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "utils/includes.h"
10 
11 #include "utils/common.h"
12 #include "utils/eloop.h"
13 #include "common/ieee802_11_defs.h"
14 #include "common/wpa_ctrl.h"
15 #include "common/ocv.h"
16 #include "ap/hostapd.h"
17 #include "ap/sta_info.h"
18 #include "ap/ap_config.h"
19 #include "ap/ap_drv_ops.h"
20 #include "ap/wpa_auth.h"
21 #include "mbo_ap.h"
22 #include "wnm_ap.h"
23 
24 #define MAX_TFS_IE_LEN  1024
25 
26 
27 /* get the TFS IE from driver */
ieee80211_11_get_tfs_ie(struct hostapd_data * hapd,const u8 * addr,u8 * buf,u16 * buf_len,enum wnm_oper oper)28 static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
29                                            u8 *buf, u16 *buf_len, enum wnm_oper oper)
30 {
31           wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
32 
33           return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
34 }
35 
36 
37 /* set the TFS IE to driver */
ieee80211_11_set_tfs_ie(struct hostapd_data * hapd,const u8 * addr,u8 * buf,u16 * buf_len,enum wnm_oper oper)38 static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
39                                            u8 *buf, u16 *buf_len, enum wnm_oper oper)
40 {
41           wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
42 
43           return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
44 }
45 
46 
47 /* MLME-SLEEPMODE.response */
ieee802_11_send_wnmsleep_resp(struct hostapd_data * hapd,const u8 * addr,u8 dialog_token,u8 action_type,u16 intval)48 static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
49                                                    const u8 *addr, u8 dialog_token,
50                                                    u8 action_type, u16 intval)
51 {
52           struct ieee80211_mgmt *mgmt;
53           int res;
54           size_t len;
55           size_t gtk_elem_len = 0;
56           size_t igtk_elem_len = 0;
57           struct wnm_sleep_element wnmsleep_ie;
58           u8 *wnmtfs_ie, *oci_ie;
59           u8 wnmsleep_ie_len, oci_ie_len;
60           u16 wnmtfs_ie_len;
61           u8 *pos;
62           struct sta_info *sta;
63           enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
64                     WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
65 
66           sta = ap_get_sta(hapd, addr);
67           if (sta == NULL) {
68                     wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
69                     return -EINVAL;
70           }
71 
72           /* WNM-Sleep Mode IE */
73           os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
74           wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
75           wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
76           wnmsleep_ie.len = wnmsleep_ie_len - 2;
77           wnmsleep_ie.action_type = action_type;
78           wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
79           wnmsleep_ie.intval = host_to_le16(intval);
80 
81           /* TFS IE(s) */
82           wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
83           if (wnmtfs_ie == NULL)
84                     return -1;
85           if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
86                                             tfs_oper)) {
87                     wnmtfs_ie_len = 0;
88                     os_free(wnmtfs_ie);
89                     wnmtfs_ie = NULL;
90           }
91 
92           oci_ie = NULL;
93           oci_ie_len = 0;
94 #ifdef CONFIG_OCV
95           if (action_type == WNM_SLEEP_MODE_EXIT &&
96               wpa_auth_uses_ocv(sta->wpa_sm)) {
97                     struct wpa_channel_info ci;
98 
99                     if (hostapd_drv_channel_info(hapd, &ci) != 0) {
100                               wpa_printf(MSG_WARNING,
101                                            "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
102                               os_free(wnmtfs_ie);
103                               return -1;
104                     }
105 
106                     oci_ie_len = OCV_OCI_EXTENDED_LEN;
107                     oci_ie = os_zalloc(oci_ie_len);
108                     if (!oci_ie) {
109                               wpa_printf(MSG_WARNING,
110                                            "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
111                               os_free(wnmtfs_ie);
112                               return -1;
113                     }
114 
115                     if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
116                               os_free(wnmtfs_ie);
117                               os_free(oci_ie);
118                               return -1;
119                     }
120           }
121 #endif /* CONFIG_OCV */
122 
123 #define MAX_GTK_SUBELEM_LEN 45
124 #define MAX_IGTK_SUBELEM_LEN 26
125           mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
126                                MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
127                                oci_ie_len);
128           if (mgmt == NULL) {
129                     wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
130                                  "WNM-Sleep Response action frame");
131                     res = -1;
132                     goto fail;
133           }
134           os_memcpy(mgmt->da, addr, ETH_ALEN);
135           os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
136           os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
137           mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
138                                                      WLAN_FC_STYPE_ACTION);
139           mgmt->u.action.category = WLAN_ACTION_WNM;
140           mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
141           mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
142           pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
143           /* add key data if MFP is enabled */
144           if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
145               hapd->conf->wnm_sleep_mode_no_keys ||
146               action_type != WNM_SLEEP_MODE_EXIT) {
147                     mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
148           } else {
149                     gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
150                     pos += gtk_elem_len;
151                     wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
152                                  (int) gtk_elem_len);
153 #ifdef CONFIG_IEEE80211W
154                     res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
155                     if (res < 0)
156                               goto fail;
157                     igtk_elem_len = res;
158                     pos += igtk_elem_len;
159                     wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
160                                  (int) igtk_elem_len);
161 #endif /* CONFIG_IEEE80211W */
162 
163                     WPA_PUT_LE16((u8 *)
164                                    &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
165                                    gtk_elem_len + igtk_elem_len);
166           }
167           os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
168           /* copy TFS IE here */
169           pos += wnmsleep_ie_len;
170           if (wnmtfs_ie) {
171                     os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
172                     pos += wnmtfs_ie_len;
173           }
174 #ifdef CONFIG_OCV
175           /* copy OCV OCI here */
176           if (oci_ie_len > 0)
177                     os_memcpy(pos, oci_ie, oci_ie_len);
178 #endif /* CONFIG_OCV */
179 
180           len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
181                     igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
182 
183           /* In driver, response frame should be forced to sent when STA is in
184            * PS mode */
185           res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
186                                               mgmt->da, &mgmt->u.action.category, len);
187 
188           if (!res) {
189                     wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
190                                  "frame");
191 
192                     /* when entering wnmsleep
193                      * 1. pause the node in driver
194                      * 2. mark the node so that AP won't update GTK/IGTK during
195                      * WNM Sleep
196                      */
197                     if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
198                         wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
199                               sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
200                               hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
201                                                        addr, NULL, NULL);
202                               wpa_set_wnmsleep(sta->wpa_sm, 1);
203                     }
204                     /* when exiting wnmsleep
205                      * 1. unmark the node
206                      * 2. start GTK/IGTK update if MFP is not used
207                      * 3. unpause the node in driver
208                      */
209                     if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
210                          wnmsleep_ie.status ==
211                          WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
212                         wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
213                               sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
214                               wpa_set_wnmsleep(sta->wpa_sm, 0);
215                               hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
216                                                        addr, NULL, NULL);
217                               if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
218                                   hapd->conf->wnm_sleep_mode_no_keys)
219                                         wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
220                     }
221           } else
222                     wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
223 
224 #undef MAX_GTK_SUBELEM_LEN
225 #undef MAX_IGTK_SUBELEM_LEN
226 fail:
227           os_free(wnmtfs_ie);
228           os_free(oci_ie);
229           os_free(mgmt);
230           return res;
231 }
232 
233 
ieee802_11_rx_wnmsleep_req(struct hostapd_data * hapd,const u8 * addr,const u8 * frm,int len)234 static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
235                                                const u8 *addr, const u8 *frm, int len)
236 {
237           /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
238           const u8 *pos = frm;
239           u8 dialog_token;
240           struct wnm_sleep_element *wnmsleep_ie = NULL;
241           /* multiple TFS Req IE (assuming consecutive) */
242           u8 *tfsreq_ie_start = NULL;
243           u8 *tfsreq_ie_end = NULL;
244           u16 tfsreq_ie_len = 0;
245 #ifdef CONFIG_OCV
246           struct sta_info *sta;
247           const u8 *oci_ie = NULL;
248           u8 oci_ie_len = 0;
249 #endif /* CONFIG_OCV */
250 
251           if (!hapd->conf->wnm_sleep_mode) {
252                     wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
253                                  MACSTR " since WNM-Sleep Mode is disabled",
254                                  MAC2STR(addr));
255                     return;
256           }
257 
258           if (len < 1) {
259                     wpa_printf(MSG_DEBUG,
260                                  "WNM: Ignore too short WNM-Sleep Mode Request from "
261                                  MACSTR, MAC2STR(addr));
262                     return;
263           }
264 
265           dialog_token = *pos++;
266           while (pos + 1 < frm + len) {
267                     u8 ie_len = pos[1];
268                     if (pos + 2 + ie_len > frm + len)
269                               break;
270                     if (*pos == WLAN_EID_WNMSLEEP &&
271                         ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
272                               wnmsleep_ie = (struct wnm_sleep_element *) pos;
273                     else if (*pos == WLAN_EID_TFS_REQ) {
274                               if (!tfsreq_ie_start)
275                                         tfsreq_ie_start = (u8 *) pos;
276                               tfsreq_ie_end = (u8 *) pos;
277 #ifdef CONFIG_OCV
278                     } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
279                                  pos[2] == WLAN_EID_EXT_OCV_OCI) {
280                               oci_ie = pos + 3;
281                               oci_ie_len = ie_len - 1;
282 #endif /* CONFIG_OCV */
283                     } else
284                               wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
285                                            *pos);
286                     pos += ie_len + 2;
287           }
288 
289           if (!wnmsleep_ie) {
290                     wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
291                     return;
292           }
293 
294 #ifdef CONFIG_OCV
295           sta = ap_get_sta(hapd, addr);
296           if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
297               sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
298                     struct wpa_channel_info ci;
299 
300                     if (hostapd_drv_channel_info(hapd, &ci) != 0) {
301                               wpa_printf(MSG_WARNING,
302                                            "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
303                               return;
304                     }
305 
306                     if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
307                                                    channel_width_to_int(ci.chanwidth),
308                                                    ci.seg1_idx) != 0) {
309                               wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr);
310                               return;
311                     }
312           }
313 #endif /* CONFIG_OCV */
314 
315           if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
316               tfsreq_ie_start && tfsreq_ie_end &&
317               tfsreq_ie_end - tfsreq_ie_start >= 0) {
318                     tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
319                               tfsreq_ie_start;
320                     wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
321                     /* pass the TFS Req IE(s) to driver for processing */
322                     if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
323                                                       &tfsreq_ie_len,
324                                                       WNM_SLEEP_TFS_REQ_IE_SET))
325                               wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
326           }
327 
328           ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
329                                               wnmsleep_ie->action_type,
330                                               le_to_host16(wnmsleep_ie->intval));
331 
332           if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
333                     /* clear the tfs after sending the resp frame */
334                     ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
335                                                   &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
336           }
337 }
338 
339 
ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data * hapd,const u8 * addr,u8 dialog_token)340 static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
341                                                               const u8 *addr,
342                                                               u8 dialog_token)
343 {
344           struct ieee80211_mgmt *mgmt;
345           size_t len;
346           u8 *pos;
347           int res;
348 
349           mgmt = os_zalloc(sizeof(*mgmt));
350           if (mgmt == NULL)
351                     return -1;
352           os_memcpy(mgmt->da, addr, ETH_ALEN);
353           os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
354           os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
355           mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
356                                                      WLAN_FC_STYPE_ACTION);
357           mgmt->u.action.category = WLAN_ACTION_WNM;
358           mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
359           mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
360           mgmt->u.action.u.bss_tm_req.req_mode = 0;
361           mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
362           mgmt->u.action.u.bss_tm_req.validity_interval = 1;
363           pos = mgmt->u.action.u.bss_tm_req.variable;
364 
365           wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
366                        MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
367                        "validity_interval=%u",
368                        MAC2STR(addr), dialog_token,
369                        mgmt->u.action.u.bss_tm_req.req_mode,
370                        le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
371                        mgmt->u.action.u.bss_tm_req.validity_interval);
372 
373           len = pos - &mgmt->u.action.category;
374           res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
375                                               mgmt->da, &mgmt->u.action.category, len);
376           os_free(mgmt);
377           return res;
378 }
379 
380 
ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data * hapd,const u8 * addr,const u8 * frm,size_t len)381 static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
382                                                          const u8 *addr, const u8 *frm,
383                                                          size_t len)
384 {
385           u8 dialog_token, reason;
386           const u8 *pos, *end;
387           int enabled = hapd->conf->bss_transition;
388 
389 #ifdef CONFIG_MBO
390           if (hapd->conf->mbo_enabled)
391                     enabled = 1;
392 #endif /* CONFIG_MBO */
393           if (!enabled) {
394                     wpa_printf(MSG_DEBUG,
395                                  "Ignore BSS Transition Management Query from "
396                                  MACSTR
397                                  " since BSS Transition Management is disabled",
398                                  MAC2STR(addr));
399                     return;
400           }
401 
402           if (len < 2) {
403                     wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
404                                  MACSTR, MAC2STR(addr));
405                     return;
406           }
407 
408           pos = frm;
409           end = pos + len;
410           dialog_token = *pos++;
411           reason = *pos++;
412 
413           wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
414                        MACSTR " dialog_token=%u reason=%u",
415                        MAC2STR(addr), dialog_token, reason);
416 
417           wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
418                         pos, end - pos);
419 
420           ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
421 }
422 
423 
ap_sta_reset_steer_flag_timer(void * eloop_ctx,void * timeout_ctx)424 void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
425 {
426           struct hostapd_data *hapd = eloop_ctx;
427           struct sta_info *sta = timeout_ctx;
428 
429           if (sta->agreed_to_steer) {
430                     wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
431                                  hapd->conf->iface, MAC2STR(sta->addr));
432                     sta->agreed_to_steer = 0;
433           }
434 }
435 
436 
ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data * hapd,const u8 * addr,const u8 * frm,size_t len)437 static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
438                                                         const u8 *addr, const u8 *frm,
439                                                         size_t len)
440 {
441           u8 dialog_token, status_code, bss_termination_delay;
442           const u8 *pos, *end;
443           int enabled = hapd->conf->bss_transition;
444           struct sta_info *sta;
445 
446 #ifdef CONFIG_MBO
447           if (hapd->conf->mbo_enabled)
448                     enabled = 1;
449 #endif /* CONFIG_MBO */
450           if (!enabled) {
451                     wpa_printf(MSG_DEBUG,
452                                  "Ignore BSS Transition Management Response from "
453                                  MACSTR
454                                  " since BSS Transition Management is disabled",
455                                  MAC2STR(addr));
456                     return;
457           }
458 
459           if (len < 3) {
460                     wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
461                                  MACSTR, MAC2STR(addr));
462                     return;
463           }
464 
465           pos = frm;
466           end = pos + len;
467           dialog_token = *pos++;
468           status_code = *pos++;
469           bss_termination_delay = *pos++;
470 
471           wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
472                        MACSTR " dialog_token=%u status_code=%u "
473                        "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
474                        status_code, bss_termination_delay);
475 
476           sta = ap_get_sta(hapd, addr);
477           if (!sta) {
478                     wpa_printf(MSG_DEBUG, "Station " MACSTR
479                                  " not found for received BSS TM Response",
480                                  MAC2STR(addr));
481                     return;
482           }
483 
484           if (status_code == WNM_BSS_TM_ACCEPT) {
485                     if (end - pos < ETH_ALEN) {
486                               wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
487                               return;
488                     }
489                     sta->agreed_to_steer = 1;
490                     eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
491                     eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
492                                                hapd, sta);
493                     wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
494                                  MAC2STR(pos));
495                     wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
496                               " status_code=%u bss_termination_delay=%u target_bssid="
497                               MACSTR,
498                               MAC2STR(addr), status_code, bss_termination_delay,
499                               MAC2STR(pos));
500                     pos += ETH_ALEN;
501           } else {
502                     sta->agreed_to_steer = 0;
503                     wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
504                               " status_code=%u bss_termination_delay=%u",
505                               MAC2STR(addr), status_code, bss_termination_delay);
506           }
507 
508           wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
509                         pos, end - pos);
510 }
511 
512 
ieee802_11_rx_wnm_notification_req(struct hostapd_data * hapd,const u8 * addr,const u8 * buf,size_t len)513 static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
514                                                          const u8 *addr, const u8 *buf,
515                                                          size_t len)
516 {
517           u8 dialog_token, type;
518 
519           if (len < 2)
520                     return;
521           dialog_token = *buf++;
522           type = *buf++;
523           len -= 2;
524 
525           wpa_printf(MSG_DEBUG,
526                        "WNM: Received WNM Notification Request frame from "
527                        MACSTR " (dialog_token=%u type=%u)",
528                        MAC2STR(addr), dialog_token, type);
529           wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
530                         buf, len);
531           if (type == WLAN_EID_VENDOR_SPECIFIC)
532                     mbo_ap_wnm_notification_req(hapd, addr, buf, len);
533 }
534 
535 
ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data * hapd,const u8 * addr,const u8 * buf,size_t len)536 static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
537                                                             const u8 *addr, const u8 *buf,
538                                                             size_t len)
539 {
540           u8 dialog_token;
541           char *hex;
542           size_t hex_len;
543 
544           if (!hapd->conf->coloc_intf_reporting) {
545                     wpa_printf(MSG_DEBUG,
546                                  "WNM: Ignore unexpected Collocated Interference Report from "
547                                  MACSTR, MAC2STR(addr));
548                     return;
549           }
550 
551           if (len < 1) {
552                     wpa_printf(MSG_DEBUG,
553                                  "WNM: Ignore too short Collocated Interference Report from "
554                                  MACSTR, MAC2STR(addr));
555                     return;
556           }
557           dialog_token = *buf++;
558           len--;
559 
560           wpa_printf(MSG_DEBUG,
561                        "WNM: Received Collocated Interference Report frame from "
562                        MACSTR " (dialog_token=%u)",
563                        MAC2STR(addr), dialog_token);
564           wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
565                         buf, len);
566 
567           hex_len = 2 * len + 1;
568           hex = os_malloc(hex_len);
569           if (!hex)
570                     return;
571           wpa_snprintf_hex(hex, hex_len, buf, len);
572           wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
573                          MAC2STR(addr), dialog_token, hex);
574           os_free(hex);
575 }
576 
577 
ieee802_11_rx_wnm_action_ap(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len)578 int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
579                                         const struct ieee80211_mgmt *mgmt, size_t len)
580 {
581           u8 action;
582           const u8 *payload;
583           size_t plen;
584 
585           if (len < IEEE80211_HDRLEN + 2)
586                     return -1;
587 
588           payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
589           action = *payload++;
590           plen = len - IEEE80211_HDRLEN - 2;
591 
592           switch (action) {
593           case WNM_BSS_TRANS_MGMT_QUERY:
594                     ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
595                                                                plen);
596                     return 0;
597           case WNM_BSS_TRANS_MGMT_RESP:
598                     ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
599                                                               plen);
600                     return 0;
601           case WNM_SLEEP_MODE_REQ:
602                     ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
603                     return 0;
604           case WNM_NOTIFICATION_REQ:
605                     ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
606                                                                plen);
607                     return 0;
608           case WNM_COLLOCATED_INTERFERENCE_REPORT:
609                     ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
610                                                                 plen);
611                     return 0;
612           }
613 
614           wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
615                        action, MAC2STR(mgmt->sa));
616           return -1;
617 }
618 
619 
wnm_send_disassoc_imminent(struct hostapd_data * hapd,struct sta_info * sta,int disassoc_timer)620 int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
621                                      struct sta_info *sta, int disassoc_timer)
622 {
623           u8 buf[1000], *pos;
624           struct ieee80211_mgmt *mgmt;
625 
626           os_memset(buf, 0, sizeof(buf));
627           mgmt = (struct ieee80211_mgmt *) buf;
628           mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
629                                                      WLAN_FC_STYPE_ACTION);
630           os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
631           os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
632           os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
633           mgmt->u.action.category = WLAN_ACTION_WNM;
634           mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
635           mgmt->u.action.u.bss_tm_req.dialog_token = 1;
636           mgmt->u.action.u.bss_tm_req.req_mode =
637                     WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
638           mgmt->u.action.u.bss_tm_req.disassoc_timer =
639                     host_to_le16(disassoc_timer);
640           mgmt->u.action.u.bss_tm_req.validity_interval = 0;
641 
642           pos = mgmt->u.action.u.bss_tm_req.variable;
643 
644           wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
645                        MACSTR, disassoc_timer, MAC2STR(sta->addr));
646           if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
647                     wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
648                                  "Management Request frame");
649                     return -1;
650           }
651 
652           return 0;
653 }
654 
655 
set_disassoc_timer(struct hostapd_data * hapd,struct sta_info * sta,int disassoc_timer)656 static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
657                                      int disassoc_timer)
658 {
659           int timeout, beacon_int;
660 
661           /*
662            * Prevent STA from reconnecting using cached PMKSA to force
663            * full authentication with the authentication server (which may
664            * decide to reject the connection),
665            */
666           wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
667 
668           beacon_int = hapd->iconf->beacon_int;
669           if (beacon_int < 1)
670                     beacon_int = 100; /* best guess */
671           /* Calculate timeout in ms based on beacon_int in TU */
672           timeout = disassoc_timer * beacon_int * 128 / 125;
673           wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
674                        " set to %d ms", MAC2STR(sta->addr), timeout);
675 
676           sta->timeout_next = STA_DISASSOC_FROM_CLI;
677           eloop_cancel_timeout(ap_handle_timer, hapd, sta);
678           eloop_register_timeout(timeout / 1000,
679                                      timeout % 1000 * 1000,
680                                      ap_handle_timer, hapd, sta);
681 }
682 
683 
wnm_send_ess_disassoc_imminent(struct hostapd_data * hapd,struct sta_info * sta,const char * url,int disassoc_timer)684 int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
685                                            struct sta_info *sta, const char *url,
686                                            int disassoc_timer)
687 {
688           u8 buf[1000], *pos;
689           struct ieee80211_mgmt *mgmt;
690           size_t url_len;
691 
692           os_memset(buf, 0, sizeof(buf));
693           mgmt = (struct ieee80211_mgmt *) buf;
694           mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
695                                                      WLAN_FC_STYPE_ACTION);
696           os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
697           os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
698           os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
699           mgmt->u.action.category = WLAN_ACTION_WNM;
700           mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
701           mgmt->u.action.u.bss_tm_req.dialog_token = 1;
702           mgmt->u.action.u.bss_tm_req.req_mode =
703                     WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
704                     WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
705           mgmt->u.action.u.bss_tm_req.disassoc_timer =
706                     host_to_le16(disassoc_timer);
707           mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
708 
709           pos = mgmt->u.action.u.bss_tm_req.variable;
710 
711           /* Session Information URL */
712           url_len = os_strlen(url);
713           if (url_len > 255)
714                     return -1;
715           *pos++ = url_len;
716           os_memcpy(pos, url, url_len);
717           pos += url_len;
718 
719           if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
720                     wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
721                                  "Management Request frame");
722                     return -1;
723           }
724 
725           if (disassoc_timer) {
726                     /* send disassociation frame after time-out */
727                     set_disassoc_timer(hapd, sta, disassoc_timer);
728           }
729 
730           return 0;
731 }
732 
733 
wnm_send_bss_tm_req(struct hostapd_data * hapd,struct sta_info * sta,u8 req_mode,int disassoc_timer,u8 valid_int,const u8 * bss_term_dur,const char * url,const u8 * nei_rep,size_t nei_rep_len,const u8 * mbo_attrs,size_t mbo_len)734 int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
735                               u8 req_mode, int disassoc_timer, u8 valid_int,
736                               const u8 *bss_term_dur, const char *url,
737                               const u8 *nei_rep, size_t nei_rep_len,
738                               const u8 *mbo_attrs, size_t mbo_len)
739 {
740           u8 *buf, *pos;
741           struct ieee80211_mgmt *mgmt;
742           size_t url_len;
743 
744           wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
745                        MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
746                        MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
747           buf = os_zalloc(1000 + nei_rep_len + mbo_len);
748           if (buf == NULL)
749                     return -1;
750           mgmt = (struct ieee80211_mgmt *) buf;
751           mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
752                                                      WLAN_FC_STYPE_ACTION);
753           os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
754           os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
755           os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
756           mgmt->u.action.category = WLAN_ACTION_WNM;
757           mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
758           mgmt->u.action.u.bss_tm_req.dialog_token = 1;
759           mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
760           mgmt->u.action.u.bss_tm_req.disassoc_timer =
761                     host_to_le16(disassoc_timer);
762           mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
763 
764           pos = mgmt->u.action.u.bss_tm_req.variable;
765 
766           if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
767               bss_term_dur) {
768                     os_memcpy(pos, bss_term_dur, 12);
769                     pos += 12;
770           }
771 
772           if (url) {
773                     /* Session Information URL */
774                     url_len = os_strlen(url);
775                     if (url_len > 255) {
776                               os_free(buf);
777                               return -1;
778                     }
779 
780                     *pos++ = url_len;
781                     os_memcpy(pos, url, url_len);
782                     pos += url_len;
783           }
784 
785           if (nei_rep) {
786                     os_memcpy(pos, nei_rep, nei_rep_len);
787                     pos += nei_rep_len;
788           }
789 
790           if (mbo_len > 0) {
791                     pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
792                                           mbo_len);
793           }
794 
795           if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
796                     wpa_printf(MSG_DEBUG,
797                                  "Failed to send BSS Transition Management Request frame");
798                     os_free(buf);
799                     return -1;
800           }
801           os_free(buf);
802 
803           if (disassoc_timer) {
804                     /* send disassociation frame after time-out */
805                     set_disassoc_timer(hapd, sta, disassoc_timer);
806           }
807 
808           return 0;
809 }
810 
811 
wnm_send_coloc_intf_req(struct hostapd_data * hapd,struct sta_info * sta,unsigned int auto_report,unsigned int timeout)812 int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
813                                   unsigned int auto_report, unsigned int timeout)
814 {
815           u8 buf[100], *pos;
816           struct ieee80211_mgmt *mgmt;
817           u8 dialog_token = 1;
818 
819           if (auto_report > 3 || timeout > 63)
820                     return -1;
821           os_memset(buf, 0, sizeof(buf));
822           mgmt = (struct ieee80211_mgmt *) buf;
823           mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
824                                                      WLAN_FC_STYPE_ACTION);
825           os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
826           os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
827           os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
828           mgmt->u.action.category = WLAN_ACTION_WNM;
829           mgmt->u.action.u.coloc_intf_req.action =
830                     WNM_COLLOCATED_INTERFERENCE_REQ;
831           mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
832           mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
833           pos = &mgmt->u.action.u.coloc_intf_req.req_info;
834           pos++;
835 
836           wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
837                        MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
838                        MAC2STR(sta->addr), dialog_token, auto_report, timeout);
839           if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
840                     wpa_printf(MSG_DEBUG,
841                                  "WNM: Failed to send Collocated Interference Request frame");
842                     return -1;
843           }
844 
845           return 0;
846 }
847