Coverage Report

Created: 2025-07-18 06:03

/src/hostap/wpa_supplicant/wnm_sta.c
Line
Count
Source (jump to first uncovered line)
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.84M
#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
2
{
309
  /*
310
   * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
311
   * WNM-Sleep Mode IE | TFS Response IE
312
   */
313
2
  const u8 *pos = frm; /* point to payload after the action field */
314
2
  u16 key_len_total;
315
2
  struct wnm_sleep_element *wnmsleep_ie = NULL;
316
  /* multiple TFS Resp IE (assuming consecutive) */
317
2
  const u8 *tfsresp_ie_start = NULL;
318
2
  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
2
  size_t left;
324
325
2
  if (!wpa_s->wnmsleep_used) {
326
2
    wpa_printf(MSG_DEBUG,
327
2
         "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested");
328
2
    return;
329
2
  }
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.54k
{
422
2.54k
  int i;
423
424
5.29k
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
425
2.74k
    os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
426
2.74k
    os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
427
2.74k
  }
428
429
2.54k
  wpa_s->wnm_num_neighbor_report = 0;
430
2.54k
  os_free(wpa_s->wnm_neighbor_report_elements);
431
2.54k
  wpa_s->wnm_neighbor_report_elements = NULL;
432
433
2.54k
  wpa_s->wnm_cand_valid_until.sec = 0;
434
2.54k
  wpa_s->wnm_cand_valid_until.usec = 0;
435
436
2.54k
  wpa_s->wnm_mode = 0;
437
2.54k
  wpa_s->wnm_dialog_token = 0;
438
2.54k
  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.54k
}
445
446
447
static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
448
             u8 id, u8 elen, const u8 *pos)
449
12.5k
{
450
12.5k
  switch (id) {
451
595
  case WNM_NEIGHBOR_TSF:
452
595
    if (elen < 2 + 2) {
453
344
      wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
454
344
      break;
455
344
    }
456
251
    rep->tsf_offset = WPA_GET_LE16(pos);
457
251
    rep->beacon_int = WPA_GET_LE16(pos + 2);
458
251
    rep->tsf_present = 1;
459
251
    break;
460
1.08k
  case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
461
1.08k
    if (elen < 2) {
462
848
      wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
463
848
           "country string");
464
848
      break;
465
848
    }
466
234
    os_memcpy(rep->country, pos, 2);
467
234
    rep->country_present = 1;
468
234
    break;
469
1.35k
  case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
470
1.35k
    if (elen < 1) {
471
692
      wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
472
692
           "candidate");
473
692
      break;
474
692
    }
475
664
    rep->preference = pos[0];
476
664
    rep->preference_present = 1;
477
664
    break;
478
867
  case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
479
867
    if (elen < 10) {
480
427
      wpa_printf(MSG_DEBUG,
481
427
           "WNM: Too short BSS termination duration");
482
427
      break;
483
427
    }
484
440
    rep->bss_term_tsf = WPA_GET_LE64(pos);
485
440
    rep->bss_term_dur = WPA_GET_LE16(pos + 8);
486
440
    rep->bss_term_present = 1;
487
440
    break;
488
545
  case WNM_NEIGHBOR_BEARING:
489
545
    if (elen < 8) {
490
240
      wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
491
240
           "bearing");
492
240
      break;
493
240
    }
494
305
    rep->bearing = WPA_GET_LE16(pos);
495
305
    rep->distance = WPA_GET_LE32(pos + 2);
496
305
    rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
497
305
    rep->bearing_present = 1;
498
305
    break;
499
1.36k
  case WNM_NEIGHBOR_MEASUREMENT_PILOT:
500
1.36k
    if (elen < 1) {
501
248
      wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
502
248
           "pilot");
503
248
      break;
504
248
    }
505
1.11k
    os_free(rep->meas_pilot);
506
1.11k
    rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
507
1.11k
    if (rep->meas_pilot == NULL)
508
0
      break;
509
1.11k
    rep->meas_pilot->measurement_pilot = pos[0];
510
1.11k
    rep->meas_pilot->subelem_len = elen - 1;
511
1.11k
    os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
512
1.11k
    break;
513
506
  case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
514
506
    if (elen < 5) {
515
256
      wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
516
256
           "capabilities");
517
256
      break;
518
256
    }
519
250
    os_memcpy(rep->rm_capab, pos, 5);
520
250
    rep->rm_capab_present = 1;
521
250
    break;
522
523
  case WNM_NEIGHBOR_MULTIPLE_BSSID:
523
523
    if (elen < 1) {
524
279
      wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
525
279
      break;
526
279
    }
527
244
    os_free(rep->mul_bssid);
528
244
    rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
529
244
    if (rep->mul_bssid == NULL)
530
0
      break;
531
244
    rep->mul_bssid->max_bssid_indicator = pos[0];
532
244
    rep->mul_bssid->subelem_len = elen - 1;
533
244
    os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
534
244
    break;
535
5.67k
  default:
536
5.67k
    wpa_printf(MSG_DEBUG,
537
5.67k
         "WNM: Unsupported neighbor report subelement id %u",
538
5.67k
         id);
539
5.67k
    break;
540
12.5k
  }
541
12.5k
}
542
543
544
static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
545
2.48k
{
546
2.48k
  struct wpa_bss *bss = wpa_s->current_bss;
547
2.48k
  const char *country = NULL;
548
2.48k
  int freq;
549
550
2.48k
  if (bss) {
551
2.48k
    const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
552
553
2.48k
    if (elem && elem[1] >= 2)
554
0
      country = (const char *) (elem + 2);
555
2.48k
  }
556
557
2.48k
  freq = ieee80211_chan_to_freq(country, op_class, chan);
558
2.48k
  if (freq <= 0 && (op_class == 0 || op_class == 255)) {
559
    /*
560
     * Some APs do not advertise correct operating class
561
     * information. Try to determine the most likely operating
562
     * frequency based on the channel number.
563
     */
564
504
    if (chan >= 1 && chan <= 13)
565
150
      freq = 2407 + chan * 5;
566
354
    else if (chan == 14)
567
25
      freq = 2484;
568
329
    else if (chan >= 36 && chan <= 177)
569
91
      freq = 5000 + chan * 5;
570
504
  }
571
2.48k
  return freq;
572
2.48k
}
573
574
575
static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
576
              const u8 *pos, u8 len,
577
              struct neighbor_report *rep)
578
2.74k
{
579
2.74k
  u8 left = len;
580
581
2.74k
  if (left < 13) {
582
261
    wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
583
261
    return;
584
261
  }
585
586
2.48k
  os_memcpy(rep->bssid, pos, ETH_ALEN);
587
2.48k
  rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
588
2.48k
  rep->regulatory_class = *(pos + 10);
589
2.48k
  rep->channel_number = *(pos + 11);
590
2.48k
  rep->phy_type = *(pos + 12);
591
592
2.48k
  pos += 13;
593
2.48k
  left -= 13;
594
595
14.9k
  while (left >= 2) {
596
12.9k
    u8 id, elen;
597
598
12.9k
    id = *pos++;
599
12.9k
    elen = *pos++;
600
12.9k
    wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
601
12.9k
    left -= 2;
602
12.9k
    if (elen > left) {
603
413
      wpa_printf(MSG_DEBUG,
604
413
           "WNM: Truncated neighbor report subelement");
605
413
      break;
606
413
    }
607
12.5k
    wnm_parse_neighbor_report_elem(rep, id, elen, pos);
608
12.5k
    left -= elen;
609
12.5k
    pos += elen;
610
12.5k
  }
611
612
2.48k
  rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
613
2.48k
             rep->channel_number);
