Coverage Report

Created: 2026-09-14 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hostap/wpa_supplicant/wnm_sta.c
Line
Count
Source
1
/*
2
 * wpa_supplicant - WNM
3
 * Copyright (c) 2011-2013, 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/ieee802_11_common.h"
15
#include "common/wpa_ctrl.h"
16
#include "common/ocv.h"
17
#include "rsn_supp/wpa.h"
18
#include "config.h"
19
#include "wpa_supplicant_i.h"
20
#include "driver_i.h"
21
#include "scan.h"
22
#include "ctrl_iface.h"
23
#include "bss.h"
24
#include "wnm_sta.h"
25
#include "notify.h"
26
#include "hs20_supplicant.h"
27
28
0
#define MAX_TFS_IE_LEN  1024
29
0
#define WNM_MAX_NEIGHBOR_REPORT 10
30
31
32
/* get the TFS IE from driver */
33
static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
34
           u16 *buf_len, enum wnm_oper oper)
35
0
{
36
0
  wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
37
38
0
  return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
39
0
}
40
41
42
/* set the TFS IE to driver */
43
static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
44
           const u8 *addr, const u8 *buf, u16 buf_len,
45
           enum wnm_oper oper)
46
0
{
47
0
  u16 len = buf_len;
48
49
0
  wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
50
51
0
  return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len);
52
0
}
53
54
55
static void wnmsleep_mode_exit_timeout(void *eloop_ctx, void *sock_ctx)
56
0
{
57
0
  struct wpa_supplicant *wpa_s = eloop_ctx;
58
59
0
  if (wpa_s->wpa_state <= WPA_ASSOCIATED ||
60
0
      wpa_s->wnmsleep_state != WNM_SLEEP_WAIT_RESP_EXIT)
61
0
    return;
62
63
0
  wpa_printf(MSG_INFO,
64
0
       "WNM: Timeout on WNM Sleep Mode Response (exit) - deauthenticate");
65
0
  wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
66
0
}
67
68
69
/* MLME-SLEEPMODE.request */
70
int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
71
         u8 action, u16 intval, struct wpabuf *tfs_req)
72
0
{
73
0
  struct ieee80211_mgmt *mgmt;
74
0
  int res;
75
0
  size_t len;
76
0
  struct wnm_sleep_element *wnmsleep_ie;
77
0
  u8 *wnmtfs_ie, *oci_ie;
78
0
  u8 wnmsleep_ie_len, oci_ie_len;
79
0
  u16 wnmtfs_ie_len;  /* possibly multiple IE(s) */
80
0
  enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
81
0
    WNM_SLEEP_TFS_REQ_IE_NONE;
82
83
0
  wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
84
0
       "action=%s to " MACSTR,
85
0
       action == 0 ? "enter" : "exit",
86
0
       MAC2STR(wpa_s->bssid));
87
88
  /* WNM-Sleep Mode IE */
89
0
  wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
90
0
  wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
91
0
  if (wnmsleep_ie == NULL)
92
0
    return -1;
93
0
  wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
94
0
  wnmsleep_ie->len = wnmsleep_ie_len - 2;
95
0
  wnmsleep_ie->action_type = action;
96
0
  wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
97
0
  wnmsleep_ie->intval = host_to_le16(intval);
98
0
  wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
99
0
        (u8 *) wnmsleep_ie, wnmsleep_ie_len);
100
101
  /* TFS IE(s) */
102
0
  if (tfs_req) {
103
0
    wnmtfs_ie_len = wpabuf_len(tfs_req);
104
0
    wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len);
105
0
    if (wnmtfs_ie == NULL) {
106
0
      os_free(wnmsleep_ie);
107
0
      return -1;
108
0
    }
109
0
  } else {
110
0
    wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
111
0
    if (wnmtfs_ie == NULL) {
112
0
      os_free(wnmsleep_ie);
113
0
      return -1;
114
0
    }
115
0
    if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
116
0
              tfs_oper)) {
117
0
      wnmtfs_ie_len = 0;
118
0
      os_free(wnmtfs_ie);
119
0
      wnmtfs_ie = NULL;
120
0
    }
121
0
  }
122
0
  wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
123
0
        (u8 *) wnmtfs_ie, wnmtfs_ie_len);
124
125
0
  oci_ie = NULL;
126
0
  oci_ie_len = 0;
127
#ifdef CONFIG_OCV
128
  if (action == WNM_SLEEP_MODE_EXIT && wpa_sm_ocv_enabled(wpa_s->wpa)) {
129
    struct wpa_channel_info ci;
130
131
    if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
132
      wpa_printf(MSG_WARNING,
133
           "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
134
      os_free(wnmsleep_ie);
135
      os_free(wnmtfs_ie);
136
      return -1;
137
    }
138
#ifdef CONFIG_TESTING_OPTIONS
139
    if (wpa_s->oci_freq_override_wnm_sleep) {
140
      wpa_printf(MSG_INFO,
141
           "TEST: Override OCI KDE frequency %d -> %d MHz",
142
           ci.frequency,
143
           wpa_s->oci_freq_override_wnm_sleep);
144
      ci.frequency = wpa_s->oci_freq_override_wnm_sleep;
145
    }
146
#endif /* CONFIG_TESTING_OPTIONS */
147
148
    oci_ie_len = OCV_OCI_EXTENDED_LEN;
149
    oci_ie = os_zalloc(oci_ie_len);
150
    if (!oci_ie) {
151
      wpa_printf(MSG_WARNING,
152
           "Failed to allocate buffer for for OCI element in WNM-Sleep Mode frame");
153
      os_free(wnmsleep_ie);
154
      os_free(wnmtfs_ie);
155
      return -1;
156
    }
157
158
    if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
159
      os_free(wnmsleep_ie);
160
      os_free(wnmtfs_ie);
161
      os_free(oci_ie);
162
      return -1;
163
    }
164
  }
165
#endif /* CONFIG_OCV */
166
167
0
  mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len +
168
0
       oci_ie_len);
169
0
  if (mgmt == NULL) {
170
0
    wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
171
0
         "WNM-Sleep Request action frame");
172
0
    os_free(wnmsleep_ie);
173
0
    os_free(wnmtfs_ie);
174
0
    return -1;
175
0
  }
176
177
0
  os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
178
0
  os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
179
0
  os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
180
0
  mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
181
0
             WLAN_FC_STYPE_ACTION);
182
0
  mgmt->u.action.category = WLAN_ACTION_WNM;
183
0
  mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
184
0
  mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
185
0
  os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
186
0
      wnmsleep_ie_len);
187
  /* copy TFS IE here */
188
0
  if (wnmtfs_ie_len > 0) {
189
0
    os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
190
0
        wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
191
0
  }
192
193
#ifdef CONFIG_OCV
194
  /* copy OCV OCI here */
195
  if (oci_ie_len > 0) {
196
    os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
197
        wnmsleep_ie_len + wnmtfs_ie_len, oci_ie, oci_ie_len);
198
  }
199
#endif /* CONFIG_OCV */
200
201
0
  len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
202
0
    wnmtfs_ie_len + oci_ie_len;
203
204
0
  res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
205
0
          wpa_s->own_addr, wpa_s->bssid,
206
0
          &mgmt->u.action.category, len, 0);
207
0
  if (res < 0) {
208
0
    wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
209
0
         "(action=%d, intval=%d)", action, intval);
210
0
  } else if (action == WNM_SLEEP_MODE_ENTER) {
211
0
    wpa_s->wnmsleep_state = WNM_SLEEP_WAIT_RESP_ENTER;
212
0
  } else if (action == WNM_SLEEP_MODE_EXIT) {
213
0
    wpa_s->wnmsleep_state = WNM_SLEEP_WAIT_RESP_EXIT;
214
0
    eloop_cancel_timeout(wnmsleep_mode_exit_timeout, wpa_s, NULL);
215
0
    eloop_register_timeout(5, 0, wnmsleep_mode_exit_timeout,
216
0
               wpa_s, NULL);
217
0
  }
218
219
0
  os_free(wnmsleep_ie);
220
0
  os_free(wnmtfs_ie);
221
0
  os_free(oci_ie);
222
0
  os_free(mgmt);
223
224
0
  return res;
225
0
}
226
227
228
static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
229
           const u8 *tfsresp_ie_start,
230
           const u8 *tfsresp_ie_end)
231
0
{
232
0
  wpa_sm_notify_wnm_sleep_mode(wpa_s->wpa, true);
233
234
0
  wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
235
0
       wpa_s->bssid, NULL, NULL);
236
  /* remove GTK/IGTK ?? */
237
238
  /* set the TFS Resp IE(s) */
239
0
  if (tfsresp_ie_start && tfsresp_ie_end &&
240
0
      tfsresp_ie_end - tfsresp_ie_start >= 0) {
241
0
    u16 tfsresp_ie_len;
242
0
    tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
243
0
      tfsresp_ie_start;
244
0
    wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
245
    /* pass the TFS Resp IE(s) to driver for processing */
246
0
    if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
247
0
              tfsresp_ie_start,
248
0
              tfsresp_ie_len,
249
0
              WNM_SLEEP_TFS_RESP_IE_SET))
250
0
      wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
251
0
  }
252
0
}
253
254
255
static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
256
          const u8 *frm, u16 key_len_total)
257
0
{
258
0
  u8 *ptr, *end;
259
0
  u8 gtk_len;
260
261
0
  wpa_sm_notify_wnm_sleep_mode(wpa_s->wpa, false);
262
263
0
  wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,  wpa_s->bssid,
264
0
       NULL, NULL);
265
266
  /* Install GTK/IGTK */
267
268
  /* point to key data field */
269
0
  ptr = (u8 *) frm + 1 + 2;
270
0
  end = ptr + key_len_total;
271
0
  wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
272
273
0
  if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) {
274
0
    wpa_msg(wpa_s, MSG_INFO,
275
0
      "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled");
276
0
    return;
277
0
  }
278
279
0
  while (end - ptr > 1) {
280
0
    if (2 + ptr[1] > end - ptr) {
281
0
      wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
282
0
           "length");
283
0
      if (end > ptr) {
284
0
        wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
285
0
              ptr, end - ptr);
286
0
      }
287
0
      break;
288
0
    }
289
0
    if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
290
0
      if (ptr[1] < 11 + 5) {
291
0
        wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
292
0
             "subelem");
293
0
        break;
294
0
      }
295
0
      gtk_len = *(ptr + 4);
296
0
      if (ptr[1] < 11 + gtk_len ||
297
0
          gtk_len !=
298
0
          wpa_cipher_key_len(wpa_s->group_cipher)) {
299
0
        wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
300
0
             "subelem");
