Coverage Report

Created: 2026-09-14 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hostap/wpa_supplicant/mbo.c
Line
Count
Source
1
/*
2
 * wpa_supplicant - MBO
3
 *
4
 * Copyright(c) 2015 Intel Deutschland GmbH
5
 * Contact Information:
6
 * Intel Linux Wireless <ilw@linux.intel.com>
7
 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
8
 *
9
 * This software may be distributed under the terms of the BSD license.
10
 * See README for more details.
11
 */
12
13
#include "utils/includes.h"
14
15
#include "utils/common.h"
16
#include "common/ieee802_11_defs.h"
17
#include "common/gas.h"
18
#include "rsn_supp/wpa.h"
19
#include "config.h"
20
#include "wpa_supplicant_i.h"
21
#include "driver_i.h"
22
#include "bss.h"
23
#include "scan.h"
24
25
/* type + length + oui + oui type */
26
0
#define MBO_IE_HEADER 6
27
28
29
static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason)
30
0
{
31
0
  if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE)
32
0
    return -1;
33
34
  /* Only checking the validity of the channel and oper_class */
35
0
  if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1)
36
0
    return -1;
37
38
0
  return 0;
39
0
}
40
41
42
const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr)
43
0
{
44
0
  const u8 *mbo;
45
0
  u8 ie_len = mbo_ie[1];
46
47
0
  if (ie_len < MBO_IE_HEADER - 2)
48
0
    return NULL;
49
0
  mbo = mbo_ie + MBO_IE_HEADER;
50
51
0
  return get_ie(mbo, 2 + ie_len - MBO_IE_HEADER, attr);
52
0
}
53
54
55
const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len,
56
         enum mbo_attr_id attr)
57
0
{
58
0
  const u8 *mbo_ie;
59
60
0
  mbo_ie = get_vendor_ie(ies, ies_len, MBO_IE_VENDOR_TYPE);
61
0
  if (!mbo_ie)
62
0
    return NULL;
63
64
0
  return mbo_attr_from_mbo_ie(mbo_ie, attr);
65
0
}
66
67
68
static const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss,
69
          enum mbo_attr_id attr, bool beacon)
70
0
{
71
0
  const u8 *mbo, *end;
72
73
0
  if (!bss)
74
0
    return NULL;
75
76
0
  if (beacon)
77
0
    mbo = wpa_bss_get_vendor_ie_beacon(bss, MBO_IE_VENDOR_TYPE);
78
0
  else
79
0
    mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
80
0
  if (!mbo)
81
0
    return NULL;
82
83
0
  end = mbo + 2 + mbo[1];
84
0
  mbo += MBO_IE_HEADER;
85
86
0
  return get_ie(mbo, end - mbo, attr);
87
0
}
88
89
90
const u8 * wpas_mbo_check_assoc_disallow(struct wpa_bss *bss)
91
0
{
92
0
  const u8 *assoc_disallow;
93
94
0
  assoc_disallow = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_ASSOC_DISALLOW,
95
0
                 bss->beacon_newer);
96
0
  if (assoc_disallow && assoc_disallow[1] >= 1)
97
0
    return assoc_disallow;
98
99
0
  return NULL;
100
0
}
101
102
103
void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
104
      struct wpa_ssid *ssid)
105
0
{
106
0
  const u8 *rsne, *mbo, *oce;
107
0
  struct wpa_ie_data ie;
108
109
0
  wpa_s->disable_mbo_oce = 0;
110
0
  if (!bss)
111
0
    return;
112
0
  mbo = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND, false);
113
0
  oce = wpas_mbo_get_bss_attr(bss, OCE_ATTR_ID_CAPA_IND, false);
114
0
  if (!mbo && !oce)
115
0
    return;
116
0
  if (oce && oce[1] >= 1 && (oce[2] & OCE_IS_STA_CFON))
117
0
    return; /* STA-CFON is not required to enable PMF */