614
2.48k
}
615
616
617
static void
618
fetch_drv_mbo_candidate_info(struct wpa_supplicant *wpa_s,
619
           enum mbo_transition_reject_reason *reason)
620
927
{
621
#ifdef CONFIG_MBO
622
  struct wpa_bss_trans_info params;
623
  struct wpa_bss_candidate_info *info = NULL;
624
  struct neighbor_report *nei;
625
  u8 *pos;
626
  unsigned int i;
627
628
  if (!wpa_s->wnm_mbo_trans_reason_present)
629
    return;
630
631
  params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
632
  params.n_candidates = 0;
633
  params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
634
  if (!params.bssid)
635
    return;
636
637
  pos = params.bssid;
638
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
639
    nei = &wpa_s->wnm_neighbor_report_elements[i];
640
641
    nei->drv_mbo_reject = 0;
642
643
    if (nei->preference_present && nei->preference == 0)
644
      continue;
645
646
    /* Should we query BSSIDs that we reject for other reasons? */
647
648
    os_memcpy(pos, nei->bssid, ETH_ALEN);
649
    pos += ETH_ALEN;
650
    params.n_candidates++;
651
  }
652
653
  if (!params.n_candidates)
654
    goto end;
655
656
  info = wpa_drv_get_bss_trans_status(wpa_s, &params);
657
  if (!info)
658
    goto end;
659
660
  for (i = 0; i < info->num; i++) {
661
    int j;
662
663
    for (j = 0; j < wpa_s->wnm_num_neighbor_report; j++) {
664
      nei = &wpa_s->wnm_neighbor_report_elements[j];
665
666
      if (!ether_addr_equal(info->candidates[i].bssid,
667
                nei->bssid))
668
        continue;
669
670
      nei->drv_mbo_reject = !info->candidates[i].is_accept;
671
672
      /* Use the reject reason from the first candidate */
673
      if (i == 0 && nei->drv_mbo_reject)
674
        *reason = info->candidates[i].reject_reason;
675
676
      break;
677
    }
678
  }
679
680
end:
681
  os_free(params.bssid);
682
  if (info) {
683
    os_free(info->candidates);
684
    os_free(info);
685
  }
686
#endif /* CONFIG_MBO */
687
927
}
688
689
690
static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
691
0
{
692
0
  const u8 *ie_a, *ie_b;
693
694
0
  if (!a || !b)
695
0
    return 0;
696
697
0
  ie_a = wpa_bss_get_ie(a, eid);
698
0
  ie_b = wpa_bss_get_ie(b, eid);
699
700
0
  if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
701
0
    return 0;
702
703
0
  return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
704
0
}
705
706
707
static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
708
0
{
709
0
  u32 info = 0;
710
711
0
  info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
712
713
  /*
714
   * Leave the security and key scope bits unset to indicate that the
715
   * security information is not available.
716
   */
717
718
0
  if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
719
0
    info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
720
0
  if (bss->caps & WLAN_CAPABILITY_QOS)
721
0
    info |= NEI_REP_BSSID_INFO_QOS;
722
0
  if (bss->caps & WLAN_CAPABILITY_APSD)
723
0
    info |= NEI_REP_BSSID_INFO_APSD;
724
0
  if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
725
0
    info |= NEI_REP_BSSID_INFO_RM;
726
0
  if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK)
727
0
    info |= NEI_REP_BSSID_INFO_DELAYED_BA;
728
0
  if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK)
729
0
    info |= NEI_REP_BSSID_INFO_IMM_BA;
730
0
  if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
731
0
    info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
732
0
  if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
733
0
    info |= NEI_REP_BSSID_INFO_HT;
734
735
0
  return info;
736
0
}
737
738
739
static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
740
         u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
741
         u8 pref)
742
0
{
743
0
  if (wpabuf_len(*buf) + 18 >
744
0
      IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
745
0
    wpa_printf(MSG_DEBUG,
746
0
         "WNM: No room in frame for Neighbor Report element");
747
0
    return -1;
748
0
  }
749
750
0
  if (wpabuf_resize(buf, 18) < 0) {
751
0
    wpa_printf(MSG_DEBUG,
752
0
         "WNM: Failed to allocate memory for Neighbor Report element");
753
0
    return -1;
754
0
  }
755
756
0
  wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
757
  /* length: 13 for basic neighbor report + 3 for preference subelement */
758
0
  wpabuf_put_u8(*buf, 16);
759
0
  wpabuf_put_data(*buf, bssid, ETH_ALEN);
760
0
  wpabuf_put_le32(*buf, bss_info);
761
0
  wpabuf_put_u8(*buf, op_class);
762
0
  wpabuf_put_u8(*buf, chan);
763
0
  wpabuf_put_u8(*buf, phy_type);
764
0
  wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
765
0
  wpabuf_put_u8(*buf, 1);
766
0
  wpabuf_put_u8(*buf, pref);
767
0
  return 0;
768
0
}
769
770
771
static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
772
             struct wpa_bss *bss, struct wpabuf **buf,
773
             u8 pref)
774
0
{
775
0
  const u8 *ie;
776
0
  u8 op_class, chan;
777
0
  int sec_chan = 0;
778
0
  enum oper_chan_width vht = CONF_OPER_CHWIDTH_USE_HT;
779
0
  enum phy_type phy_type;
780
0
  u32 info;
781
0
  struct ieee80211_ht_operation *ht_oper = NULL;
782
0
  struct ieee80211_vht_operation *vht_oper = NULL;
783
784
0
  ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
785
0
  if (ie && ie[1] >= 2) {
786
0
    ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
787
788
0
    if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
789
0
      sec_chan = 1;
790
0
    else if (ht_oper->ht_param &
791
0
       HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
792
0
      sec_chan = -1;
793
0
  }
794
795
0
  ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION);
796
0
  if (ie && ie[1] >= 1) {
797
0
    vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
798
799
0
    if (vht_oper->vht_op_info_chwidth == CHANWIDTH_80MHZ ||
800
0
        vht_oper->vht_op_info_chwidth == CHANWIDTH_160MHZ ||
801
0
        vht_oper->vht_op_info_chwidth == CHANWIDTH_80P80MHZ)
802
0
      vht = vht_oper->vht_op_info_chwidth;
803
0
  }
804
805
0
  if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class,
806
0
            &chan) == NUM_HOSTAPD_MODES) {
807
0
    wpa_printf(MSG_DEBUG,
808
0
         "WNM: Cannot determine operating class and channel");
809
0
    return -2;
810
0
  }
811
812
0
  phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL),
813
0
            (vht_oper != NULL));
814
0
  if (phy_type == PHY_TYPE_UNSPECIFIED) {
815
0
    wpa_printf(MSG_DEBUG,
816
0
         "WNM: Cannot determine BSS phy type for Neighbor Report");
817
0
    return -2;
818
0
  }
819
820
0
  info = wnm_get_bss_info(wpa_s, bss);
821
822
0
  return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type,
823
0
             pref);