301
0
        break;
302
0
      }
303
0
      wpa_wnmsleep_install_key(
304
0
        wpa_s->wpa,
305
0
        WNM_SLEEP_SUBELEM_GTK,
306
0
        ptr);
307
0
    } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
308
0
      if (ptr[1] < 2 + 6 +
309
0
          wpa_cipher_key_len(wpa_s->mgmt_group_cipher)) {
310
0
        wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
311
0
             "subelem");
312
0
        break;
313
0
      }
314
0
      wpa_wnmsleep_install_key(wpa_s->wpa,
315
0
             WNM_SLEEP_SUBELEM_IGTK, ptr);
316
0
    } else if (*ptr == WNM_SLEEP_SUBELEM_BIGTK) {
317
0
      if (ptr[1] < 2 + 6 +
318
0
          wpa_cipher_key_len(wpa_s->mgmt_group_cipher)) {
319
0
        wpa_printf(MSG_DEBUG,
320
0
             "WNM: Too short BIGTK subelem");
321
0
        break;
322
0
      }
323
0
      wpa_wnmsleep_install_key(wpa_s->wpa,
324
0
             WNM_SLEEP_SUBELEM_BIGTK, ptr);
325
0
    } else
326
0
      break; /* skip the loop */
327
0
    ptr += 2 + ptr[1];
328
0
  }
329
0
}
330
331
332
static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
333
          const u8 *da, const u8 *sa,
334
          const u8 *frm, int len)
335
0
{
336
  /*
337
   * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
338
   * WNM-Sleep Mode IE | TFS Response IE
339
   */
340
0
  const u8 *pos = frm; /* point to payload after the action field */
341
0
  u16 key_len_total;
342
0
  struct wnm_sleep_element *wnmsleep_ie = NULL;
343
  /* multiple TFS Resp IE (assuming consecutive) */
344
0
  const u8 *tfsresp_ie_start = NULL;
345
0
  const u8 *tfsresp_ie_end = NULL;
346
#ifdef CONFIG_OCV
347
  const u8 *oci_ie = NULL;
348
  u8 oci_ie_len = 0;
349
#endif /* CONFIG_OCV */
350
0
  size_t left;
351
352
0
  if (wpa_s->wnmsleep_state != WNM_SLEEP_WAIT_RESP_ENTER &&
353
0
      wpa_s->wnmsleep_state != WNM_SLEEP_WAIT_RESP_EXIT) {
354
0
    wpa_printf(MSG_DEBUG,
355
0
         "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested");
356
0
    return;
357
0
  }
358
359
0
  if (is_multicast_ether_addr(da)) {
360
0
    wpa_printf(MSG_DEBUG,
361
0
         "WNM: Ignore group-addressed WNM-Sleep Mode Response frame (A1="
362
0
         MACSTR " A2=" MACSTR ")",
363
0
         MAC2STR(da), MAC2STR(sa));
364
0
    return;
365
0
  }
366
367
0
  if (len < 3)
368
0
    return;
369
0
  key_len_total = WPA_GET_LE16(frm + 1);
370
371
0
  wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
372
0
       frm[0], key_len_total);
373
0
  left = len - 3;
374
0
  if (key_len_total > left) {
375
0
    wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
376
0
    return;
377
0
  }
378
0
  pos += 3 + key_len_total;
379
0
  while (pos - frm + 1 < len) {
380
0
    u8 ie_len = *(pos + 1);
381
0
    if (2 + ie_len > frm + len - pos) {
382
0
      wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
383
0
      break;
384
0
    }
385
0
    wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
386
0
    if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4)
387
0
      wnmsleep_ie = (struct wnm_sleep_element *) pos;
388
0
    else if (*pos == WLAN_EID_TFS_RESP) {
389
0
      if (!tfsresp_ie_start)
390
0
        tfsresp_ie_start = pos;
391
0
      tfsresp_ie_end = pos;
392
#ifdef CONFIG_OCV
393
    } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
394
         pos[2] == WLAN_EID_EXT_OCV_OCI) {
395
      oci_ie = pos + 3;
396
      oci_ie_len = ie_len - 1;
397
#endif /* CONFIG_OCV */
398
0
    } else
399
0
      wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
400
0
    pos += ie_len + 2;
401
0
  }
402
403
0
  if (!wnmsleep_ie) {
404
0
    wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
405
0
    return;
406
0
  }
407
408
0
  if (!((wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
409
0
         wpa_s->wnmsleep_state == WNM_SLEEP_WAIT_RESP_ENTER) ||
410
0
        (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
411
0
         wpa_s->wnmsleep_state == WNM_SLEEP_WAIT_RESP_EXIT))) {
412
0
    wpa_printf(MSG_DEBUG,
413
0
         "WNM: Ignore WNM Sleep Mode Response frame with unexpected Action Type %u",
414
0
         wnmsleep_ie->action_type);
415
0
    return;
416
0
  }
417
418
#ifdef CONFIG_OCV
419
  if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
420
      wpa_sm_ocv_enabled(wpa_s->wpa)) {
421
    struct wpa_channel_info ci;
422
423
    if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
424
      wpa_msg(wpa_s, MSG_WARNING,
425
        "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
426
      return;
427
    }
428
429
    if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
430
           channel_width_to_int(ci.chanwidth),
431
           ci.seg1_idx) != OCI_SUCCESS) {
432
      wpa_msg(wpa_s, MSG_WARNING, "WNM: OCV failed: %s",
433
        ocv_errorstr);
434
      return;
435
    }
436
  }
437
#endif /* CONFIG_OCV */
438
439
0
  wpa_s->wnmsleep_state = WNM_SLEEP_IDLE;
440
441
0
  if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
442
0
      wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
443
0
    wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
444
0
         "frame (action=%d, intval=%d)",
445
0
         wnmsleep_ie->action_type, wnmsleep_ie->intval);
446
0
    if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
447
0
      wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
448
0
                 tfsresp_ie_end);
449
0
    } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
450
0
      wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
451
0
    }
452
0
  } else {
453
0
    wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
454
0
         "(action=%d, intval=%d)",
455
0
         wnmsleep_ie->action_type, wnmsleep_ie->intval);
456
0
    if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
457
0
      wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
458
0
           wpa_s->bssid, NULL, NULL);
459
0
    else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
460
0
      wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
461
0
           wpa_s->bssid, NULL, NULL);
462
0
  }
463
0
}
464
465
466
void wnm_sleep_mode_clear(struct wpa_supplicant *wpa_s)
467
0
{
468
0
  eloop_cancel_timeout(wnmsleep_mode_exit_timeout, wpa_s, NULL);
469
0
  wpa_s->wnmsleep_state = WNM_SLEEP_IDLE;
470
0
}
471
472
473
void wnm_btm_reset(struct wpa_supplicant *wpa_s)
474
0
{
475
0
  int i;
476
477
0
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
478
0
    os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
479
0
    os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
480
0
  }
481
482
0
  wpa_s->wnm_num_neighbor_report = 0;
483
0
  os_free(wpa_s->wnm_neighbor_report_elements);
484
0
  wpa_s->wnm_neighbor_report_elements = NULL;
485
486
0
  wpa_s->wnm_target_bss = NULL;
487
488
0
  wpa_s->wnm_cand_valid_until.sec = 0;
489
0
  wpa_s->wnm_cand_valid_until.usec = 0;
490
491
0
  wpa_s->wnm_mode = 0;
492
0
  wpa_s->wnm_dialog_token = 0;
493
0
  wpa_s->wnm_reply = 0;
494
495
0
#ifdef CONFIG_MBO
496
0
  wpa_s->wnm_mbo_trans_reason_present = 0;
497
0
  wpa_s->wnm_mbo_transition_reason = 0;
498
0
#endif /* CONFIG_MBO */
499
0
}
500
501
502
static void wnm_parse_neighbor_report_multi_link(struct neighbor_report *rep,
503
             u8 id, u8 elen, const u8 *pos)
504
0
{
505
0
  const struct ieee80211_eht_ml *ml =
506
0
    (const struct ieee80211_eht_ml *) pos;
507
0
  bool has_link_id;
508
0
  u8 common_info_len;
509
510
  /* The Basic Multi-Link subelement has the same body as the Basic MLE.
511
   * It includes at least the 2 octet Multi-Link Control field, 1 octet
512
   * Common Info Length, and the 6 oxtet MLD MAC Address fields. */
513
0
  if (elen < sizeof(*ml) + 1 + ETH_ALEN) {
514
0
    wpa_printf(MSG_DEBUG, "WNM: Too short ML element");
515
0
    return;
516
0
  }
517
518
  /* The ML control should be all zeroes except for the Link ID Info
519
   * Present field. */
520
0
  if ((le_to_host16(ml->ml_control) &
521
0
       ~BASIC_MULTI_LINK_CTRL_PRES_LINK_ID))
522
0
    wpa_printf(MSG_DEBUG,
523
0
         "WNM: Ignore unsupported ML Control field bits: 0x%04x",
524
0
         le_to_host16(ml->ml_control) &
525
0
         ~BASIC_MULTI_LINK_CTRL_PRES_LINK_ID);
526
527
0
  has_link_id = !!(le_to_host16(ml->ml_control) &
528
0
       BASIC_MULTI_LINK_CTRL_PRES_LINK_ID);
529
530
  /* Followed by the Common Info Length and the MLD MAC Address fields */
531
0
  common_info_len = pos[2];
532
0
  if (common_info_len < 1 + ETH_ALEN) {
533
0
    wpa_printf(MSG_DEBUG, "WNM: Too short ML Common Info: %u < 7",
534
0
         common_info_len);
535
0
    return;
536
0
  }
537
538
  /* MLD MAC Address */
539
0
  os_memcpy(rep->mld_addr, &pos[3], ETH_ALEN);
540
541
0
  if (!has_link_id)
542
0
    return;
543
544
0
  if (common_info_len < 1 + ETH_ALEN + 1 || common_info_len + 2 > elen) {
545
0
    wpa_printf(MSG_DEBUG,
546
0
         "WNM: ML Common Info too short or does not fit: %u (elen: %u)",
547
0
         common_info_len, elen);
548
0
    return;
549
0
  }
550
551
  /* Link ID Info */
552
0
  if ((pos[9] & EHT_ML_LINK_ID_MSK) >= MAX_NUM_MLD_LINKS) {
553
0
    wpa_printf(MSG_DEBUG,
554
0
         "WNM: ML common info contains invalid link ID");
555
0
    return;
556
0
  }
557
558
0
  rep->mld_links = BIT(pos[9] & EHT_ML_LINK_ID_MSK);
559
560
0
  elen -= common_info_len + 2;
561
0
  pos += common_info_len + 2;
562
563
  /* Parse out per-STA information */
564
0
  while (elen >= 2) {
565
0
    u8 sub_elem_len = pos[1];
566
567
0
    if (2 + sub_elem_len > elen) {
568
0
      wpa_printf(MSG_DEBUG,
569
0
           "WNM: Invalid sub-element length: %u %u",
570
0
           2 + sub_elem_len, elen);
571
0
      rep->mld_links = 0;
572
0
      break;
573
0
    }
574
575
0
    if  (*pos == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
576
0
      const struct ieee80211_eht_per_sta_profile *sta_prof =
577
0
        (const struct ieee80211_eht_per_sta_profile *)
578
0
        (pos + 2);
579
0
      u16 control;
580
0
      u8 link_id;
581
582
0
      if (sub_elem_len < sizeof(*sta_prof)) {
583
0
        wpa_printf(MSG_DEBUG,
584
0
             "WNM: Invalid STA-profile length: %u",
585
0
             sub_elem_len);
586
0
        rep->mld_links = 0;
587
0
        break;
588
0
      }
589
590
0
      control = le_to_host16(sta_prof->sta_control);
591
592
0
      link_id = control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
593
0
      if (link_id < MAX_NUM_MLD_LINKS)
594
0
        rep->mld_links |= BIT(link_id);
595
0
    }
596
597
0
    pos += 2 + sub_elem_len;
598
0
    elen -= 2 + sub_elem_len;
599
0
  }
600
601
0
  if (elen != 0) {
602
0
    wpa_printf(MSG_DEBUG,
603
0
         "WNM: Data left at end of multi-link element: %u",
604
0
         elen);
605
0
    rep->mld_links = 0;
606
0
  }
607
0
}
608
609
610
static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
611
             u8 id, u8 elen, const u8 *pos)