118
0
  rsne = wpa_bss_get_rsne(wpa_s, bss, ssid, false);
119
0
  if (!rsne || wpa_parse_wpa_ie(rsne, 2 + rsne[1], &ie) < 0)
120
0
    return; /* AP is not using RSN */
121
122
0
  if (!(ie.capabilities & WPA_CAPABILITY_MFPC))
123
0
    wpa_s->disable_mbo_oce = 1; /* AP uses RSN without PMF */
124
0
  if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
125
0
    wpa_s->disable_mbo_oce = 1; /* STA uses RSN without PMF */
126
0
  if (wpa_s->disable_mbo_oce)
127
0
    wpa_printf(MSG_INFO,
128
0
         "MBO: Disable MBO/OCE due to misbehaving AP not having enabled PMF");
129
0
}
130
131
132
static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s,
133
               struct wpabuf *mbo,
134
               u8 start, u8 end)
135
0
{
136
0
  u8 i;
137
138
0
  wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class);
139
140
0
  for (i = start; i < end; i++)
141
0
    wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan);
142
143
0
  wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference);
144
0
  wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason);
145
0
}
146
147
148
static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf *mbo, size_t size)
149
0
{
150
0
  wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
151
0
  wpabuf_put_u8(mbo, size); /* Length */
152
0
}
153
154
155
static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s,
156
          struct wpabuf *mbo, u8 start, u8 end)
157
0
{
158
0
  size_t size = end - start + 3;
159
160
0
  if (size + 2 > wpabuf_tailroom(mbo))
161
0
    return;
162
163
0
  wpas_mbo_non_pref_chan_attr_hdr(mbo, size);
164
0
  wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
165
0
}
166
167
168
static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len)
169
0
{
170
0
  wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC);
171
0
  wpabuf_put_u8(mbo, len); /* Length */
172
0
  wpabuf_put_be24(mbo, OUI_WFA);
173
0
  wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
174
0
}
175
176
177
static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s,
178
                struct wpabuf *mbo, u8 start,
179
                u8 end)
180
0
{
181
0
  size_t size = end - start + 7;
182
183
0
  if (size + 2 > wpabuf_tailroom(mbo))
184
0
    return;
185
186
0
  wpas_mbo_non_pref_chan_subelem_hdr(mbo, size);
187
0
  wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
188
0
}
189
190
191
static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s,
192
           struct wpabuf *mbo, int subelement)
193
0
{
194
0
  u8 i, start = 0;
195
0
  struct wpa_mbo_non_pref_channel *start_pref;
196
197
0
  if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) {
198
0
    if (subelement)
199
0
      wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4);
200
0
    else
201
0
      wpas_mbo_non_pref_chan_attr_hdr(mbo, 0);
202
0
    return;
203
0
  }
204
0
  start_pref = &wpa_s->non_pref_chan[0];
205
206
0
  for (i = 1; i <= wpa_s->non_pref_chan_num; i++) {
207
0
    struct wpa_mbo_non_pref_channel *non_pref = NULL;
208
209
0
    if (i < wpa_s->non_pref_chan_num)
210
0
      non_pref = &wpa_s->non_pref_chan[i];
211
0
    if (!non_pref ||
212
0
        non_pref->oper_class != start_pref->oper_class ||
213
0
        non_pref->reason != start_pref->reason ||
214
0
        non_pref->preference != start_pref->preference) {
215
0
      if (subelement)
216
0
        wpas_mbo_non_pref_chan_subelement(wpa_s, mbo,
217
0
                  start, i);
218
0
      else
219
0
        wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start,
220
0
                  i);
221
222
0
      if (!non_pref)
223
0
        return;
224
225
0
      start = i;
226
0
      start_pref = non_pref;
227
0
    }
228
0
  }
229
0
}
230
231
232
int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
233
    int add_oce_capa)