824
0
}
825
826
827
static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
828
0
{
829
0
  unsigned int i, pref = 255;
830
0
  struct os_reltime now;
831
0
  struct wpa_ssid *ssid = wpa_s->current_ssid;
832
833
0
  if (!ssid)
834
0
    return;
835
836
  /*
837
   * TODO: Define when scan results are no longer valid for the candidate
838
   * list.
839
   */
840
0
  os_get_reltime(&now);
841
0
  if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
842
0
    return;
843
844
0
  wpa_printf(MSG_DEBUG,
845
0
       "WNM: Add candidate list to BSS Transition Management Response frame");
846
0
  for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
847
0
    struct wpa_bss *bss = wpa_s->last_scan_res[i];
848
0
    int res;
849
850
0
    if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0, false)) {
851
0
      res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
852
0
      if (res == -2)
853
0
        continue; /* could not build entry for BSS */
854
0
      if (res < 0)
855
0
        break; /* no more room for candidates */
856
0
      if (pref == 1)
857
0
        break;
858
0
    }
859
0
  }
860
861
0
  wpa_hexdump_buf(MSG_DEBUG,
862
0
      "WNM: BSS Transition Management Response candidate list",
863
0
      *buf);
864
0
}
865
866
867
25
#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
868
869
static int wnm_send_bss_transition_mgmt_resp(
870
  struct wpa_supplicant *wpa_s,
871
  enum bss_trans_mgmt_status_code status,
872
  enum mbo_transition_reject_reason reason,
873
  u8 delay, const u8 *target_bssid)
874
25
{
875
25
  struct wpabuf *buf;
876
25
  int res;
877
878
25
  wpa_s->wnm_reply = 0;
879
880
25
  wpa_printf(MSG_DEBUG,
881
25
       "WNM: Send BSS Transition Management Response to " MACSTR
882
25
       " dialog_token=%u status=%u reason=%u delay=%d",
883
25
       MAC2STR(wpa_s->bssid), wpa_s->wnm_dialog_token, status,
884
25
       reason, delay);
885
25
  if (!wpa_s->current_bss) {
886
0
    wpa_printf(MSG_DEBUG,
887
0
         "WNM: Current BSS not known - drop response");
888
0
    return -1;
889
0
  }
890
891
25
  buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
892
25
  if (!buf) {
893
0
    wpa_printf(MSG_DEBUG,
894
0
         "WNM: Failed to allocate memory for BTM response");
895
0
    return -1;
896
0
  }
897
898
25
  wpa_s->bss_tm_status = status;
899
25
  wpas_notify_bss_tm_status(wpa_s);
900
901
25
  wpabuf_put_u8(buf, WLAN_ACTION_WNM);
902
25
  wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
903
25
  wpabuf_put_u8(buf, wpa_s->wnm_dialog_token);
904
25
  wpabuf_put_u8(buf, status);
905
25
  wpabuf_put_u8(buf, delay);
906
25
  if (target_bssid) {
907
0
    wpabuf_put_data(buf, target_bssid, ETH_ALEN);
908
25
  } else if (status == WNM_BSS_TM_ACCEPT) {
909
    /*
910
     * IEEE Std 802.11-2024, 9.6.13.10 (BSS Transition Management
911
     * Response frame format) clarifies that the Target BSSID field
912
     * is always present when status code is zero, so use a fake
913
     * value here if no BSSID is yet known.
914
     */
915
1
    wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
916
1
  }
917
918
25
  if (status == WNM_BSS_TM_ACCEPT && target_bssid)
919
0
    wnm_add_cand_list(wpa_s, &buf);
920
921
#ifdef CONFIG_MBO
922
  if (status != WNM_BSS_TM_ACCEPT &&
923
      wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
924
    u8 mbo[10];
925
    size_t ret;
926
927
    ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
928
               reason);
929
    if (ret) {
930
      if (wpabuf_resize(&buf, ret) < 0) {
931
        wpabuf_free(buf);
932
        wpa_printf(MSG_DEBUG,
933
             "WNM: Failed to allocate memory for MBO IE");
934
        return -1;
935
      }
936
937
      wpabuf_put_data(buf, mbo, ret);
938
    }
939
  }
940
#endif /* CONFIG_MBO */
941
942
25
  res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
943
25
          wpa_s->own_addr, wpa_s->bssid,
944
25
          wpabuf_head_u8(buf), wpabuf_len(buf), 0);
945
25
  if (res < 0) {
946
25
    wpa_printf(MSG_DEBUG,
947
25
         "WNM: Failed to send BSS Transition Management Response");
948
25
  }
949
950
25
  wpabuf_free(buf);
951
952
25
  return res;
953
25
}
954
955
956
static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
957
             struct wpa_bss *bss, struct wpa_ssid *ssid,
958
             int after_new_scan)
959
0
{
960
0
  struct wpa_radio_work *already_connecting;
961
962
0
  wpa_dbg(wpa_s, MSG_DEBUG,
963
0
    "WNM: Transition to BSS " MACSTR
964
0
    " based on BSS Transition Management Request (old BSSID "
965
0
    MACSTR " after_new_scan=%d)",
966
0
    MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan);
967
968
  /* Send the BSS Management Response - Accept */
969
0
  if (wpa_s->wnm_reply) {
970
0
    wpa_s->wnm_target_bss = bss;
971
0
    wpa_printf(MSG_DEBUG,
972
0
         "WNM: Sending successful BSS Transition Management Response");
973
974
    /* This function will be called again from the TX handler to
975
     * start the actual reassociation after this response has been
976
     * delivered to the current AP. */
977
0
    if (wnm_send_bss_transition_mgmt_resp(
978
0
          wpa_s, WNM_BSS_TM_ACCEPT,
979
0
          MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
980
0
          bss->bssid) >= 0)
981
0
      return;
982
0
  }
983
984
0
  if (bss == wpa_s->current_bss) {
985
0
    wpa_printf(MSG_DEBUG,
986
0
         "WNM: Already associated with the preferred candidate");
987
0
    wnm_btm_reset(wpa_s);
988
0
    return;
989
0
  }
990
991
0
  already_connecting = radio_work_pending(wpa_s, "sme-connect");
992
0
  wpa_s->reassociate = 1;
993
0
  wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
994
0
  wpa_supplicant_connect(wpa_s, bss, ssid);
995
996
  /*
997
   * Indicate that a BSS transition is in progress so scan results that
998
   * come in before the 'sme-connect' radio work gets executed do not
999
   * override the original connection attempt.
1000
   */
1001
0
  if (!already_connecting && radio_work_pending(wpa_s, "sme-connect"))
1002
0
    wpa_s->bss_trans_mgmt_in_progress = true;
1003
0
}
1004
1005
1006
int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
1007
927
{
1008
927
  struct wpa_bss *bss, *current_bss = wpa_s->current_bss;
1009
927
  struct wpa_ssid *ssid = wpa_s->current_ssid;
1010
927
  enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1011
927
  enum mbo_transition_reject_reason reason =
1012
927
    MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
1013
927
  struct wpa_ssid *selected_ssid = NULL;
1014
1015
927
  if (!ssid || !wpa_s->wnm_dialog_token)
1016
0
    return 0;
1017
1018
927
  wpa_dbg(wpa_s, MSG_DEBUG,
1019
927
    "WNM: Process scan results for BSS Transition Management");
1020
927
  if (!pre_scan_check &&
1021
927
      os_reltime_initialized(&wpa_s->wnm_cand_valid_until) &&
1022
927
      os_reltime_before(&wpa_s->wnm_cand_valid_until,
1023
0
            &wpa_s->scan_trigger_time)) {
1024
0
    wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
1025
0
    goto send_bss_resp_fail;
1026
0
  }
1027
1028
927
  if (!pre_scan_check && !wpa_s->wnm_transition_scan)
1029
0
    return 0;
1030
1031
927
  wpa_s->wnm_transition_scan = false;
1032
1033
  /* Fetch MBO transition candidate rejection information from driver */
1034
927
  fetch_drv_mbo_candidate_info(wpa_s, &reason);
1035
1036
  /* Compare the Neighbor Report and scan results */
1037
927
  bss = wpa_supplicant_select_bss(wpa_s, ssid, &selected_ssid, 1);
1038
#ifdef CONFIG_MBO
1039
  if (!bss && wpa_s->wnm_mbo_trans_reason_present &&
1040
      (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT)) {
1041
    int i;
1042
    bool changed = false;
1043
1044
    /*
1045
     * We didn't find any candidate, the driver had a say about
1046
     * which targets to reject and disassociation is immiment.
1047
     *
1048
     * We should still try to roam, so retry after ignoring the
1049
     * driver reject for any BSS that has an RSSI better than
1050
     * disassoc_imminent_rssi_threshold.
1051
     */
1052
    for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1053
      struct neighbor_report *nei;
1054
1055
      nei = &wpa_s->wnm_neighbor_report_elements[i];
1056
      bss = wpa_bss_get_bssid(wpa_s, nei->bssid);
1057
      if (bss && bss->level >
1058
          wpa_s->conf->disassoc_imminent_rssi_threshold) {
1059
        nei->drv_mbo_reject = 0;
1060
        changed = true;
1061
      }
1062
    }
1063
1064
    if (changed) {
1065
      wpa_printf(MSG_DEBUG,
1066
           "WNM: Ignore driver rejection due to imminent disassociation and acceptable RSSI");
1067
      bss = wpa_supplicant_select_bss(wpa_s, ssid,
1068
              &selected_ssid, 1);
1069
    }
1070
  }