612
0
{
613
0
  switch (id) {
614
0
  case WNM_NEIGHBOR_TSF:
615
0
    if (elen < 2 + 2) {
616
0
      wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
617
0
      break;
618
0
    }
619
0
    rep->tsf_offset = WPA_GET_LE16(pos);
620
0
    rep->beacon_int = WPA_GET_LE16(pos + 2);
621
0
    rep->tsf_present = 1;
622
0
    break;
623
0
  case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
624
0
    if (elen < 2) {
625
0
      wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
626
0
           "country string");
627
0
      break;
628
0
    }
629
0
    os_memcpy(rep->country, pos, 2);
630
0
    rep->country_present = 1;
631
0
    break;
632
0
  case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
633
0
    if (elen < 1) {
634
0
      wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
635
0
           "candidate");
636
0
      break;
637
0
    }
638
0
    rep->preference = pos[0];
639
0
    rep->preference_present = 1;
640
0
    break;
641
0
  case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
642
0
    if (elen < 10) {
643
0
      wpa_printf(MSG_DEBUG,
644
0
           "WNM: Too short BSS termination duration");
645
0
      break;
646
0
    }
647
0
    rep->bss_term_tsf = WPA_GET_LE64(pos);
648
0
    rep->bss_term_dur = WPA_GET_LE16(pos + 8);
649
0
    rep->bss_term_present = 1;
650
0
    break;
651
0
  case WNM_NEIGHBOR_BEARING:
652
0
    if (elen < 8) {
653
0
      wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
654
0
           "bearing");
655
0
      break;
656
0
    }
657
0
    rep->bearing = WPA_GET_LE16(pos);
658
0
    rep->distance = WPA_GET_LE32(pos + 2);
659
0
    rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
660
0
    rep->bearing_present = 1;
661
0
    break;
662
0
  case WNM_NEIGHBOR_MEASUREMENT_PILOT:
663
0
    if (elen < 1) {
664
0
      wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
665
0
           "pilot");
666
0
      break;
667
0
    }
668
0
    os_free(rep->meas_pilot);
669
0
    rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
670
0
    if (rep->meas_pilot == NULL)
671
0
      break;
672
0
    rep->meas_pilot->measurement_pilot = pos[0];
673
0
    rep->meas_pilot->subelem_len = elen - 1;
674
0
    os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
675
0
    break;
676
0
  case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
677
0
    if (elen < 5) {
678
0
      wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
679
0
           "capabilities");
680
0
      break;
681
0
    }
682
0
    os_memcpy(rep->rm_capab, pos, 5);
683
0
    rep->rm_capab_present = 1;
684
0
    break;
685
0
  case WNM_NEIGHBOR_MULTIPLE_BSSID:
686
0
    if (elen < 1) {
687
0
      wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
688
0
      break;
689
0
    }
690
0
    os_free(rep->mul_bssid);
691
0
    rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
692
0
    if (rep->mul_bssid == NULL)
693
0
      break;
694
0
    rep->mul_bssid->max_bssid_indicator = pos[0];
695
0
    rep->mul_bssid->subelem_len = elen - 1;
696
0
    os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
697
0
    break;
698
0
  case WNM_NEIGHBOR_MULTI_LINK:
699
0
    wnm_parse_neighbor_report_multi_link(rep, id, elen, pos);
700
0
    break;
701
0
  default:
702
0
    wpa_printf(MSG_DEBUG,
703
0
         "WNM: Unsupported neighbor report subelement id %u",
704
0
         id);
705
0
    break;
706
0
  }
707
0
}
708
709
710
static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
711
0
{
712
0
  struct wpa_bss *bss = wpa_s->current_bss;
713
0
  const char *country = NULL;
714
0
  int freq;
715
716
0
  if (bss) {
717
0
    const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
718
719
0
    if (elem && elem[1] >= 2)
720
0
      country = (const char *) (elem + 2);
721
0
  }
722
723
0
  freq = ieee80211_chan_to_freq(country, op_class, chan);
724
0
  if (freq <= 0 && (op_class == 0 || op_class == 255)) {
725
    /*
726
     * Some APs do not advertise correct operating class
727
     * information. Try to determine the most likely operating
728
     * frequency based on the channel number.
729
     */
730
0
    if (chan >= 1 && chan <= 13)
731
0
      freq = 2407 + chan * 5;
732
0
    else if (chan == 14)
733
0
      freq = 2484;
734
0
    else if (chan >= 36 && chan <= 177)
735
0
      freq = 5000 + chan * 5;
736
0
  }
737
0
  return freq;
738
0
}
739
740
741
static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
742
              const u8 *pos, u8 len,
743
              struct neighbor_report *rep)
744
0
{
745
0
  u8 left = len;
746
747
0
  if (left < 13) {
748
0
    wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
749
0
    return;
750
0
  }
751
752
0
  os_memcpy(rep->bssid, pos, ETH_ALEN);
753
0
  rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
754
0
  rep->regulatory_class = *(pos + 10);
755
0
  rep->channel_number = *(pos + 11);
756
0
  rep->phy_type = *(pos + 12);
757
758
0
  pos += 13;
759
0
  left -= 13;
760
761
0
  while (left >= 2) {
762
0
    u8 id, elen;
763
764
0
    id = *pos++;
765
0
    elen = *pos++;
766
0
    wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
767
0
    left -= 2;
768
0
    if (elen > left) {
769
0
      wpa_printf(MSG_DEBUG,
770
0
           "WNM: Truncated neighbor report subelement");
771
0
      break;
772
0
    }
773
0
    wnm_parse_neighbor_report_elem(rep, id, elen, pos);
774
0
    left -= elen;
775
0
    pos += elen;
776
0
  }
777
778
0
  rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
779
0
             rep->channel_number);
780
0
}
781
782
783
static void
784
fetch_drv_mbo_candidate_info(struct wpa_supplicant *wpa_s,
785
           enum mbo_transition_reject_reason *reason)
786
0
{
787
0
#ifdef CONFIG_MBO
788
0
  struct wpa_bss_trans_info params;
789
0
  struct wpa_bss_candidate_info *info = NULL;
790
0
  struct neighbor_report *nei;
791
0
  u8 *pos;
792
0
  unsigned int i;
793
794
0
  if (!wpa_s->wnm_mbo_trans_reason_present)
795
0
    return;
796
797
0
  params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
798
0
  params.n_candidates = 0;
799
0
  params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
800
0
  if (!params.bssid)
801
0
    return;
802
803
0
  pos = params.bssid;
804
0
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
805
0
    nei = &wpa_s->wnm_neighbor_report_elements[i];
806
807
0
    nei->drv_mbo_reject = 0;
808
809
0
    if (nei->preference_present && nei->preference == 0)
810
0
      continue;
811
812
    /* Should we query BSSIDs that we reject for other reasons? */
813
814
0
    os_memcpy(pos, nei->bssid, ETH_ALEN);
815
0
    pos += ETH_ALEN;
816
0
    params.n_candidates++;
817
0
  }
818
819
0
  if (!params.n_candidates)
820
0
    goto end;
821
822
0
  info = wpa_drv_get_bss_trans_status(wpa_s, &params);
823
0
  if (!info)
824
0
    goto end;
825
826
0
  for (i = 0; i < info->num; i++) {
827
0
    int j;
828
829
0
    for (j = 0; j < wpa_s->wnm_num_neighbor_report; j++) {
830
0
      nei = &wpa_s->wnm_neighbor_report_elements[j];
831
832
0
      if (!ether_addr_equal(info->candidates[i].bssid,
833
0
                nei->bssid))
834
0
        continue;
835
836
0
      nei->drv_mbo_reject = !info->candidates[i].is_accept;
837
838
      /* Use the reject reason from the first candidate */
839
0
      if (i == 0 && nei->drv_mbo_reject)
840
0
        *reason = info->candidates[i].reject_reason;
841
842
0
      break;
843
0
    }
844
0
  }
845
846
0
end:
847
0
  os_free(params.bssid);
848
0
  if (info) {
849
0
    os_free(info->candidates);
850
0
    os_free(info);
851
0
  }
852
0
#endif /* CONFIG_MBO */
853
0
}
854
855
856
static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
857
0
{
858
0
  const u8 *ie_a, *ie_b;
859
860
0
  if (!a || !b)
861
0
    return 0;
862
863
0
  ie_a = wpa_bss_get_ie(a, eid);
864
0
  ie_b = wpa_bss_get_ie(b, eid);
865
866
0
  if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
867
0
    return 0;
868
869
0
  return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
870
0
}
871
872
873
static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
874
0
{
875
0
  u32 info = 0;
876
877
0
  info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
878
879
  /*
880
   * Leave the security and key scope bits unset to indicate that the
881
   * security information is not available.
882
   */
883
884
0
  if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
885
0
    info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
886
0
  if (bss->caps & WLAN_CAPABILITY_QOS)
887
0
    info |= NEI_REP_BSSID_INFO_QOS;
888
0
  if (bss->caps & WLAN_CAPABILITY_APSD)
889
0
    info |= NEI_REP_BSSID_INFO_APSD;
890
0
  if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
891
0
    info |= NEI_REP_BSSID_INFO_RM;
892
0
  if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
893
0
    info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
894
0
  if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
895
0
    info |= NEI_REP_BSSID_INFO_HT;
896
897
0
  return info;
898
0
}
899
900
901
static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
902
         u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