234
0
{
235
0
  struct wpabuf *mbo;
236
0
  int res;
237
238
0
  if (len < MBO_IE_HEADER + 3 + 7 +
239
0
      ((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
240
0
    return 0;
241
242
  /* Leave room for the MBO IE header */
243
0
  mbo = wpabuf_alloc(len - MBO_IE_HEADER);
244
0
  if (!mbo)
245
0
    return 0;
246
247
  /* Add non-preferred channels attribute */
248
0
  wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0);
249
250
  /*
251
   * Send cellular capabilities attribute even if AP does not advertise
252
   * cellular capabilities.
253
   */
254
0
  wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA);
255
0
  wpabuf_put_u8(mbo, 1);
256
0
  wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa);
257
258
  /* Add OCE capability indication attribute if OCE is enabled */
259
0
  if ((wpa_s->enable_oce & OCE_STA) && add_oce_capa) {
260
0
    wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND);
261
0
    wpabuf_put_u8(mbo, 1);
262
0
    wpabuf_put_u8(mbo, OCE_RELEASE);
263
0
  }
264
265
0
  res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
266
0
  if (!res)
267
0
    wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE");
268
269
0
  wpabuf_free(mbo);
270
0
  return res;
271
0
}
272
273
274
static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s,
275
             const u8 *data, size_t len)
276
0
{
277
0
  struct wpabuf *buf;
278
0
  int res;
279
280
  /*
281
   * Send WNM-Notification Request frame only in case of a change in
282
   * non-preferred channels list during association, if the AP supports
283
   * MBO.
284
   */
285
0
  if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_bss ||
286
0
      !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE))
287
0
    return;
288
289
0
  buf = wpabuf_alloc(4 + len);
290
0
  if (!buf)
291
0
    return;
292
293
0
  wpabuf_put_u8(buf, WLAN_ACTION_WNM);
294
0
  wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
295
0
  wpa_s->mbo_wnm_token++;
296
0
  if (wpa_s->mbo_wnm_token == 0)
297
0
    wpa_s->mbo_wnm_token++;
298
0
  wpabuf_put_u8(buf, wpa_s->mbo_wnm_token);
299
0
  wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */
300
301
0
  wpabuf_put_data(buf, data, len);
302
303
0
  res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
304
0
          wpa_s->own_addr, wpa_s->bssid,
305
0
          wpabuf_head(buf), wpabuf_len(buf), 0);
306
0
  if (res < 0)
307
0
    wpa_printf(MSG_DEBUG,
308
0
         "Failed to send WNM-Notification Request frame with non-preferred channel list");
309
310
0
  wpabuf_free(buf);
311
0
}
312
313
314
static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s)
315
0
{
316
0
  struct wpabuf *buf;
317
318
0
  buf = wpabuf_alloc(512);
319
0
  if (!buf)
320
0
    return;
321
322
0
  wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1);
323
0
  wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf),
324
0
               wpabuf_len(buf));
325
0
  wpas_update_mbo_connect_params(wpa_s);
326
0
  wpabuf_free(buf);
327
0
}
328
329
330
static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a,
331
           struct wpa_mbo_non_pref_channel *b)
332
0
{
333
0
  return a->oper_class == b->oper_class && a->chan == b->chan;
334
0
}
335
336
337
/*
338
 * wpa_non_pref_chan_cmp - Compare two channels for sorting
339
 *
340
 * In MBO IE non-preferred channel subelement we can put many channels in an
341
 * attribute if they are in the same operating class and have the same
342
 * preference and reason. To make it easy for the functions that build
343
 * the IE attributes and WNM Request subelements, save the channels sorted
344
 * by their oper_class and reason.
345
 */
346
static int wpa_non_pref_chan_cmp(const void *_a, const void *_b)
347
0
{
348
0
  const struct wpa_mbo_non_pref_channel *a = _a, *b = _b;
349
350
0
  if (a->oper_class != b->oper_class)
351
0
    return (int) a->oper_class - (int) b->oper_class;
352
0
  if (a->reason != b->reason)
353
0
    return (int) a->reason - (int) b->reason;
354
0
  return (int) a->preference - (int) b->preference;
355
0
}
356
357
358
int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
359
          const char *non_pref_chan)