1071
#endif /* CONFIG_MBO */
1072
1073
  /*
1074
   * If this is a pre-scan check, returning 0 will trigger a scan and
1075
   * another call. In that case, reject "bad" candidates in the hope of
1076
   * finding a better candidate after scanning.
1077
   *
1078
   * Use a simple heuristic to check whether the selection is reasonable
1079
   * or a scan is a good idea. For that, we need to have found a
1080
   * candidate BSS (which might be the current one), it is up-to-date,
1081
   * and we don't want to immediately roam back again.
1082
   */
1083
927
  if (pre_scan_check) {
1084
927
    struct os_reltime age;
1085
1086
927
    if (!bss)
1087
927
      return 0;
1088
1089
0
    os_reltime_age(&bss->last_update, &age);
1090
0
    if (age.sec >= 10)
1091
0
      return 0;
1092
1093
0
#ifndef CONFIG_NO_ROAMING
1094
0
    if (current_bss && bss != current_bss &&
1095
0
        wpa_supplicant_need_to_roam_within_ess(wpa_s, bss,
1096
0
                 current_bss, false))
1097
0
      return 0;
1098
0
#endif /* CONFIG_NO_ROAMING */
1099
0
  }
1100
1101
0
#ifndef CONFIG_NO_ROAMING
1102
  /* Apply normal roaming rules if we can stay with the current BSS */
1103
0
  if (current_bss && bss != current_bss &&
1104
0
      wpa_scan_res_match(wpa_s, 0, current_bss, wpa_s->current_ssid,
1105
0
             1, 0, false) &&
1106
0
      !wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss, bss,
1107
0
                true))
1108
0
    bss = current_bss;
1109
0
#endif /* CONFIG_NO_ROAMING */
1110
1111
0
  if (!bss) {
1112
0
    wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
1113
0
    status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
1114
0
    goto send_bss_resp_fail;
1115
0
  }
1116
1117
0
  wpa_printf(MSG_DEBUG,
1118
0
       "WNM: Found an acceptable preferred transition candidate BSS "
1119
0
       MACSTR " (RSSI %d, tput: %d  bss-tput: %d)",
1120
0
       MAC2STR(bss->bssid), bss->level, bss->est_throughput,
1121
0
       current_bss ? (int) current_bss->est_throughput : -1);
1122
1123
  /* Associate to the network */
1124
0
  wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
1125
0
  return 1;
1126
1127
0
send_bss_resp_fail:
1128
0
  if (wpa_s->wnm_reply) {
1129
    /* If disassoc imminent is set, we must not reject */
1130
0
    if (wpa_s->wnm_mode &
1131
0
        (WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
1132
0
         WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)) {
1133
0
      wpa_printf(MSG_DEBUG,
1134
0
           "WNM: Accept BTM request because disassociation imminent bit is set");
1135
0
      status = WNM_BSS_TM_ACCEPT;
1136
0
    }
1137
1138
0
    wnm_send_bss_transition_mgmt_resp(wpa_s, status, reason,
1139
0
              0, NULL);
1140
0
  }
1141
1142
0
  wnm_btm_reset(wpa_s);
1143
1144
0
  return 0;
1145
0
}
1146
1147
1148
static int cand_pref_compar(const void *a, const void *b)
1149
2.79k
{
1150
2.79k
  const struct neighbor_report *aa = a;
1151
2.79k
  const struct neighbor_report *bb = b;
1152
1153
2.79k
  if (aa->disassoc_imminent && !bb->disassoc_imminent)
1154
111
    return 1;
1155
2.68k
  if (bb->disassoc_imminent && !aa->disassoc_imminent)
1156
35
    return -1;
1157
1158
2.65k
  if (!aa->preference_present && !bb->preference_present)
1159
2.10k
    return 0;
1160
545
  if (!aa->preference_present)
1161
136
    return 1;
1162
409
  if (!bb->preference_present)
1163
96
    return -1;
1164
313
  if (bb->preference > aa->preference)
1165
106
    return 1;
1166
207
  if (bb->preference < aa->preference)
1167
58
    return -1;
1168
149
  return 0;
1169
207
}
1170
1171
1172
static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
1173
914
{
1174
914
  if (!wpa_s->wnm_neighbor_report_elements)
1175
0
    return;
1176
914
  qsort(wpa_s->wnm_neighbor_report_elements,
1177
914
        wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
1178
914
        cand_pref_compar);
1179
914
}
1180
1181
1182
static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
1183
914
{
1184
914
  unsigned int i;
1185
1186
914
  wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
1187
914
  if (!wpa_s->wnm_neighbor_report_elements)
1188
0
    return;
1189
3.53k
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1190
2.61k
    struct neighbor_report *nei;
1191
1192
2.61k
    nei = &wpa_s->wnm_neighbor_report_elements[i];
1193
2.61k
    wpa_printf(MSG_DEBUG, "%u: " MACSTR
1194
2.61k
         " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
1195
2.61k
         i, MAC2STR(nei->bssid), nei->bssid_info,
1196
2.61k
         nei->regulatory_class,
1197
2.61k
         nei->channel_number, nei->phy_type,
1198
2.61k
         nei->preference_present ? nei->preference : -1,
1199
2.61k
         nei->freq);
1200
2.61k
  }