903
         u8 pref)
904
0
{
905
0
  if (wpabuf_len(*buf) + 18 >
906
0
      IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
907
0
    wpa_printf(MSG_DEBUG,
908
0
         "WNM: No room in frame for Neighbor Report element");
909
0
    return -1;
910
0
  }
911
912
0
  if (wpabuf_resize(buf, 18) < 0) {
913
0
    wpa_printf(MSG_DEBUG,
914
0
         "WNM: Failed to allocate memory for Neighbor Report element");
915
0
    return -1;
916
0
  }
917
918
0
  wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
919
  /* length: 13 for basic neighbor report + 3 for preference subelement */
920
0
  wpabuf_put_u8(*buf, 16);
921
0
  wpabuf_put_data(*buf, bssid, ETH_ALEN);
922
0
  wpabuf_put_le32(*buf, bss_info);
923
0
  wpabuf_put_u8(*buf, op_class);
924
0
  wpabuf_put_u8(*buf, chan);
925
0
  wpabuf_put_u8(*buf, phy_type);
926
0
  wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
927
0
  wpabuf_put_u8(*buf, 1);
928
0
  wpabuf_put_u8(*buf, pref);
929
0
  return 0;
930
0
}
931
932
933
static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
934
             struct wpa_bss *bss, struct wpabuf **buf,
935
             u8 pref)
936
0
{
937
0
  u8 op_class, chan;
938
0
  int sec_chan = 0, chanwidth = 0;
939
0
  struct ieee802_11_elems elems;
940
0
  struct ieee80211_ht_operation *ht_oper;
941
0
  enum phy_type phy_type;
942
0
  u32 info;
943
944
0
  if (ieee802_11_parse_elems(wpa_bss_ie_ptr(bss), bss->ie_len, &elems,
945
0
           1) == ParseFailed)
946
0
    return -2;
947
948
0
  chanwidth = get_operation_channel_width(&elems);
949
0
  if (chanwidth == CHAN_WIDTH_UNKNOWN) {
950
0
    wpa_printf(MSG_DEBUG, "Cannot determine channel width");
951
0
    return -2;
952
0
  }
953
0
  ht_oper = (struct ieee80211_ht_operation *) elems.ht_operation;
954
0
  if (ht_oper) {
955
0
    u8 sec_chan_offset = ht_oper->ht_param &
956
0
      HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
957
958
0
    if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
959
0
      sec_chan = 1;
960
0
    else if (sec_chan_offset ==
961
0
       HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
962
0
      sec_chan = -1;
963
0
  }
964
965
0
  if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, chanwidth,
966
0
            &op_class, &chan) ==
967
0
      NUM_HOSTAPD_MODES) {
968
0
    wpa_printf(MSG_DEBUG,
969
0
         "WNM: Cannot determine operating class and channel");
970
0
    return -2;
971
0
  }
972
973
0
  phy_type = ieee80211_get_phy_type(bss->freq, elems.ht_operation != NULL,
974
0
            elems.vht_operation != NULL,
975
0
            elems.he_operation != NULL);
976
0
  if (phy_type == PHY_TYPE_UNSPECIFIED) {
977
0
    wpa_printf(MSG_DEBUG,
978
0
         "WNM: Cannot determine BSS phy type for Neighbor Report");
979
0
    return -2;
980
0
  }
981
982
0
  info = wnm_get_bss_info(wpa_s, bss);
983
984
0
  return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type,
985
0
             pref);
986
0
}
987
988
989
static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
990
0
{
991
0
  unsigned int i, pref = 255;
992
0
  struct os_reltime now;
993
0
  struct wpa_ssid *ssid = wpa_s->current_ssid;
994
995
0
  if (!ssid)
996
0
    return;
997
998
  /*
999
   * TODO: Define when scan results are no longer valid for the candidate
1000
   * list.
1001
   */
1002
0
  os_get_reltime(&now);
1003
0
  if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
1004
0
    return;
1005
1006
0
  wpa_printf(MSG_DEBUG,
1007
0
       "WNM: Add candidate list to BSS Transition Management Response frame");
1008
0
  for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
1009
0
    struct wpa_bss *bss = wpa_s->last_scan_res[i];
1010
0
    int res;
1011
1012
0
    if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0, false)) {
1013
0
      res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
1014
0
      if (res == -2)
1015
0
        continue; /* could not build entry for BSS */
1016
0
      if (res < 0)
1017
0
        break; /* no more room for candidates */
1018
0
      if (pref == 1)
1019
0
        break;
1020
0
    }
1021
0
  }
1022
1023
0
  wpa_hexdump_buf(MSG_DEBUG,
1024
0
      "WNM: BSS Transition Management Response candidate list",
1025
0
      *buf);
1026
0
}
1027
1028
1029
0
#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
1030
1031
static int wnm_send_bss_transition_mgmt_resp(
1032
  struct wpa_supplicant *wpa_s,
1033
  enum bss_trans_mgmt_status_code status,
1034
  enum mbo_transition_reject_reason reason,
1035
  u8 delay, const u8 *target_bssid)
1036
0
{
1037
0
  struct wpabuf *buf;
1038
0
  int res;
1039
1040
0
  wpa_s->wnm_reply = 0;
1041
1042
0
  wpa_printf(MSG_DEBUG,
1043
0
       "WNM: Send BSS Transition Management Response to " MACSTR
1044
0
       " dialog_token=%u status=%u reason=%u delay=%d",
1045
0
       MAC2STR(wpa_s->bssid), wpa_s->wnm_dialog_token, status,
1046
0
       reason, delay);
1047
0
  if (!wpa_s->current_bss) {
1048
0
    wpa_printf(MSG_DEBUG,
1049
0
         "WNM: Current BSS not known - drop response");
1050
0
    return -1;
1051
0
  }
1052
1053
0
  buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
1054
0
  if (!buf) {
1055
0
    wpa_printf(MSG_DEBUG,
1056
0
         "WNM: Failed to allocate memory for BTM response");
1057
0
    return -1;
1058
0
  }
1059
1060
0
  wpa_s->bss_tm_status = status;
1061
0
  wpas_notify_bss_tm_status(wpa_s);
1062
1063
0
  wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1064
0
  wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
1065
0
  wpabuf_put_u8(buf, wpa_s->wnm_dialog_token);
1066
0
  wpabuf_put_u8(buf, status);
1067
0
  wpabuf_put_u8(buf, delay);
1068
0
  if (target_bssid) {
1069
0
    wpabuf_put_data(buf, target_bssid, ETH_ALEN);
1070
0
  } else if (status == WNM_BSS_TM_ACCEPT) {
1071
    /*
1072
     * IEEE Std 802.11-2024, 9.6.13.10 (BSS Transition Management
1073
     * Response frame format) clarifies that the Target BSSID field
1074
     * is always present when status code is zero, so use a fake
1075
     * value here if no BSSID is yet known.
1076
     */
1077
0
    wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
1078
0
  }
1079
1080
0
  if (status == WNM_BSS_TM_ACCEPT && target_bssid)
1081
0
    wnm_add_cand_list(wpa_s, &buf);
1082
1083
0
#ifdef CONFIG_MBO
1084
0
  if (status != WNM_BSS_TM_ACCEPT &&
1085
0
      wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
1086
0
    u8 mbo[10];
1087
0
    size_t ret;
1088
1089
0
    ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
1090
0
               reason);
1091
0
    if (ret) {
1092
0
      if (wpabuf_resize(&buf, ret) < 0) {
1093
0
        wpabuf_free(buf);
1094
0
        wpa_printf(MSG_DEBUG,
1095
0
             "WNM: Failed to allocate memory for MBO IE");
1096
0
        return -1;
1097
0
      }
1098
1099
0
      wpabuf_put_data(buf, mbo, ret);
1100
0
    }
1101
0
  }
1102
0
#endif /* CONFIG_MBO */
1103
1104
0
  res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1105
0
          wpa_s->own_addr, wpa_s->bssid,
1106
0
          wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1107
0
  if (res < 0) {
1108
0
    wpa_printf(MSG_DEBUG,
1109
0
         "WNM: Failed to send BSS Transition Management Response");
1110
0
  }
1111
1112
0
  wpabuf_free(buf);
1113
1114
0
  return res;
1115
0
}
1116
1117
1118
static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
1119
             struct wpa_bss *bss, struct wpa_ssid *ssid,
1120
             int after_new_scan)