360
0
{
361
0
  char *cmd, *token, *context = NULL;
362
0
  struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans;
363
0
  size_t num = 0, size = 0;
364
0
  unsigned i;
365
366
0
  wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s",
367
0
       non_pref_chan ? non_pref_chan : "N/A");
368
369
  /*
370
   * The shortest channel configuration is 7 characters - 3 colons and
371
   * 4 values.
372
   */
373
0
  if (!non_pref_chan || os_strlen(non_pref_chan) < 7)
374
0
    goto update;
375
376
0
  cmd = os_strdup(non_pref_chan);
377
0
  if (!cmd)
378
0
    return -1;
379
380
0
  while ((token = str_token(cmd, " ", &context))) {
381
0
    struct wpa_mbo_non_pref_channel *chan;
382
0
    int ret;
383
0
    unsigned int _oper_class;
384
0
    unsigned int _chan;
385
0
    unsigned int _preference;
386
0
    unsigned int _reason;
387
388
0
    if (num == size) {
389
0
      size = size ? size * 2 : 1;
390
0
      tmp_chans = os_realloc_array(chans, size,
391
0
                 sizeof(*chans));
392
0
      if (!tmp_chans) {
393
0
        wpa_printf(MSG_ERROR,
394
0
             "Couldn't reallocate non_pref_chan");
395
0
        goto fail;
396
0
      }
397
0
      chans = tmp_chans;
398
0
    }
399
400
0
    chan = &chans[num];
401
402
0
    ret = sscanf(token, "%u:%u:%u:%u", &_oper_class,
403
0
           &_chan, &_preference, &_reason);
404
0
    if (ret != 4 ||
405
0
        _oper_class > 255 || _chan > 255 ||
406
0
        _preference > 255 || _reason > 65535 ) {
407
0
      wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s",
408
0
           token);
409
0
      goto fail;
410
0
    }
411
0
    chan->oper_class = _oper_class;
412
0
    chan->chan = _chan;
413
0
    chan->preference = _preference;
414
0
    chan->reason = _reason;
415
416
0
    if (wpas_mbo_validate_non_pref_chan(chan->oper_class,
417
0
                chan->chan, chan->reason)) {
418
0
      wpa_printf(MSG_ERROR,
419
0
           "Invalid non_pref_chan: oper class %d chan %d reason %d",
420
0
           chan->oper_class, chan->chan, chan->reason);
421
0
      goto fail;
422
0
    }
423
424
0
    for (i = 0; i < num; i++)
425
0
      if (wpa_non_pref_chan_is_eq(chan, &chans[i]))
426
0
        break;
427
0
    if (i != num) {
428
0
      wpa_printf(MSG_ERROR,
429
0
           "oper class %d chan %d is duplicated",
430
0
           chan->oper_class, chan->chan);
431
0
      goto fail;
432
0
    }
433
434
0
    num++;
435
0
  }
436
437
0
  os_free(cmd);
438
439
0
  if (chans) {
440
0
    qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel),
441
0
          wpa_non_pref_chan_cmp);
442
0
  }
443
444
0
update:
445
0
  os_free(wpa_s->non_pref_chan);
446
0
  wpa_s->non_pref_chan = chans;
447
0
  wpa_s->non_pref_chan_num = num;
448
0
  wpas_mbo_non_pref_chan_changed(wpa_s);
449
450
0
  return 0;
451
452
0
fail:
453
0
  os_free(chans);
454
0
  os_free(cmd);
455
0
  return -1;
