Coverage Report

Created: 2025-10-10 06:31

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