1121
0
{
1122
0
  struct wpa_radio_work *already_connecting;
1123
1124
0
  wpa_dbg(wpa_s, MSG_DEBUG,
1125
0
    "WNM: Transition to BSS " MACSTR
1126
0
    " based on BSS Transition Management Request (old BSSID "
1127
0
    MACSTR " after_new_scan=%d)",
1128
0
    MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan);
1129
1130
  /* Send the BSS Management Response - Accept */
1131
0
  if (wpa_s->wnm_reply) {
1132
0
    wpa_s->wnm_target_bss = bss;
1133
0
    wpa_printf(MSG_DEBUG,
1134
0
         "WNM: Sending successful BSS Transition Management Response");
1135
1136
    /* This function will be called again from the TX handler to
1137
     * start the actual reassociation after this response has been
1138
     * delivered to the current AP. */
1139
0
    if (wnm_send_bss_transition_mgmt_resp(
1140
0
          wpa_s, WNM_BSS_TM_ACCEPT,
1141
0
          MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
1142
0
          bss->bssid) >= 0)
1143
0
      return;
1144
0
  }
1145
1146
0
  if (bss == wpa_s->current_bss) {
1147
0
    wpa_printf(MSG_DEBUG,
1148
0
         "WNM: Already associated with the preferred candidate");
1149
0
    wnm_btm_reset(wpa_s);
1150
0
    return;
1151
0
  }
1152
1153
0
  already_connecting = radio_work_pending(wpa_s, "sme-connect");
1154
0
  wpa_s->reassociate = 1;
1155
0
  wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
1156
0
  wpa_supplicant_connect(wpa_s, bss, ssid);
1157
1158
  /*
1159
   * Indicate that a BSS transition is in progress so scan results that
1160
   * come in before the 'sme-connect' radio work gets executed do not
1161
   * override the original connection attempt.
1162
   */
1163
0
  if (!already_connecting && radio_work_pending(wpa_s, "sme-connect"))
1164
0
    wpa_s->bss_trans_mgmt_in_progress = true;
1165
0
}
1166
1167
1168
int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
1169
0
{
1170
0
  struct wpa_bss *bss, *current_bss = wpa_s->current_bss;
1171
0
  struct wpa_ssid *ssid = wpa_s->current_ssid;
1172
0
  enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1173
0
  enum mbo_transition_reject_reason reason =
1174
0
    MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
1175
0
  struct wpa_ssid *selected_ssid = NULL;
1176
1177
0
  if (!ssid || !wpa_s->wnm_dialog_token)
1178
0
    return 0;
1179
1180
0
  wpa_dbg(wpa_s, MSG_DEBUG,
1181
0
    "WNM: Process scan results for BSS Transition Management");
1182
0
  if (!pre_scan_check &&
1183
0
      os_reltime_initialized(&wpa_s->wnm_cand_valid_until) &&
1184
0
      os_reltime_before(&wpa_s->wnm_cand_valid_until,
1185
0
            &wpa_s->scan_trigger_time)) {
1186
0
    wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
1187
0
    goto send_bss_resp_fail;
1188
0
  }
1189
1190
0
  if (!pre_scan_check && !wpa_s->wnm_transition_scan)
1191
0
    return 0;
1192
1193
0
  wpa_s->wnm_transition_scan = false;
1194
1195
  /* Fetch MBO transition candidate rejection information from driver */
1196
0
  fetch_drv_mbo_candidate_info(wpa_s, &reason);
1197
1198
  /* Compare the Neighbor Report and scan results */
1199
0
  bss = wpa_supplicant_select_bss(wpa_s, ssid, &selected_ssid, 1);
1200
0
#ifdef CONFIG_MBO
1201
0
  if (!bss && wpa_s->wnm_mbo_trans_reason_present &&
1202
0
      (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT)) {
1203
0
    int i;
1204
0
    bool changed = false;
1205
1206
    /*
1207
     * We didn't find any candidate, the driver had a say about
1208
     * which targets to reject and disassociation is immiment.
1209
     *
1210
     * We should still try to roam, so retry after ignoring the
1211
     * driver reject for any BSS that has an RSSI better than
1212
     * disassoc_imminent_rssi_threshold.
1213
     */
1214
0
    for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1215
0
      struct neighbor_report *nei;
1216
1217
0
      nei = &wpa_s->wnm_neighbor_report_elements[i];
1218
0
      bss = wpa_bss_get_bssid(wpa_s, nei->bssid);
1219
0
      if (bss && bss->level >
1220
0
          wpa_s->conf->disassoc_imminent_rssi_threshold) {
1221
0
        nei->drv_mbo_reject = 0;
1222
0
        changed = true;
1223
0
      }
1224
0
    }
1225
1226
0
    if (changed) {
1227
0
      wpa_printf(MSG_DEBUG,
1228
0
           "WNM: Ignore driver rejection due to imminent disassociation and acceptable RSSI");
1229
0
      bss = wpa_supplicant_select_bss(wpa_s, ssid,
1230
0
              &selected_ssid, 1);
1231
0
    }
1232
0
  }
1233
0
#endif /* CONFIG_MBO */
1234
1235
  /*
1236
   * If this is a pre-scan check, returning 0 will trigger a scan and
1237
   * another call. In that case, reject "bad" candidates in the hope of
1238
   * finding a better candidate after scanning.
1239
   *
1240
   * Use a simple heuristic to check whether the selection is reasonable
1241
   * or a scan is a good idea. For that, we need to have found a
1242
   * candidate BSS (which might be the current one), it is up-to-date,
1243
   * and we don't want to immediately roam back again.
1244
   */
1245
0
  if (pre_scan_check) {
1246
0
    struct os_reltime age;
1247
1248
0
    if (!bss)
1249
0
      return 0;
1250
1251
0
    os_reltime_age(&bss->last_update, &age);
1252
0
    if (age.sec >= 10)
1253
0
      return 0;
1254
1255
0
#ifndef CONFIG_NO_ROAMING
1256
0
    if (current_bss && bss != current_bss &&
1257
0
        wpa_supplicant_need_to_roam_within_ess(wpa_s, bss,
1258
0
                 current_bss, false))
1259
0
      return 0;
1260
0
#endif /* CONFIG_NO_ROAMING */
1261
0
  }
1262
1263
0
#ifndef CONFIG_NO_ROAMING
1264
  /* Apply normal roaming rules if we can stay with the current BSS */
1265
0
  if (current_bss && bss && bss != current_bss &&
1266
0
      wpa_scan_res_match(wpa_s, 0, current_bss, wpa_s->current_ssid,
1267
0
             1, 0, false) &&
1268
0
      !wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss, bss,
1269
0
                true))
1270
0
    bss = current_bss;
1271
0
#endif /* CONFIG_NO_ROAMING */
1272
1273
0
  if (!bss) {
1274
0
    wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
1275
0
    status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
1276
0
    goto send_bss_resp_fail;
1277
0
  }
1278
1279
0
  wpa_printf(MSG_DEBUG,
1280
0
       "WNM: Found an acceptable preferred transition candidate BSS "
1281
0
       MACSTR " (RSSI %d, tput: %d  bss-tput: %d)",
1282
0
       MAC2STR(bss->bssid), bss->level, bss->est_throughput,
1283
0
       current_bss ? (int) current_bss->est_throughput : -1);
1284
1285
  /* Associate to the network */
1286
0
  wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
1287
0
  return 1;
1288
1289
0
send_bss_resp_fail:
1290
0
  if (wpa_s->wnm_reply) {
1291
    /* If disassoc imminent is set, we must not reject */
1292
0
    if (wpa_s->wnm_mode &
1293
0
        (WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
1294
0
         WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)) {
1295
0
      wpa_printf(MSG_DEBUG,
1296
0
           "WNM: Accept BTM request because disassociation imminent bit is set");
1297
0
      status = WNM_BSS_TM_ACCEPT;
1298
0
    }
1299
1300
0
    wnm_send_bss_transition_mgmt_resp(wpa_s, status, reason,
1301
0
              0, NULL);
1302
0
  }
1303
1304
0
  wnm_btm_reset(wpa_s);
1305
1306
0
  return 1;
1307
0
}
1308
1309
1310
static int cand_pref_compar(const void *a, const void *b)
1311
0
{
1312
0
  const struct neighbor_report *aa = a;
1313
0
  const struct neighbor_report *bb = b;
1314
1315
0
  if (aa->disassoc_imminent && !bb->disassoc_imminent)
1316
0
    return 1;
1317
0
  if (bb->disassoc_imminent && !aa->disassoc_imminent)
1318
0
    return -1;
1319
1320
0
  if (!aa->preference_present && !bb->preference_present)
1321
0
    return 0;
1322
0
  if (!aa->preference_present)
1323
0
    return 1;
1324
0
  if (!bb->preference_present)
1325
0
    return -1;
1326
0
  if (bb->preference > aa->preference)
1327
0
    return 1;
1328
0
  if (bb->preference < aa->preference)
1329
0
    return -1;
1330
0
  return 0;
1331
0
}
1332
1333
1334
static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
1335
0
{
1336
0
  if (!wpa_s->wnm_neighbor_report_elements)
1337
0
    return;
1338
0
  qsort(wpa_s->wnm_neighbor_report_elements,
1339
0
        wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
1340
0
        cand_pref_compar);
1341
0
}
1342
1343
1344
static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
1345
0
{
1346
0
  unsigned int i;
1347
1348
0
  wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
1349
0
  if (!wpa_s->wnm_neighbor_report_elements)
1350
0
    return;
1351
0
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1352
0
    struct neighbor_report *nei;
1353
0
    char mld_info[42] = "";
1354
1355
0
    nei = &wpa_s->wnm_neighbor_report_elements[i];
1356
1357
0
    if (!is_zero_ether_addr(nei->mld_addr))
1358
0
      os_snprintf(mld_info, sizeof(mld_info) - 1,
1359
0
            " mld_addr=" MACSTR " links=0x%02x",
1360
0
            MAC2STR(nei->mld_addr), nei->mld_links);
1361
1362
0
    wpa_printf(MSG_DEBUG, "%u: " MACSTR
1363
0
         " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d%s",
1364
0
         i, MAC2STR(nei->bssid), nei->bssid_info,
1365
0
         nei->regulatory_class,
1366
0
         nei->channel_number, nei->phy_type,
1367
0
         nei->preference_present ? nei->preference : -1,
1368
0
         nei->freq, mld_info);
1369
0
  }
1370
0
}
1371
1372
1373
static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
1374
0
{
1375
0
  unsigned int i;
1376
1377
0
  for (i = 0; i < wpa_s->hw.num_modes; i++) {
1378
0
    struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
1379
0
    int j;
1380
1381
0
    for (j = 0; j < mode->num_channels; j++) {
1382
0
      struct hostapd_channel_data *chan;
1383
1384
0
      chan = &mode->channels[j];
1385
0
      if (chan->freq == freq &&
1386
0
          !(chan->flag & HOSTAPD_CHAN_DISABLED))
1387
0
        return 1;
1388
0
    }
1389
0
  }
1390
1391
0
  return 0;
1392
0
}
1393
1394
1395
static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
1396
0
{
1397
0
  unsigned int i;
1398
0
  bool has_6ghz = false;
1399
1400
0
  if (!wpa_s->wnm_neighbor_report_elements)
1401
0
    return;
1402
1403
0
  if (wpa_s->hw.modes == NULL)
1404
0
    return;
1405
1406
0
  os_free(wpa_s->next_scan_freqs);
1407
0
  wpa_s->next_scan_freqs = NULL;
1408
1409
0
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1410
0
    struct neighbor_report *nei;
1411
1412
0
    nei = &wpa_s->wnm_neighbor_report_elements[i];
1413
1414
0
    if (nei->preference_present && nei->preference == 0)
1415
0
      continue;
1416
1417
0
    if (nei->freq <= 0) {
1418
0
      wpa_printf(MSG_DEBUG,
1419
0
           "WNM: Unknown neighbor operating frequency for "
1420
0
           MACSTR " - scan all channels",
1421
0
           MAC2STR(nei->bssid));
1422
0
      os_free(wpa_s->next_scan_freqs);
1423
0
      wpa_s->next_scan_freqs = NULL;
1424
0
      return;
1425
0
    }
1426
0
    if (chan_supported(wpa_s, nei->freq))
1427
0
      int_array_add_unique(&wpa_s->next_scan_freqs,
1428
0
               nei->freq);
1429
0
    has_6ghz |= is_6ghz_freq(nei->freq);
1430
0
  }