456
0
}
457
458
459
void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
460
0
{
461
0
  u8 *len;
462
463
0
  if (wpa_s->drv_max_probe_req_ie_len <
464
0
      9 + ((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
465
0
    return;
466
467
0
  wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
468
0
  len = wpabuf_put(ie, 1);
469
470
0
  wpabuf_put_be24(ie, OUI_WFA);
471
0
  wpabuf_put_u8(ie, MBO_OUI_TYPE);
472
473
0
  wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
474
0
  wpabuf_put_u8(ie, 1);
475
0
  wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa);
476
0
  if (wpa_s->enable_oce & OCE_STA) {
477
0
    wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND);
478
0
    wpabuf_put_u8(ie, 1);
479
0
    wpabuf_put_u8(ie, OCE_RELEASE);
480
0
  }
481
0
  *len = (u8 *) wpabuf_put(ie, 0) - len - 1;
482
0
}
483
484
485
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
486
         size_t len)
487
0
{
488
0
  const u8 *pos, *cell_pref = NULL;
489
0
  u8 id, elen;
490
0
  u16 disallowed_sec = 0;
491
492
0
  if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
493
0
      mbo_ie[3] != MBO_OUI_TYPE)
494
0
    return;
495
496
  /* MBO mandates use of PMF when RSN is enabled and we disable MBO for an
497
   * association if PMF cannot be negotiated. However, that might not
498
   * happen if the AP does not advertise support for MBO and still ends up
499
   * including MBO element in BTM. Cover that unexpected behavior
500
   * explicitly here. */
501
0
  if (wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
502
0
      !wpa_sm_pmf_enabled(wpa_s->wpa)) {
503
0
    wpa_printf(MSG_DEBUG,
504
0
         "MBO: Ignore MBO element in BTM request when PMF is not used");
505
0
    return;
506
0
  }
507
508
0
  pos = mbo_ie + 4;
509
0
  len -= 4;
510
511
0
  while (len >= 2) {
512
0
    id = *pos++;
513
0
    elen = *pos++;
514
0
    len -= 2;
515
516
0
    if (elen > len)
517
0
      goto fail;
518
519
0
    switch (id) {
520
0
    case MBO_ATTR_ID_CELL_DATA_PREF:
521
0
      if (elen != 1)
522
0
        goto fail;
523
524
0
      if (wpa_s->conf->mbo_cell_capa ==
525
0
          MBO_CELL_CAPA_AVAILABLE)
526
0
        cell_pref = pos;
527
0
      else
528
0
        wpa_printf(MSG_DEBUG,
529
0
             "MBO: Station does not support Cellular data connection");
530
0
      break;
531
0
    case MBO_ATTR_ID_TRANSITION_REASON:
532
0
      if (elen != 1)
533
0
        goto fail;
534
535
0
      wpa_s->wnm_mbo_trans_reason_present = 1;
536
0
      wpa_s->wnm_mbo_transition_reason = *pos;
537
0
      break;
538
0
    case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
539
0
      if (elen != 2)
540
0
        goto fail;
541
542
0
      if (wpa_s->wnm_mode &
543
0
          WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
544
0
        wpa_printf(MSG_DEBUG,
545
0
             "MBO: Unexpected association retry delay, BSS is terminating");
546
0
        goto fail;
547
0
      } else if (wpa_s->wnm_mode &
548
0
           WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
549
0
        disallowed_sec = WPA_GET_LE16(pos);
550
0
        wpa_printf(MSG_DEBUG,
551
0
             "MBO: Association retry delay: %u",
552
0
             disallowed_sec);
553
0
      } else {
554
0
        wpa_printf(MSG_DEBUG,
555
0
             "MBO: Association retry delay attribute not in disassoc imminent mode");
556
0
      }
557
558
0
      break;
559
0
    case MBO_ATTR_ID_AP_CAPA_IND:
560
0
    case MBO_ATTR_ID_NON_PREF_CHAN_REPORT:
561
0
    case MBO_ATTR_ID_CELL_DATA_CAPA:
562
0
    case MBO_ATTR_ID_ASSOC_DISALLOW:
563
0
    case MBO_ATTR_ID_TRANSITION_REJECT_REASON:
564
0
      wpa_printf(MSG_DEBUG,
565
0
           "MBO: Attribute %d should not be included in BTM Request frame",
566
0
           id);
567
0
      break;
568
0
    default:
569
0
      wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u",
570
0
           id);
571
0
      return;
572
0
    }