1201
914
}
1202
1203
1204
static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
1205
0
{
1206
0
  unsigned int i;
1207
1208
0
  for (i = 0; i < wpa_s->hw.num_modes; i++) {
1209
0
    struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
1210
0
    int j;
1211
1212
0
    for (j = 0; j < mode->num_channels; j++) {
1213
0
      struct hostapd_channel_data *chan;
1214
1215
0
      chan = &mode->channels[j];
1216
0
      if (chan->freq == freq &&
1217
0
          !(chan->flag & HOSTAPD_CHAN_DISABLED))
1218
0
        return 1;
1219
0
    }
1220
0
  }
1221
1222
0
  return 0;
1223
0
}
1224
1225
1226
static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
1227
927
{
1228
927
  int *freqs;
1229
927
  int num_freqs = 0;
1230
927
  unsigned int i;
1231
1232
927
  if (!wpa_s->wnm_neighbor_report_elements)
1233
13
    return;
1234
1235
914
  if (wpa_s->hw.modes == NULL)
1236
914
    return;
1237
1238
0
  os_free(wpa_s->next_scan_freqs);
1239
0
  wpa_s->next_scan_freqs = NULL;
1240
1241
0
  freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
1242
0
  if (freqs == NULL)
1243
0
    return;
1244
1245
0
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1246
0
    struct neighbor_report *nei;
1247
1248
0
    nei = &wpa_s->wnm_neighbor_report_elements[i];
1249
1250
0
    if (nei->preference_present && nei->preference == 0)
1251
0
      continue;
1252
1253
0
    if (nei->freq <= 0) {
1254
0
      wpa_printf(MSG_DEBUG,
1255
0
           "WNM: Unknown neighbor operating frequency for "
1256
0
           MACSTR " - scan all channels",
1257
0
           MAC2STR(nei->bssid));
1258
0
      os_free(freqs);
1259
0
      return;
1260
0
    }
1261
0
    if (chan_supported(wpa_s, nei->freq))
1262
0
      add_freq(freqs, &num_freqs, nei->freq);
1263
0
  }
1264
1265
0
  if (num_freqs == 0) {
1266
0
    os_free(freqs);
1267
0
    return;
1268
0
  }
1269
1270
0
  wpa_printf(MSG_DEBUG,
1271
0
       "WNM: Scan %d frequencies based on transition candidate list",
1272
0
       num_freqs);
1273
0
  wpa_s->next_scan_freqs = freqs;
1274
0
}
1275
1276
1277
static int wnm_parse_candidate_list(struct wpa_supplicant *wpa_s,
1278
            const u8 *pos, const u8 *end,
1279
            int *num_valid_candidates)
1280
1.01k
{
1281
1.01k
  *num_valid_candidates = 0;
1282
1283
6.84M
  while (end - pos >= 2 &&
1284
6.84M
         wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) {
1285
6.84M
    u8 tag = *pos++;
1286
6.84M
    u8 len = *pos++;
1287
1288
6.84M
    wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag);
1289
6.84M
    if (len > end - pos) {
1290
60
      wpa_printf(MSG_DEBUG, "WNM: Truncated request");
1291
60
      return -1;
1292
60
    }
1293
6.84M
    if (tag == WLAN_EID_NEIGHBOR_REPORT) {
1294
2.74k
      struct neighbor_report *rep;
1295
1296
2.74k
      if (!wpa_s->wnm_num_neighbor_report) {
1297
955
        wpa_s->wnm_neighbor_report_elements = os_calloc(
1298
955
          WNM_MAX_NEIGHBOR_REPORT,
1299
955
          sizeof(struct neighbor_report));
1300
955
        if (!wpa_s->wnm_neighbor_report_elements)
1301
0
          return -1;
1302
955
      }
1303
1304
2.74k
      rep = &wpa_s->wnm_neighbor_report_elements[
1305
2.74k
        wpa_s->wnm_num_neighbor_report];
1306
2.74k
      wnm_parse_neighbor_report(wpa_s, pos, len, rep);
1307
2.74k
      if ((wpa_s->wnm_mode &
1308
2.74k
           WNM_BSS_TM_REQ_DISASSOC_IMMINENT) &&
1309
2.74k
          ether_addr_equal(rep->bssid, wpa_s->bssid))
1310
139
        rep->disassoc_imminent = 1;
1311
1312
2.74k
      if (rep->preference_present && rep->preference)
1313
237
        *num_valid_candidates += 1;
1314
1315
2.74k
      wpa_s->wnm_num_neighbor_report++;
1316
2.74k
    }
1317
1318
6.84M
    pos += len;
1319
6.84M
  }
1320
1321
955
  return 0;
1322
1.01k
}
1323
1324
1325
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
1326
               const u8 *pos, const u8 *end,
1327
               int reply)
1328
1.03k
{
1329
1.03k
  unsigned int beacon_int;
1330
1.03k
  u8 valid_int;
1331
#ifdef CONFIG_MBO
1332
  const u8 *vendor;
1333
#endif /* CONFIG_MBO */
1334
1.03k
  bool disassoc_imminent;
1335
1.03k
  int num_valid_candidates;
1336
1337
1.03k
  if (wpa_s->disable_mbo_oce || wpa_s->conf->disable_btm)
1338
0
    return;
1339
1340
1.03k
  if (end - pos < 5)
1341
4
    return;
1342
1343
1.03k
  if (wpa_s->current_bss)
1344
1.03k
    beacon_int = wpa_s->current_bss->beacon_int;
1345
0
  else
1346
0
    beacon_int = 100; /* best guess */
1347
1348
1.03k
  wnm_btm_reset(wpa_s);
1349
1350
1.03k
  wpa_s->wnm_dialog_token = pos[0];
1351
1.03k
  wpa_s->wnm_mode = pos[1];
1352
1.03k
  wpa_s->wnm_disassoc_timer = WPA_GET_LE16(pos + 2);
1353
1.03k
  wpa_s->wnm_link_removal = false;
1354
1.03k
  valid_int = pos[4];
1355
1.03k
  wpa_s->wnm_reply = reply;
1356
1357
1.03k
  wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
1358
1.03k
       "dialog_token=%u request_mode=0x%x "
1359
1.03k
       "disassoc_timer=%u validity_interval=%u",
1360
1.03k
       wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
1361
1.03k
       wpa_s->wnm_disassoc_timer, valid_int);
1362
1363
1.03k
  if (!wpa_s->wnm_dialog_token) {
1364
1
    wpa_printf(MSG_DEBUG, "WNM: Invalid dialog token");
1365
1
    goto reset;
1366
1
  }
1367
1368
#if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
1369
  if (wpa_s->reject_btm_req_reason) {
1370
    wpa_printf(MSG_INFO,
1371
         "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
1372
         wpa_s->reject_btm_req_reason);
1373
    wnm_send_bss_transition_mgmt_resp(
1374
      wpa_s, wpa_s->reject_btm_req_reason,
1375
      MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
1376
    goto reset;
1377
  }
1378
#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
1379
1380
1.03k
  pos += 5;
1381
1382
1.03k
  if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
1383
35
    if (end - pos < 12) {
1384
6
      wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
1385
6
      goto reset;
1386
6
    }
1387
29
    os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
1388
29
    pos += 12; /* BSS Termination Duration */
1389
29
  }
1390
1391
1.02k
  if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
1392
85
    char url[256];
1393
85
    u8 url_len;
1394
1395
85
    if (end - pos < 1) {
1396
2
      wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
1397
2
           "Management Request (URL)");
1398
2
      goto reset;
1399
2
    }
1400
83
    url_len = *pos++;
1401
83
    if (url_len > end - pos) {
1402
10
      wpa_printf(MSG_DEBUG,
1403
10
           "WNM: Invalid BSS Transition Management Request (URL truncated)");
1404
10
      goto reset;
1405
10
    }
1406
73
    os_memcpy(url, pos, url_len);
1407
73
    url[url_len] = '\0';
1408
73
    pos += url_len;
1409
1410
73
    wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
1411
73
      wpa_sm_pmf_enabled(wpa_s->wpa),