1431
1432
0
  if (!wpa_s->next_scan_freqs)
1433
0
    return;
1434
1435
0
  wpa_printf(MSG_DEBUG,
1436
0
       "WNM: Scan %zu frequencies based on transition candidate list",
1437
0
       int_array_len(wpa_s->next_scan_freqs));
1438
1439
  /*
1440
   * Candidates on 6 GHz channels might be collocated ones, and thus, in
1441
   * order to discover them need to include the frequencies on the 2.4
1442
   * and 5 GHz bands. Since the scan time can be long, optimize the case
1443
   * of a single channel by forcing passive scan instead of doing a
1444
   * collocated scan.
1445
   */
1446
0
  if (has_6ghz) {
1447
0
    struct hostapd_channel_data *chan;
1448
0
    struct os_reltime now;
1449
1450
    /* In case the candidate validity time is too short, force it
1451
     * to be long enough to account for the longer scan time.
1452
     */
1453
0
    os_get_reltime(&now);
1454
0
    now.sec += 5;
1455
1456
0
    if (os_reltime_initialized(&wpa_s->wnm_cand_valid_until) &&
1457
0
        !os_reltime_before(&wpa_s->wnm_cand_valid_until, &now)) {
1458
0
      wpa_printf(MSG_DEBUG,
1459
0
           "WNM: Scan: 6 GHz: update validity time");
1460
1461
0
      os_memcpy(&wpa_s->wnm_cand_valid_until, &now,
1462
0
          sizeof(now));
1463
0
    }
1464
1465
0
    if (int_array_len(wpa_s->next_scan_freqs) == 1) {
1466
0
      wpa_printf(MSG_DEBUG,
1467
0
           "WNM: Scan: Single 6 GHz channel: passive");
1468
1469
0
      wpa_s->scan_req = MANUAL_SCAN_REQ;
1470
0
      wpa_s->manual_scan_passive = 1;
1471
0
      return;
1472
0
    }
1473
1474
0
    wpa_printf(MSG_DEBUG,
1475
0
         "WNM: Scan: Add 2.4/5 GHz channels as well for 6 GHz discovery");
1476
1477
0
    for (i = 0; i < wpa_s->hw.num_modes; i++) {
1478
0
      struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
1479
0
      int j;
1480
1481
      /* skip all the irrelevant modes */
1482
0
      if ((mode->mode != HOSTAPD_MODE_IEEE80211B &&
1483
0
           mode->mode != HOSTAPD_MODE_IEEE80211G &&
1484
0
           mode->mode != HOSTAPD_MODE_IEEE80211A) ||
1485
0
          mode->is_6ghz)
1486
0
        continue;
1487
1488
0
      for (j = 0; j < mode->num_channels; j++) {
1489
0
        chan = &mode->channels[j];
1490
0
        if (chan->flag & HOSTAPD_CHAN_DISABLED)
1491
0
          continue;
1492
1493
0
        int_array_add_unique(&wpa_s->next_scan_freqs,
1494
0
                 chan->freq);
1495
0
      }
1496
0
    }
1497
1498
0
    wpa_printf(MSG_DEBUG,
1499
0
         "WNM: Scan %zu frequencies (including collocated)",
1500
0
         int_array_len(wpa_s->next_scan_freqs));
1501
0
  }
1502
0
}
1503
1504
1505
static int wnm_parse_candidate_list(struct wpa_supplicant *wpa_s,
1506
            const u8 *pos, const u8 *end,
1507
            int *num_valid_candidates)
1508
0
{
1509
0
  *num_valid_candidates = 0;
1510
1511
0
  while (end - pos >= 2 &&
1512
0
         wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) {
1513
0
    u8 tag = *pos++;
1514
0
    u8 len = *pos++;
1515
1516
0
    wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag);
1517
0
    if (len > end - pos) {
1518
0
      wpa_printf(MSG_DEBUG, "WNM: Truncated request");
1519
0
      return -1;
1520
0
    }
1521
0
    if (tag == WLAN_EID_NEIGHBOR_REPORT) {
1522
0
      struct neighbor_report *rep;
1523
1524
0
      if (!wpa_s->wnm_num_neighbor_report) {
1525
0
        wpa_s->wnm_neighbor_report_elements = os_calloc(
1526
0
          WNM_MAX_NEIGHBOR_REPORT,
1527
0
          sizeof(struct neighbor_report));
1528
0
        if (!wpa_s->wnm_neighbor_report_elements)
1529
0
          return -1;
1530
0
      }
1531
1532
0
      rep = &wpa_s->wnm_neighbor_report_elements[
1533
0
        wpa_s->wnm_num_neighbor_report];
1534
0
      wnm_parse_neighbor_report(wpa_s, pos, len, rep);
1535
0
      if ((wpa_s->wnm_mode &
1536
0
           WNM_BSS_TM_REQ_DISASSOC_IMMINENT) &&
1537
0
          ether_addr_equal(rep->bssid, wpa_s->bssid))
1538
0
        rep->disassoc_imminent = 1;
1539
1540
0
      if (rep->preference_present && rep->preference)
1541
0
        *num_valid_candidates += 1;
1542
1543
0
      wpa_s->wnm_num_neighbor_report++;
1544
0
    }
1545
1546
0
    pos += len;
1547
0
  }
1548
1549
0
  return 0;
1550
0
}
1551
1552
1553
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
1554
               const u8 *pos, const u8 *end,
1555
               int reply)
1556
0
{
1557
0
  unsigned int beacon_int;
1558
0
  u8 valid_int;
1559
0
#ifdef CONFIG_MBO
1560
0
  const u8 *vendor;
1561
0
#endif /* CONFIG_MBO */
1562
0
  bool disassoc_imminent;
1563
0
  int num_valid_candidates;
1564
1565
0
  if (wpa_s->disable_mbo_oce || wpa_s->conf->disable_btm)
1566
0
    return;
1567
1568
0
  if (end - pos < 5)
1569
0
    return;
1570
1571
0
  if (wpa_s->current_bss)
1572
0
    beacon_int = wpa_s->current_bss->beacon_int;
1573
0
  else
1574
0
    beacon_int = 100; /* best guess */
1575
1576
0
  wnm_btm_reset(wpa_s);
1577
1578
0
  wpa_s->wnm_dialog_token = pos[0];
1579
0
  wpa_s->wnm_mode = pos[1];
1580
0
  wpa_s->wnm_disassoc_timer = WPA_GET_LE16(pos + 2);
1581
0
  wpa_s->wnm_link_removal = false;
1582
0
  valid_int = pos[4];
1583
0
  wpa_s->wnm_reply = reply;
1584
1585
0
  wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
1586
0
       "dialog_token=%u request_mode=0x%x "
1587
0
       "disassoc_timer=%u validity_interval=%u",
1588
0
       wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
1589
0
       wpa_s->wnm_disassoc_timer, valid_int);
1590
1591
0
  if (!wpa_s->wnm_dialog_token) {
1592
0
    wpa_printf(MSG_DEBUG, "WNM: Invalid dialog token");
1593
0
    goto reset;
1594
0
  }
1595
1596
#if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
1597
  if (wpa_s->reject_btm_req_reason) {
1598
    wpa_printf(MSG_INFO,
1599
         "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
1600
         wpa_s->reject_btm_req_reason);
1601
    wnm_send_bss_transition_mgmt_resp(
1602
      wpa_s, wpa_s->reject_btm_req_reason,
1603
      MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
1604
    goto reset;
1605
  }
1606
#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
1607
1608
0
  pos += 5;
1609
1610
0
  if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
1611
0
    if (end - pos < 12) {
1612
0
      wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
1613
0
      goto reset;
1614
0
    }
1615
0
    os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
1616
0
    pos += 12; /* BSS Termination Duration */
1617
0
  }
1618
1619
0
  if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
1620
0
    char url[256];
1621
0
    u8 url_len;
1622
1623
0
    if (end - pos < 1) {
1624
0
      wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
1625
0
           "Management Request (URL)");
1626
0
      goto reset;
1627
0
    }
1628
0
    url_len = *pos++;
1629
0
    if (url_len > end - pos) {
1630
0
      wpa_printf(MSG_DEBUG,
1631
0
           "WNM: Invalid BSS Transition Management Request (URL truncated)");
1632
0
      goto reset;
1633
0
    }
1634
0
    os_memcpy(url, pos, url_len);
1635
0
    url[url_len] = '\0';
1636
0
    pos += url_len;
1637
1638
0
    wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
1639
0
      wpa_sm_pmf_enabled(wpa_s->wpa),
1640
0
      wpa_s->wnm_disassoc_timer * beacon_int * 128 / 125,
1641
0
      url);
1642
0
  }
1643
1644
0
  disassoc_imminent = wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
1645
1646
  /*
1647
   * Based on IEEE Std 802.11be-2024, Table 9-538a (BSS Termination
1648
   * Included and Link Removal Imminent fields encoding), when a station
1649
   * is a non-AP MLD with more than one affiliated link, the Link Removal
1650
   * Imminent field is set to 1, and the BSS Termination Included field
1651
   * is set to 1, only one of the links is removed and the other links
1652
   * remain associated. Ignore the Disassociation Imminent field in such
1653
   * a case.
1654
   *
1655
   * TODO: We should check if the AP has more than one link.
1656
   * TODO: We should pass the RX link and use that
1657
   */
1658
0
  if (disassoc_imminent && wpa_s->valid_links &&
1659
0
      (wpa_s->wnm_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT) &&
1660
0
      (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED)) {
1661
    /* If we still have a link, then just accept the request */
1662
0
    if (wpa_s->valid_links & (wpa_s->valid_links - 1)) {
1663
0
      wpa_printf(MSG_INFO,
1664
0
           "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
1665
0
      disassoc_imminent = false;
1666
1667
0
      wnm_send_bss_transition_mgmt_resp(
1668
0
        wpa_s, WNM_BSS_TM_ACCEPT, 0, 0, NULL);
1669
1670
0
      goto reset;
1671
0
    }
1672
1673
    /* The last link is being removed (which must be the assoc link)
1674
     */
1675
0
    wpa_s->wnm_link_removal = true;
1676
0
    wpa_s->wnm_disassoc_mld = false;
1677
0
    os_memcpy(wpa_s->wnm_disassoc_addr,
1678
0
        wpa_s->links[wpa_s->mlo_assoc_link_id].bssid,
1679
0
        ETH_ALEN);
1680
0
  } else if (wpa_s->valid_links) {
1681
0
    wpa_s->wnm_disassoc_mld = true;
1682
0
    os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->ap_mld_addr,
1683
0
        ETH_ALEN);
1684
0
  } else {
1685
0
    wpa_s->wnm_disassoc_mld = false;
1686
0
    os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->bssid, ETH_ALEN);