573
574
0
    pos += elen;
575
0
    len -= elen;
576
0
  }
577
578
0
  if (cell_pref)
579
0
    wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
580
0
      *cell_pref);
581
582
0
  if (wpa_s->wnm_mbo_trans_reason_present)
583
0
    wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
584
0
      wpa_s->wnm_mbo_transition_reason);
585
586
0
  if (disallowed_sec && wpa_s->current_bss)
587
0
    wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
588
0
             disallowed_sec, 0);
589
590
0
  return;
591
0
fail:
592
0
  wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)",
593
0
       id, elen, len);
594
0
}
595
596
597
size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
598
            size_t len,
599
            enum mbo_transition_reject_reason reason)
600
0
{
601
0
  u8 reject_attr[3];
602
603
0
  reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON;
604
0
  reject_attr[1] = 1;
605
0
  reject_attr[2] = reason;
606
607
0
  return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr));
608
0
}
609
610
611
void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa)
612
0
{
613
0
  u8 cell_capa[7];
614
615
0
  if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) {
616
0
    wpa_printf(MSG_DEBUG,
617
0
         "MBO: Cellular capability already set to %u",
618
0
         mbo_cell_capa);
619
0
    return;
620
0
  }
621
622
0
  wpa_s->conf->mbo_cell_capa = mbo_cell_capa;
623
624
0
  cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC;
625
0
  cell_capa[1] = 5; /* Length */
626
0
  WPA_PUT_BE24(cell_capa + 2, OUI_WFA);
627
0
  cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA;
628
0
  cell_capa[6] = mbo_cell_capa;
629
630
0
  wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7);
631
0
  wpa_supplicant_set_default_scan_ies(wpa_s);
632
0
  wpas_update_mbo_connect_params(wpa_s);
633
0
}
634
635
636
struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
637
           struct wpa_bss *bss, u32 mbo_subtypes)
638
0
{
639
0
  struct wpabuf *anqp_buf;
640
0
  u8 *len_pos;
641
0
  u8 i;
642
643
0
  if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
644
0
    wpa_printf(MSG_INFO, "MBO: " MACSTR
645
0
         " does not support MBO - cannot request MBO ANQP elements from it",
646
0
         MAC2STR(bss->bssid));
647
0
    return NULL;
648
0
  }
649
650
  /* Allocate size for the maximum case - all MBO subtypes are set */
651
0
  anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE);
652
0
  if (!anqp_buf)
653
0
    return NULL;
654
655
0
  len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC);
656
0
  wpabuf_put_be24(anqp_buf, OUI_WFA);
657
0
  wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
658
659
0
  wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST);
660
661
  /* The first valid MBO subtype is 1 */
662
0
  for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) {
663
0
    if (mbo_subtypes & BIT(i))
664
0
      wpabuf_put_u8(anqp_buf, i);
665
0
  }
666
667
0
  gas_anqp_set_element_len(anqp_buf, len_pos);
668
669
0
  return anqp_buf;
670
0
}
671
672
673
void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
674
          struct wpa_bss *bss, const u8 *sa,
675
          const u8 *data, size_t slen)
676
0
{
677
0
  const u8 *pos = data;
678
0
  u8 subtype;
679
680
0
  if (slen < 1)
681
0
    return;
682
683
0
  subtype = *pos++;
684
0
  slen--;
685
686
0
  switch (subtype) {
687
0
  case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
688
0
    if (slen < 1)
689
0
      break;
690
0
    wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR
691
0
      " cell_conn_pref=%u", MAC2STR(sa), *pos);
692
0
    break;
693
0
  default:
694
0
    wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u",
695
0
         subtype);
696
0
    break;
697
0
  }
698
0
}