1412
73
      wpa_s->wnm_disassoc_timer * beacon_int * 128 / 125,
1413
73
      url);
1414
73
  }
1415
1416
1.01k
  disassoc_imminent = wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
1417
1418
  /*
1419
   * Based on IEEE P802.11be/D5.0, when a station is a non-AP MLD with
1420
   * more than one affiliated link, the Link Removal Imminent field is
1421
   * set to 1, and the BSS Termination Included field is set to 1, only
1422
   * one of the links is removed and the other links remain associated.
1423
   * Ignore the Disassociation Imminent field in such a case.
1424
   *
1425
   * TODO: We should check if the AP has more than one link.
1426
   * TODO: We should pass the RX link and use that
1427
   */
1428
1.01k
  if (disassoc_imminent && wpa_s->valid_links &&
1429
1.01k
      (wpa_s->wnm_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT) &&
1430
1.01k
      (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED)) {
1431
    /* If we still have a link, then just accept the request */
1432
0
    if (wpa_s->valid_links & (wpa_s->valid_links - 1)) {
1433
0
      wpa_printf(MSG_INFO,
1434
0
           "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
1435
0
      disassoc_imminent = false;
1436
1437
0
      wnm_send_bss_transition_mgmt_resp(
1438
0
        wpa_s, WNM_BSS_TM_ACCEPT, 0, 0, NULL);
1439
1440
0
      goto reset;
1441
0
    }
1442
1443
    /* The last link is being removed (which must be the assoc link)
1444
     */
1445
0
    wpa_s->wnm_link_removal = true;
1446
0
    wpa_s->wnm_disassoc_mld = false;
1447
0
    os_memcpy(wpa_s->wnm_disassoc_addr,
1448
0
        wpa_s->links[wpa_s->mlo_assoc_link_id].bssid,
1449
0
        ETH_ALEN);
1450
1.01k
  } else if (wpa_s->valid_links) {
1451
0
    wpa_s->wnm_disassoc_mld = true;
1452
0
    os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->ap_mld_addr,
1453
0
        ETH_ALEN);
1454
1.01k
  } else {
1455
1.01k
    wpa_s->wnm_disassoc_mld = false;
1456
1.01k
    os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->bssid, ETH_ALEN);
1457
1.01k
  }
1458
1459
1.01k
  if (disassoc_imminent)
1460
444
    wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
1461
444
      "Disassociation Timer %u", wpa_s->wnm_disassoc_timer);
1462
1463
#ifdef CONFIG_MBO
1464
  vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
1465
  if (vendor)
1466
    wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
1467
#endif /* CONFIG_MBO */
1468
1469
1.01k
  if (wnm_parse_candidate_list(wpa_s, pos, end,
1470
1.01k
             &num_valid_candidates) < 0)
1471
60
    goto reset;
1472
1473
955
  if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
1474
512
    if (!wpa_s->wnm_num_neighbor_report) {
1475
16
      wpa_printf(MSG_DEBUG,
1476
16
           "WNM: Candidate list included bit is set, but no candidates found");
1477
16
      wnm_send_bss_transition_mgmt_resp(
1478
16
        wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
1479
16
        MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
1480
16
        NULL);
1481
16
      goto reset;
1482
16
    }
1483
496
    wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
1484
496
  }
1485
1486
939
  if (wpa_s->wnm_num_neighbor_report) {
1487
914
    unsigned int valid_ms;
1488
1489
914
    wnm_sort_cand_list(wpa_s);
1490
914
    wnm_dump_cand_list(wpa_s);
1491
914
    valid_ms = valid_int * beacon_int * 128 / 125;
1492
914
    wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
1493
914
         valid_ms);
1494
914
    os_get_reltime(&wpa_s->wnm_cand_valid_until);
1495
914
    os_reltime_add_ms(&wpa_s->wnm_cand_valid_until, valid_ms);
1496
914
  } else if (!disassoc_imminent) {
1497
12
    enum bss_trans_mgmt_status_code status;
1498
1499
    /* No candidate list and disassociation is not imminent */
1500
1501
12
    if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) ||
1502
12
        wpa_s->wnm_link_removal)
1503
2
      status = WNM_BSS_TM_ACCEPT;
1504
10
    else {
1505
10
      wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
1506
10
      status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1507
10
    }
1508
1509
12
    if (reply)
1510
9
      wnm_send_bss_transition_mgmt_resp(
1511
9
        wpa_s, status,
1512
9
        MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
1513
9
        NULL);
1514
1515
12
    goto reset;
1516
12
  }
1517
1518
  /*
1519
   * Try fetching the latest scan results from the kernel.
1520
   * This can help in finding more up-to-date information should
1521
   * the driver have done some internal scanning operations after
1522
   * the last scan result update in wpa_supplicant.
1523
   *
1524
   * It is not a new scan, this does not update the last_scan
1525
   * timestamp nor will it expire old BSSs.
1526
   */
1527
927
  wpa_supplicant_update_scan_results(wpa_s, NULL);
1528
927
  if (wnm_scan_process(wpa_s, true) > 0)
1529
0
    return;
1530
927
  wpa_printf(MSG_DEBUG,
1531
927
       "WNM: No valid match in previous scan results - try a new scan");
1532
1533
  /*
1534
   * If we have a fixed BSSID configured, just reject at this point.
1535
   * NOTE: We could actually check if we are allowed to stay (and we do
1536
   * above if we have scan results available).
1537
   */
1538
927
  if (wpa_s->current_ssid && wpa_s->current_ssid->bssid_set) {
1539
0
    wpa_printf(MSG_DEBUG, "WNM: Fixed BSSID, rejecting request");
1540
1541
0
    if (reply)
1542
0
      wnm_send_bss_transition_mgmt_resp(
1543
0
        wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
1544
0
        MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
1545
0
        NULL);
1546
1547
0
    goto reset;
1548
0
  }
1549
1550
927
  wnm_set_scan_freqs(wpa_s);
1551
927
  if (num_valid_candidates == 1) {
1552
    /* Any invalid candidate was sorted to the end */
1553
39
    os_memcpy(wpa_s->next_scan_bssid,
1554
39
        wpa_s->wnm_neighbor_report_elements[0].bssid,
1555
39
        ETH_ALEN);
1556
39
    wpa_printf(MSG_DEBUG,
1557
39
        "WNM: Scan only for a specific BSSID since there is only a single candidate "
1558
39
        MACSTR, MAC2STR(wpa_s->next_scan_bssid));
1559
39
  }
1560
927
  wpa_s->wnm_transition_scan = true;
1561
927
  wpa_supplicant_req_scan(wpa_s, 0, 0);
1562
1563
  /* Continue from scan handler */
1564
927
  return;
1565
1566
107
reset:
1567
107
  wnm_btm_reset(wpa_s);
1568
107
}
1569
1570
1571
int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
1572
         size_t data_len)