1687
0
  }
1688
1689
0
  if (disassoc_imminent)
1690
0
    wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
1691
0
      "Disassociation Timer %u", wpa_s->wnm_disassoc_timer);
1692
1693
0
#ifdef CONFIG_MBO
1694
0
  vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
1695
0
  if (vendor)
1696
0
    wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
1697
0
#endif /* CONFIG_MBO */
1698
1699
0
  if (wnm_parse_candidate_list(wpa_s, pos, end,
1700
0
             &num_valid_candidates) < 0)
1701
0
    goto reset;
1702
1703
0
  if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
1704
0
    if (!wpa_s->wnm_num_neighbor_report) {
1705
0
      wpa_printf(MSG_DEBUG,
1706
0
           "WNM: Candidate list included bit is set, but no candidates found");
1707
0
      wnm_send_bss_transition_mgmt_resp(
1708
0
        wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
1709
0
        MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
1710
0
        NULL);
1711
0
      goto reset;
1712
0
    }
1713
0
    wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
1714
0
  }
1715
1716
0
  if (wpa_s->wnm_num_neighbor_report) {
1717
0
    unsigned int valid_ms;
1718
1719
0
    wnm_sort_cand_list(wpa_s);
1720
0
    wnm_dump_cand_list(wpa_s);
1721
0
    valid_ms = valid_int * beacon_int * 128 / 125;
1722
0
    wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
1723
0
         valid_ms);
1724
0
    os_get_reltime(&wpa_s->wnm_cand_valid_until);
1725
0
    os_reltime_add_ms(&wpa_s->wnm_cand_valid_until, valid_ms);
1726
0
  } else if (!disassoc_imminent) {
1727
0
    enum bss_trans_mgmt_status_code status;
1728
1729
    /* No candidate list and disassociation is not imminent */
1730
1731
0
    if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) ||
1732
0
        wpa_s->wnm_link_removal)
1733
0
      status = WNM_BSS_TM_ACCEPT;
1734
0
    else {
1735
0
      wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
1736
0
      status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1737
0
    }
1738
1739
0
    if (reply)
1740
0
      wnm_send_bss_transition_mgmt_resp(
1741
0
        wpa_s, status,
1742
0
        MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
1743
0
        NULL);
1744
1745
0
    goto reset;
1746
0
  }
1747
1748
  /*
1749
   * Try fetching the latest scan results from the kernel.
1750
   * This can help in finding more up-to-date information should
1751
   * the driver have done some internal scanning operations after
1752
   * the last scan result update in wpa_supplicant.
1753
   *
1754
   * It is not a new scan, this does not update the last_scan
1755
   * timestamp nor will it expire old BSSs.
1756
   */
1757
0
  wpa_supplicant_update_scan_results(wpa_s, NULL);
1758
0
  if (wnm_scan_process(wpa_s, true) > 0)
1759
0
    return;
1760
0
  wpa_printf(MSG_DEBUG,
1761
0
       "WNM: No valid match in previous scan results - try a new scan");
1762
1763
  /*
1764
   * If we have a fixed BSSID configured, just reject at this point.
1765
   * NOTE: We could actually check if we are allowed to stay (and we do
1766
   * above if we have scan results available).
1767
   */
1768
0
  if (wpa_s->current_ssid && wpa_s->current_ssid->bssid_set) {
1769
0
    wpa_printf(MSG_DEBUG, "WNM: Fixed BSSID, rejecting request");
1770
1771
0
    if (reply)
1772
0
      wnm_send_bss_transition_mgmt_resp(
1773
0
        wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
1774
0
        MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
1775
0
        NULL);
1776
1777
0
    goto reset;
1778
0
  }
1779
1780
0
  wnm_set_scan_freqs(wpa_s);
1781
0
  if (num_valid_candidates == 1) {
1782
    /* Any invalid candidate was sorted to the end */
1783
0
    os_memcpy(wpa_s->next_scan_bssid,
1784
0
        wpa_s->wnm_neighbor_report_elements[0].bssid,
1785
0
        ETH_ALEN);
1786
0
    wpa_printf(MSG_DEBUG,
1787
0
        "WNM: Scan only for a specific BSSID since there is only a single candidate "
1788
0
        MACSTR, MAC2STR(wpa_s->next_scan_bssid));
1789
0
  }
1790
0
  wpa_s->wnm_transition_scan = true;
1791
0
  wpa_supplicant_req_scan(wpa_s, 0, 0);
1792
1793
  /* Continue from scan handler */
1794
0
  return;
1795
1796
0
reset:
1797
0
  wnm_btm_reset(wpa_s);
1798
0
}
1799
1800
1801
int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
1802
         size_t data_len)
1803
0
{
1804
0
  const struct ieee80211_mgmt *frame =
1805
0
    (const struct ieee80211_mgmt *) data;
1806
1807
0
  if (data_len <
1808
0
      IEEE80211_HDRLEN + sizeof(frame->u.action.u.bss_tm_resp) ||
1809
0
      frame->u.action.category != WLAN_ACTION_WNM ||
1810
0
      frame->u.action.u.bss_tm_resp.action != WNM_BSS_TRANS_MGMT_RESP ||
1811
0
      frame->u.action.u.bss_tm_resp.status_code != WNM_BSS_TM_ACCEPT)
1812
0
    return -1;
1813
1814
  /*
1815
   * If disassoc imminent bit was set in the request, the response may
1816
   * indicate accept even if no candidate was found, so bail out here.
1817
   */
1818
0
  if (!wpa_s->wnm_target_bss) {
1819
0
    wpa_printf(MSG_DEBUG, "WNM: Target BSS is not set");
1820
0
    return 0;
1821
0
  }
1822
1823
0
  if (!wpa_s->current_ssid)
1824
0
    return 0;
1825
1826
0
  wnm_bss_tm_connect(wpa_s, wpa_s->wnm_target_bss, wpa_s->current_ssid,
1827
0
         0);
1828
1829
0
  wpa_s->wnm_target_bss = NULL;
1830
0
  return 0;
1831
0
}
1832
1833
1834
0
#define BTM_QUERY_MIN_SIZE  4
1835
1836
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
1837
               u8 query_reason,
1838
               const char *btm_candidates,
1839
               int cand_list)
1840
0
{
1841
0
  struct wpabuf *buf;
1842
0
  int ret;
1843
1844
0
  wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
1845
0
       MACSTR " query_reason=%u%s",
1846
0
       MAC2STR(wpa_s->bssid), query_reason,
1847
0
       cand_list ? " candidate list" : "");
1848
1849
0
  buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
1850
0
  if (!buf)
1851
0
    return -1;
1852
1853
0
  wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1854
0
  wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
1855
0
  wpabuf_put_u8(buf, 1);
1856
0
  wpabuf_put_u8(buf, query_reason);
1857
1858
0
  if (cand_list)
1859
0
    wnm_add_cand_list(wpa_s, &buf);
1860
1861
0
  if (btm_candidates) {
1862
0
    const size_t max_len = 1000;
1863
1864
0
    ret = wpabuf_resize(&buf, max_len);
1865
0
    if (ret < 0) {
1866
0
      wpabuf_free(buf);
1867
0
      return ret;
1868
0
    }
1869
1870
0
    ret = ieee802_11_parse_candidate_list(btm_candidates,
1871
0
                  wpabuf_put(buf, 0),
1872
0
                  max_len);
1873
0
    if (ret < 0) {
1874
0
      wpabuf_free(buf);
1875
0
      return ret;
1876
0
    }
1877
1878
0
    wpabuf_put(buf, ret);
1879
0
  }
1880
1881
0
  ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1882
0
          wpa_s->own_addr, wpa_s->bssid,
1883
0
          wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1884
1885
0
  wpabuf_free(buf);
1886
0
  return ret;
1887
0
}
1888
1889
1890
static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
1891
              const u8 *sa, const u8 *data,
1892
              int len)
1893
0
{
1894
0
  const u8 *pos, *end, *next;
1895
0
  u8 ie, ie_len;
1896
1897
0
  pos = data;
1898
0
  end = data + len;
1899
1900
0
  while (end - pos > 1) {
1901
0
    ie = *pos++;
1902
0
    ie_len = *pos++;
1903
0
    wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
1904
0
         ie, ie_len);
1905
0
    if (ie_len > end - pos) {
1906
0
      wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
1907
0
           "subelement");
1908
0
      break;
1909
0
    }
1910
0
    next = pos + ie_len;
1911
0
    if (ie_len < 4) {
1912
0
      pos = next;
1913
0
      continue;
1914
0
    }
1915
0
    wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
1916
0
         WPA_GET_BE24(pos), pos[3]);
1917
1918
0
#ifdef CONFIG_HS20
1919
0
    if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
1920
0
        WPA_GET_BE24(pos) == OUI_WFA &&
1921
0
        pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
1922
0
      const u8 *ie_end;
1923
0
      u8 url_len;
1924
0
      char *url;
1925
0
      u8 code;
1926
0
      u16 reauth_delay;
1927
1928
0
      ie_end = pos + ie_len;
1929
0
      pos += 4;
1930
0
      code = *pos++;
1931
0
      reauth_delay = WPA_GET_LE16(pos);
1932
0
      pos += 2;
1933
0
      url_len = *pos++;
1934
0
      wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
1935
0
           "Imminent - Reason Code %u   "
1936
0
           "Re-Auth Delay %u  URL Length %u",
1937
0
           code, reauth_delay, url_len);
1938
0
      if (url_len > ie_end - pos)
1939
0
        break;
1940
0
      url = os_malloc(url_len + 1);
1941
0
      if (url == NULL)
1942
0
        break;
1943
0
      os_memcpy(url, pos, url_len);
1944
0
      url[url_len] = '\0';
1945
0
      hs20_rx_deauth_imminent_notice(wpa_s, code,
1946
0
                   reauth_delay, url);
1947
0
      os_free(url);
1948
0
      pos = next;
1949
0
      continue;
1950
0
    }
1951
1952
0
    if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
1953
0
        WPA_GET_BE24(pos) == OUI_WFA &&