1573
0
{
1574
0
  const struct ieee80211_mgmt *frame =
1575
0
    (const struct ieee80211_mgmt *) data;
1576
1577
0
  if (data_len <
1578
0
      IEEE80211_HDRLEN + sizeof(frame->u.action.u.bss_tm_resp) ||
1579
0
      frame->u.action.category != WLAN_ACTION_WNM ||
1580
0
      frame->u.action.u.bss_tm_resp.action != WNM_BSS_TRANS_MGMT_RESP ||
1581
0
      frame->u.action.u.bss_tm_resp.status_code != WNM_BSS_TM_ACCEPT)
1582
0
    return -1;
1583
1584
  /*
1585
   * If disassoc imminent bit was set in the request, the response may
1586
   * indicate accept even if no candidate was found, so bail out here.
1587
   */
1588
0
  if (!wpa_s->wnm_target_bss) {
1589
0
    wpa_printf(MSG_DEBUG, "WNM: Target BSS is not set");
1590
0
    return 0;
1591
0
  }
1592
1593
0
  if (!wpa_s->current_ssid)
1594
0
    return 0;
1595
1596
0
  wnm_bss_tm_connect(wpa_s, wpa_s->wnm_target_bss, wpa_s->current_ssid,
1597
0
         0);
1598
1599
0
  wpa_s->wnm_target_bss = NULL;
1600
0
  return 0;
1601
0
}
1602
1603
1604
0
#define BTM_QUERY_MIN_SIZE  4
1605
1606
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
1607
               u8 query_reason,
1608
               const char *btm_candidates,
1609
               int cand_list)
1610
0
{
1611
0
  struct wpabuf *buf;
1612
0
  int ret;
1613
1614
0
  wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
1615
0
       MACSTR " query_reason=%u%s",
1616
0
       MAC2STR(wpa_s->bssid), query_reason,
1617
0
       cand_list ? " candidate list" : "");
1618
1619
0
  buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
1620
0
  if (!buf)
1621
0
    return -1;
1622
1623
0
  wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1624
0
  wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
1625
0
  wpabuf_put_u8(buf, 1);
1626
0
  wpabuf_put_u8(buf, query_reason);
1627
1628
0
  if (cand_list)
1629
0
    wnm_add_cand_list(wpa_s, &buf);
1630
1631
0
  if (btm_candidates) {
1632
0
    const size_t max_len = 1000;
1633
1634
0
    ret = wpabuf_resize(&buf, max_len);
1635
0
    if (ret < 0) {
1636
0
      wpabuf_free(buf);
1637
0
      return ret;
1638
0
    }
1639
1640
0
    ret = ieee802_11_parse_candidate_list(btm_candidates,
1641
0
                  wpabuf_put(buf, 0),
1642
0
                  max_len);
1643
0
    if (ret < 0) {
1644
0
      wpabuf_free(buf);
1645
0
      return ret;
1646
0
    }
1647
1648
0
    wpabuf_put(buf, ret);
1649
0
  }
1650
1651
0
  ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1652
0
          wpa_s->own_addr, wpa_s->bssid,
1653
0
          wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1654
1655
0
  wpabuf_free(buf);
1656
0
  return ret;
1657
0
}
1658
1659
1660
static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
1661
              const u8 *sa, const u8 *data,
1662
              int len)
1663
277
{
1664
277
  const u8 *pos, *end, *next;
1665
277
  u8 ie, ie_len;
1666
1667
277
  pos = data;
1668
277
  end = data + len;
1669
1670
1.46M
  while (end - pos > 1) {
1671
1.46M
    ie = *pos++;
1672
1.46M
    ie_len = *pos++;
1673
1.46M
    wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
1674
1.46M
         ie, ie_len);
1675
1.46M
    if (ie_len > end - pos) {
1676
60
      wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
1677
60
           "subelement");
1678
60
      break;
1679
60
    }
1680
1.46M
    next = pos + ie_len;
1681
1.46M
    if (ie_len < 4) {
1682
1.40M
      pos = next;
1683
1.40M
      continue;
1684
1.40M
    }
1685
66.5k
    wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
1686
66.5k
         WPA_GET_BE24(pos), pos[3]);
1687
1688
66.5k
#ifdef CONFIG_HS20
1689
66.5k
    if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
1690
66.5k
        WPA_GET_BE24(pos) == OUI_WFA &&
1691
66.5k
        pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
1692
4.49k
      const u8 *ie_end;
1693
4.49k
      u8 url_len;
1694
4.49k
      char *url;
1695
4.49k
      u8 code;
1696
4.49k
      u16 reauth_delay;
1697
1698
4.49k
      ie_end = pos + ie_len;
1699
4.49k
      pos += 4;
1700
4.49k
      code = *pos++;
1701
4.49k
      reauth_delay = WPA_GET_LE16(pos);
1702
4.49k
      pos += 2;
1703
4.49k
      url_len = *pos++;
1704
4.49k
      wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
1705
4.49k
           "Imminent - Reason Code %u   "
1706
4.49k
           "Re-Auth Delay %u  URL Length %u",
1707
4.49k
           code, reauth_delay, url_len);
1708
4.49k
      if (url_len > ie_end - pos)
1709
13
        break;
1710
4.47k
      url = os_malloc(url_len + 1);
1711
4.47k
      if (url == NULL)
1712
0
        break;
1713
4.47k
      os_memcpy(url, pos, url_len);
1714
4.47k
      url[url_len] = '\0';
1715
4.47k
      hs20_rx_deauth_imminent_notice(wpa_s, code,
1716
4.47k
                   reauth_delay, url);
1717
4.47k
      os_free(url);
1718
4.47k
      pos = next;
1719
4.47k
      continue;
1720
4.47k
    }
1721
1722
62.0k
    if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
1723
62.0k
        WPA_GET_BE24(pos) == OUI_WFA &&
1724
62.0k
        pos[3] == HS20_WNM_T_C_ACCEPTANCE) {
1725
774
      const u8 *ie_end;
1726
774
      u8 url_len;
1727
774
      char *url;
1728
1729
774
      ie_end = pos + ie_len;
1730
774
      pos += 4;
1731
774
      url_len = *pos++;
1732
774
      wpa_printf(MSG_DEBUG,
1733
774
           "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)",
1734
774
           url_len);
1735
774
      if (url_len > ie_end - pos)
1736
11
        break;
1737
763
      url = os_malloc(url_len + 1);
1738
763
      if (!url)
1739
0
        break;
1740
763
      os_memcpy(url, pos, url_len);
1741
763
      url[url_len] = '\0';
1742
763
      hs20_rx_t_c_acceptance(wpa_s, url);
1743
763
      os_free(url);
1744
763
      pos = next;
1745
763
      continue;
1746
763
    }
1747
61.3k
#endif /* CONFIG_HS20 */
1748
1749
61.3k
    pos = next;
1750
61.3k
  }
1751
277
}
1752
1753
1754
static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
1755
          const u8 *sa, const u8 *frm, int len)
1756
289
{
1757
289
  const u8 *pos, *end;
1758
289
  u8 dialog_token, type;
1759
1760
  /* Dialog Token [1] | Type [1] | Subelements */
1761
1762
289
  if (len < 2 || sa == NULL)
1763
2
    return;
1764
287
  end = frm + len;
1765
287
  pos = frm;
1766
287
  dialog_token = *pos++;
1767
287
  type = *pos++;
1768
1769
287
  wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
1770
287
    "(dialog_token %u type %u sa " MACSTR ")",
1771
287
    dialog_token, type, MAC2STR(sa));
1772
287
  wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
1773
287
        pos, end - pos);
1774
1775
287
  if (wpa_s->wpa_state != WPA_COMPLETED ||
1776
287
      (!ether_addr_equal(sa, wpa_s->bssid) &&
1777
287
       (!wpa_s->valid_links ||
1778
0
        !ether_addr_equal(sa, wpa_s->ap_mld_addr)))) {
1779
0
    wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
1780
0
      "from our AP - ignore it");
1781
0
    return;
1782
0
  }
1783
1784
287
  switch (type) {
1785
277
  case 1:
1786
277
    ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
1787
277
    break;
1788
10
  default:
1789
10
    wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
1790
10
      "WNM-Notification type %u", type);
1791
10
    break;
1792
287
  }
1793
287
}
1794
1795
1796
static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s,
1797
               const u8 *sa, const u8 *frm,
1798
               int len)
1799
2
{
1800
2
  u8 dialog_token, req_info, auto_report, timeout;
1801
1802
2
  if (!wpa_s->conf->coloc_intf_reporting)
1803
2
    return;
1804
1805
  /* Dialog Token [1] | Request Info [1] */
1806
1807
0
  if (len < 2)
1808
0
    return;
1809
0
  dialog_token = frm[0];
1810
0
  req_info = frm[1];
1811
0
  auto_report = req_info & 0x03;
1812
0
  timeout = req_info >> 2;
1813
1814
0
  wpa_dbg(wpa_s, MSG_DEBUG,
1815
0
    "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")",
1816
0
    dialog_token, auto_report, timeout, MAC2STR(sa));
1817
1818
0
  if (dialog_token == 0)
1819
0
    return; /* only nonzero values are used for request */
1820
1821
0
  if (wpa_s->wpa_state != WPA_COMPLETED ||
1822
0
      (!ether_addr_equal(sa, wpa_s->bssid) &&
1823
0
       (!wpa_s->valid_links ||
1824
0
        !ether_addr_equal(sa, wpa_s->ap_mld_addr)))) {
1825
0
    wpa_dbg(wpa_s, MSG_DEBUG,
1826
0
      "WNM: Collocated Interference Request frame not from current AP - ignore it");
1827
0
    return;
1828
0
  }
1829
1830
0
  wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u",
1831
0
    dialog_token, auto_report, timeout);
1832
0
  wpa_s->coloc_intf_dialog_token = dialog_token;
1833
0
  wpa_s->coloc_intf_auto_report = auto_report;
1834
0
  wpa_s->coloc_intf_timeout = timeout;
1835
0
}
1836
1837
1838
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
1839
            const struct ieee80211_mgmt *mgmt, size_t len)
1840
1.40k
{
1841
1.40k
  const u8 *pos, *end;
1842
1.40k
  u8 act;
1843
1844
1.40k
  if (len < IEEE80211_HDRLEN + 2)
1845
24
    return;
1846
1847
1.38k
  pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
1848
1.38k
  act = *pos++;
1849
1.38k
  end = ((const u8 *) mgmt) + len;
1850
1851
1.38k
  wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
1852
1.38k
       act, MAC2STR(mgmt->sa));
1853
1.38k
  if (wpa_s->wpa_state < WPA_ASSOCIATED ||
1854
1.38k
      (!ether_addr_equal(mgmt->sa, wpa_s->bssid) &&
1855
1.38k
       (!wpa_s->valid_links ||
1856
52
        !ether_addr_equal(mgmt->sa, wpa_s->ap_mld_addr)))) {
1857
52
    wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
1858
52
         "frame");
1859
52
    return;
1860
52
  }
1861
1862
1.33k
  switch (act) {
1863
1.03k
  case WNM_BSS_TRANS_MGMT_REQ:
1864
1.03k
    ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
1865
1.03k
             !(mgmt->da[0] & 0x01));
1866
1.03k
    break;
1867
2
  case WNM_SLEEP_MODE_RESP:
1868
2
    ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
1869
2
    break;
1870
289
  case WNM_NOTIFICATION_REQ:
1871
289
    ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
1872
289
    break;
1873
2
  case WNM_COLLOCATED_INTERFERENCE_REQ:
1874
2
    ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos,
1875
2
             end - pos);
1876
2
    break;
1877
1
  default:
1878
1
    wpa_printf(MSG_ERROR, "WNM: Unknown request");
1879
1
    break;
1880
1.33k
  }
1881
1.33k
}
1882
1883
1884
int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
1885
             const struct wpabuf *elems)
1886
0
{
1887
0
  struct wpabuf *buf;
1888
0
  int ret;
1889
1890
0
  if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems)
1891
0
    return -1;
1892
1893
0
  wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to "
1894
0
       MACSTR " (dialog token %u)",
1895
0
       MAC2STR(wpa_s->bssid), dialog_token);
1896
1897
0
  buf = wpabuf_alloc(3 + wpabuf_len(elems));
1898
0
  if (!buf)
1899
0
    return -1;
1900
1901
0
  wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1902
0
  wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT);
1903
0
  wpabuf_put_u8(buf, dialog_token);
1904
0
  wpabuf_put_buf(buf, elems);
1905
1906
0
  ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1907
0
          wpa_s->own_addr, wpa_s->bssid,
1908
0
          wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1909
0
  wpabuf_free(buf);
1910
0
  return ret;
1911
0
}
1912
1913
1914
void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
1915
            struct wpabuf *elems)
1916
0
{
1917
0
  if (elems && wpabuf_len(elems) == 0) {
1918
0
    wpabuf_free(elems);
1919
0
    elems = NULL;
1920
0
  }
1921
1922
  /* NOTE: The elements are not stored as they are only send out once */
1923
1924
0
  if (wpa_s->conf->coloc_intf_reporting && elems &&
1925
0
      wpa_s->coloc_intf_dialog_token &&
1926
0
      (wpa_s->coloc_intf_auto_report == 1 ||
1927
0
       wpa_s->coloc_intf_auto_report == 3)) {
1928
    /* TODO: Check that there has not been less than
1929
     * wpa_s->coloc_intf_timeout * 200 TU from the last report.
1930
     */
1931
0
    wnm_send_coloc_intf_report(wpa_s,
1932
0
             wpa_s->coloc_intf_dialog_token,
1933
0
             elems);
1934
0
  }
1935
1936
0
  wpabuf_free(elems);
1937
0
}
1938
1939
1940
void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s)
1941
0
{
1942
0
  wpa_s->coloc_intf_dialog_token = 0;
1943
0
  wpa_s->coloc_intf_auto_report = 0;
1944
0
}
1945
1946
1947
bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
1948
0
{
1949
0
  int i;
1950
1951
  /*
1952
   * In case disassociation imminent is set, do no try to use a BSS to
1953
   * which we are connected.
1954
   */
1955
0
  if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
1956
0
    if (!wpa_s->wnm_disassoc_mld) {
1957
0
      if (ether_addr_equal(bss->bssid,
1958
0
               wpa_s->wnm_disassoc_addr))
1959
0
        return true;
1960
0
    } else {
1961
0
      if (ether_addr_equal(bss->mld_addr,
1962
0
               wpa_s->wnm_disassoc_addr))
1963
0
        return true;
1964
0
    }
1965
0
  }
1966
1967
0
  for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1968
0
    struct neighbor_report *nei;
1969
1970
0
    nei = &wpa_s->wnm_neighbor_report_elements[i];
1971
0
    if (!ether_addr_equal(nei->bssid, bss->bssid))
1972
0
      continue;
1973
1974
0
    if (nei->preference_present && nei->preference == 0)
1975
0
      return true;
1976
1977
#ifdef CONFIG_MBO
1978
    if (nei->drv_mbo_reject)
1979
      return true;
1980
#endif /* CONFIG_MBO */
1981
1982
0
    break;
1983
0
  }
1984
1985
  /* If the abridged bit is set, the BSS must be a known neighbor. */
1986
0
  if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ABRIDGED) &&
1987
0
      wpa_s->wnm_num_neighbor_report == i)
1988
0
    return true;
1989
1990
0
  return false;
1991
0
}