1954
0
        pos[3] == HS20_WNM_T_C_ACCEPTANCE) {
1955
0
      const u8 *ie_end;
1956
0
      u8 url_len;
1957
0
      char *url;
1958
1959
0
      ie_end = pos + ie_len;
1960
0
      pos += 4;
1961
0
      url_len = *pos++;
1962
0
      wpa_printf(MSG_DEBUG,
1963
0
           "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)",
1964
0
           url_len);
1965
0
      if (url_len > ie_end - pos)
1966
0
        break;
1967
0
      url = os_malloc(url_len + 1);
1968
0
      if (!url)
1969
0
        break;
1970
0
      os_memcpy(url, pos, url_len);
1971
0
      url[url_len] = '\0';
1972
0
      hs20_rx_t_c_acceptance(wpa_s, url);
1973
0
      os_free(url);
1974
0
      pos = next;
1975
0
      continue;
1976
0
    }
1977
0
#endif /* CONFIG_HS20 */
1978
1979
0
    pos = next;
1980
0
  }
1981
0
}
1982
1983
1984
static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
1985
          const u8 *da, const u8 *sa,
1986
          const u8 *frm, int len)
1987
0
{
1988
0
  const u8 *pos, *end;
1989
0
  u8 dialog_token, type;
1990
1991
0
  if (is_multicast_ether_addr(da)) {
1992
0
    wpa_printf(MSG_DEBUG,
1993
0
         "WNM: Ignore group-addressed WNM Notification Request frame (A1="
1994
0
         MACSTR " A2=" MACSTR ")",
1995
0
         MAC2STR(da), MAC2STR(sa));
1996
0
    return;
1997
0
  }
1998
1999
  /* Dialog Token [1] | Type [1] | Subelements */
2000
2001
0
  if (len < 2 || sa == NULL)
2002
0
    return;
2003
0
  end = frm + len;
2004
0
  pos = frm;
2005
0
  dialog_token = *pos++;
2006
0
  type = *pos++;
2007
2008
0
  wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
2009
0
    "(dialog_token %u type %u sa " MACSTR ")",
2010
0
    dialog_token, type, MAC2STR(sa));
2011
0
  wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
2012
0
        pos, end - pos);
2013
2014
0
  if (wpa_s->wpa_state != WPA_COMPLETED ||
2015
0
      (!ether_addr_equal(sa, wpa_s->bssid) &&
2016
0
       (!wpa_s->valid_links ||
2017
0
        !ether_addr_equal(sa, wpa_s->ap_mld_addr)))) {
2018
0
    wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
2019
0
      "from our AP - ignore it");
2020
0
    return;
2021
0
  }
2022
2023
0
  switch (type) {
2024
0
  case 1:
2025
0
    ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
2026
0
    break;
2027
0
  default:
2028
0
    wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
2029
0
      "WNM-Notification type %u", type);
2030
0
    break;
2031
0
  }
2032
0
}
2033
2034
2035
static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s,
2036
               const u8 *sa, const u8 *frm,
2037
               int len)
2038
0
{
2039
0
  u8 dialog_token, req_info, auto_report, timeout;
2040
2041
0
  if (!wpa_s->conf->coloc_intf_reporting)
2042
0
    return;
2043
2044
  /* Dialog Token [1] | Request Info [1] */
2045
2046
0
  if (len < 2)
2047
0
    return;
2048
0
  dialog_token = frm[0];
2049
0
  req_info = frm[1];
2050
0
  auto_report = req_info & 0x03;
2051
0
  timeout = req_info >> 2;
2052
2053
0
  wpa_dbg(wpa_s, MSG_DEBUG,
2054
0
    "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")",
2055
0
    dialog_token, auto_report, timeout, MAC2STR(sa));
2056
2057
0
  if (dialog_token == 0)
2058
0
    return; /* only nonzero values are used for request */
2059
2060
0
  if (wpa_s->wpa_state != WPA_COMPLETED ||
2061
0
      (!ether_addr_equal(sa, wpa_s->bssid) &&
2062
0
       (!wpa_s->valid_links ||
2063
0
        !ether_addr_equal(sa, wpa_s->ap_mld_addr)))) {
2064
0
    wpa_dbg(wpa_s, MSG_DEBUG,
2065
0
      "WNM: Collocated Interference Request frame not from current AP - ignore it");
2066
0
    return;
2067
0
  }
2068
2069
0
  wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u",
2070
0
    dialog_token, auto_report, timeout);
2071
0
  wpa_s->coloc_intf_dialog_token = dialog_token;
2072
0
  wpa_s->coloc_intf_auto_report = auto_report;
2073
0
  wpa_s->coloc_intf_timeout = timeout;
2074
0
}
2075
2076
2077
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
2078
            const struct ieee80211_mgmt *mgmt, size_t len)
2079
0
{
2080
0
  const u8 *pos, *end;
2081
0
  u8 act;
2082
2083
0
  if (len < IEEE80211_HDRLEN + 2)
2084
0
    return;
2085
2086
0
  pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
2087
0
  act = *pos++;
2088
0
  end = ((const u8 *) mgmt) + len;
2089
2090
0
  wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
2091
0
       act, MAC2STR(mgmt->sa));
2092
0
  if (wpa_s->wpa_state < WPA_ASSOCIATED ||
2093
0
      (!ether_addr_equal(mgmt->sa, wpa_s->bssid) &&
2094
0
       (!wpa_s->valid_links ||
2095
0
        !ether_addr_equal(mgmt->sa, wpa_s->ap_mld_addr)))) {
2096
0
    wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
2097
0
         "frame");
2098
0
    return;
2099
0
  }
2100
2101
0
  switch (act) {
2102
0
  case WNM_BSS_TRANS_MGMT_REQ:
2103
0
    ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
2104
0
             !(mgmt->da[0] & 0x01));
2105
0
    break;
2106
0
  case WNM_SLEEP_MODE_RESP:
2107
0
    ieee802_11_rx_wnmsleep_resp(wpa_s, mgmt->da, mgmt->sa,
2108
0
              pos, end - pos);
2109
0
    break;
2110
0
  case WNM_NOTIFICATION_REQ:
2111
0
    ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->da, mgmt->sa,
2112
0
              pos, end - pos);
2113
0
    break;
2114
0
  case WNM_COLLOCATED_INTERFERENCE_REQ:
2115
0
    ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos,
2116
0
             end - pos);
2117
0
    break;
2118
0
  default:
2119
0
    wpa_printf(MSG_ERROR, "WNM: Unknown request");
2120
0
    break;
2121
0
  }
2122
0
}
2123
2124
2125
int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
2126
             const struct wpabuf *elems)
2127
0
{
2128
0
  struct wpabuf *buf;
2129
0
  int ret;
2130
2131
0
  if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems)
2132
0
    return -1;
2133
2134
0
  wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to "
2135
0
       MACSTR " (dialog token %u)",
2136
0
       MAC2STR(wpa_s->bssid), dialog_token);
2137
2138
0
  buf = wpabuf_alloc(3 + wpabuf_len(elems));
2139
0
  if (!buf)
2140
0
    return -1;
2141
2142
0
  wpabuf_put_u8(buf, WLAN_ACTION_WNM);
2143
0
  wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT);
2144
0
  wpabuf_put_u8(buf, dialog_token);
2145
0
  wpabuf_put_buf(buf, elems);
2146
2147
0
  ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
2148
0
          wpa_s->own_addr, wpa_s->bssid,
2149
0
          wpabuf_head_u8(buf), wpabuf_len(buf), 0);
2150
0
  wpabuf_free(buf);
2151
0
  return ret;
2152
0
}
2153
2154
2155
void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
2156
            struct wpabuf *elems)
2157
0
{
2158
0
  if (elems && wpabuf_len(elems) == 0) {
2159
0
    wpabuf_free(elems);
2160
0
    elems = NULL;
2161
0
  }
2162
2163
  /* NOTE: The elements are not stored as they are only send out once */
2164
2165
0
  if (wpa_s->conf->coloc_intf_reporting && elems &&
2166
0
      wpa_s->coloc_intf_dialog_token &&
2167
0
      (wpa_s->coloc_intf_auto_report == 1 ||
2168
0
       wpa_s->coloc_intf_auto_report == 3)) {
2169
    /* TODO: Check that there has not been less than
2170
     * wpa_s->coloc_intf_timeout * 200 TU from the last report.
2171
     */
2172
0
    wnm_send_coloc_intf_report(wpa_s,
2173
0
             wpa_s->coloc_intf_dialog_token,
2174
0
             elems);
2175
0
  }
2176
2177
0
  wpabuf_free(elems);
2178
0
}
2179
2180
2181
void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s)
2182
0
{
2183
0
  wpa_s->coloc_intf_dialog_token = 0;
2184
0
  wpa_s->coloc_intf_auto_report = 0;
2185
0
}
2186
2187
2188
bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
2189
0
{
2190
0
  int i;
2191
2192
  /*
2193
   * In case disassociation imminent is set, do no try to use a BSS to
2194
   * which we are connected.
2195
   */
2196
0
  if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
2197
0
    if (!wpa_s->wnm_disassoc_mld) {
2198
0
      if (ether_addr_equal(bss->bssid,
2199
0
               wpa_s->wnm_disassoc_addr))
2200
0
        return true;
2201
0
    } else {
2202
0
      if (ether_addr_equal(bss->mld_addr,
2203
0
               wpa_s->wnm_disassoc_addr))
2204
0
        return true;
2205
0
    }
2206
0
  }
2207
2208
0
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
2209
0
    struct neighbor_report *nei;
2210
2211
0
    nei = &wpa_s->wnm_neighbor_report_elements[i];
2212
0
    if (!ether_addr_equal(nei->bssid, bss->bssid) &&
2213
0
        (is_zero_ether_addr(bss->mld_addr) ||
2214
0
         !ether_addr_equal(nei->mld_addr, bss->mld_addr)))
2215
0
      continue;
2216
2217
0
    if (nei->preference_present && nei->preference == 0)
2218
0
      return true;
2219
2220
0
#ifdef CONFIG_MBO
2221
0
    if (nei->drv_mbo_reject)
2222
0
      return true;
2223
0
#endif /* CONFIG_MBO */
2224
2225
    /*
2226
     * NOTE: We should select one entry and stick with it, but to
2227
     * do that we need to refactor the BSS selection to be MLD
2228
     * aware from the beginning. Instead we just check whether the
2229
     * link is permitted in any possible configuration. We are not
2230
     * supposed to do that, however the AP is able to reject a
2231
     * subset of the requested links.
2232
     */
2233
0
    if (nei->mld_links && !(nei->mld_links & BIT(bss->mld_link_id)))
2234
0
      continue;
2235
2236
0
    break;
2237
0
  }
2238
2239
  /* If the abridged bit is set, the BSS must be a known neighbor. */
2240
0
  if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ABRIDGED) &&
2241
0
      wpa_s->wnm_num_neighbor_report == i)
2242
0
    return true;
2243
2244
0
  return false;
2245
0
}