LCOV - code coverage report
Current view: top level - wpa_supplicant - mbo.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1475438200 Lines: 372 395 94.2 %
Date: 2016-10-02 Functions: 26 26 100.0 %

          Line data    Source code
       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 "config.h"
      19             : #include "wpa_supplicant_i.h"
      20             : #include "driver_i.h"
      21             : #include "bss.h"
      22             : #include "scan.h"
      23             : 
      24             : /* type + length + oui + oui type */
      25             : #define MBO_IE_HEADER 6
      26             : 
      27             : 
      28          15 : static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason)
      29             : {
      30          15 :         if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE)
      31           1 :                 return -1;
      32             : 
      33             :         /* Only checking the validity of the channel and oper_class */
      34          14 :         if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1)
      35           1 :                 return -1;
      36             : 
      37          13 :         return 0;
      38             : }
      39             : 
      40             : 
      41        8682 : const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr)
      42             : {
      43             :         const u8 *mbo, *end;
      44             : 
      45        8682 :         if (!bss)
      46           0 :                 return NULL;
      47             : 
      48        8682 :         mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
      49        8682 :         if (!mbo)
      50        8662 :                 return NULL;
      51             : 
      52          20 :         end = mbo + 2 + mbo[1];
      53          20 :         mbo += MBO_IE_HEADER;
      54             : 
      55          20 :         return get_ie(mbo, end - mbo, attr);
      56             : }
      57             : 
      58             : 
      59          10 : static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s,
      60             :                                              struct wpabuf *mbo,
      61             :                                              u8 start, u8 end)
      62             : {
      63             :         u8 i;
      64             : 
      65          10 :         wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class);
      66             : 
      67          23 :         for (i = start; i < end; i++)
      68          13 :                 wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan);
      69             : 
      70          10 :         wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference);
      71          10 :         wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason);
      72          10 : }
      73             : 
      74             : 
      75           2 : static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s,
      76             :                                         struct wpabuf *mbo, u8 start, u8 end)
      77             : {
      78           2 :         size_t size = end - start + 3;
      79             : 
      80           2 :         if (size + 2 > wpabuf_tailroom(mbo))
      81           2 :                 return;
      82             : 
      83           2 :         wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
      84           2 :         wpabuf_put_u8(mbo, size); /* Length */
      85             : 
      86           2 :         wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
      87             : }
      88             : 
      89             : 
      90         703 : static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len)
      91             : {
      92         703 :         wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC);
      93         703 :         wpabuf_put_u8(mbo, len); /* Length */
      94         703 :         wpabuf_put_be24(mbo, OUI_WFA);
      95         703 :         wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
      96         703 : }
      97             : 
      98             : 
      99           8 : static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s,
     100             :                                               struct wpabuf *mbo, u8 start,
     101             :                                               u8 end)
     102             : {
     103           8 :         size_t size = end - start + 7;
     104             : 
     105           8 :         if (size + 2 > wpabuf_tailroom(mbo))
     106           8 :                 return;
     107             : 
     108           8 :         wpas_mbo_non_pref_chan_subelem_hdr(mbo, size);
     109           8 :         wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
     110             : }
     111             : 
     112             : 
     113         719 : static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s,
     114             :                                          struct wpabuf *mbo, int subelement)
     115             : {
     116         719 :         u8 i, start = 0;
     117             :         struct wpa_mbo_non_pref_channel *start_pref;
     118             : 
     119         719 :         if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) {
     120         713 :                 if (subelement)
     121         695 :                         wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4);
     122         713 :                 return;
     123             :         }
     124           6 :         start_pref = &wpa_s->non_pref_chan[0];
     125             : 
     126          13 :         for (i = 1; i <= wpa_s->non_pref_chan_num; i++) {
     127          13 :                 struct wpa_mbo_non_pref_channel *non_pref = NULL;
     128             : 
     129          13 :                 if (i < wpa_s->non_pref_chan_num)
     130           7 :                         non_pref = &wpa_s->non_pref_chan[i];
     131          20 :                 if (!non_pref ||
     132          13 :                     non_pref->oper_class != start_pref->oper_class ||
     133           9 :                     non_pref->reason != start_pref->reason ||
     134           3 :                     non_pref->preference != start_pref->preference) {
     135          10 :                         if (subelement)
     136           8 :                                 wpas_mbo_non_pref_chan_subelement(wpa_s, mbo,
     137             :                                                                   start, i);
     138             :                         else
     139           2 :                                 wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start,
     140             :                                                             i);
     141             : 
     142          10 :                         if (!non_pref)
     143           6 :                                 return;
     144             : 
     145           4 :                         start = i;
     146           4 :                         start_pref = non_pref;
     147             :                 }
     148             :         }
     149             : }
     150             : 
     151             : 
     152          20 : int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
     153             : {
     154             :         struct wpabuf *mbo;
     155             :         int res;
     156             : 
     157          20 :         if (len < MBO_IE_HEADER + 3 + 7)
     158           0 :                 return 0;
     159             : 
     160             :         /* Leave room for the MBO IE header */
     161          20 :         mbo = wpabuf_alloc(len - MBO_IE_HEADER);
     162          20 :         if (!mbo)
     163           1 :                 return 0;
     164             : 
     165             :         /* Add non-preferred channels attribute */
     166          19 :         wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0);
     167             : 
     168             :         /*
     169             :          * Send cellular capabilities attribute even if AP does not advertise
     170             :          * cellular capabilities.
     171             :          */
     172          19 :         wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA);
     173          19 :         wpabuf_put_u8(mbo, 1);
     174          19 :         wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa);
     175             : 
     176          19 :         res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
     177          19 :         if (!res)
     178           0 :                 wpa_printf(MSG_ERROR, "Failed to add MBO IE");
     179             : 
     180          19 :         wpabuf_free(mbo);
     181          19 :         return res;
     182             : }
     183             : 
     184             : 
     185         964 : static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s,
     186             :                                            const u8 *data, size_t len)
     187             : {
     188             :         struct wpabuf *buf;
     189             :         int res;
     190             : 
     191             :         /*
     192             :          * Send WNM-Notification Request frame only in case of a change in
     193             :          * non-preferred channels list during association, if the AP supports
     194             :          * MBO.
     195             :          */
     196        1229 :         if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_bss ||
     197         265 :             !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE))
     198         700 :                 return;
     199             : 
     200         264 :         buf = wpabuf_alloc(4 + len);
     201         264 :         if (!buf)
     202           1 :                 return;
     203             : 
     204         263 :         wpabuf_put_u8(buf, WLAN_ACTION_WNM);
     205         263 :         wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
     206         263 :         wpa_s->mbo_wnm_token++;
     207         263 :         if (wpa_s->mbo_wnm_token == 0)
     208           1 :                 wpa_s->mbo_wnm_token++;
     209         263 :         wpabuf_put_u8(buf, wpa_s->mbo_wnm_token);
     210         263 :         wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */
     211             : 
     212         263 :         wpabuf_put_data(buf, data, len);
     213             : 
     214         526 :         res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
     215         263 :                                   wpa_s->own_addr, wpa_s->bssid,
     216         263 :                                   wpabuf_head(buf), wpabuf_len(buf), 0);
     217         263 :         if (res < 0)
     218           1 :                 wpa_printf(MSG_DEBUG,
     219             :                            "Failed to send WNM-Notification Request frame with non-preferred channel list");
     220             : 
     221         263 :         wpabuf_free(buf);
     222             : }
     223             : 
     224             : 
     225         701 : static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s)
     226             : {
     227             :         struct wpabuf *buf;
     228             : 
     229         701 :         buf = wpabuf_alloc(512);
     230         701 :         if (!buf)
     231         702 :                 return;
     232             : 
     233         700 :         wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1);
     234         700 :         wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf),
     235             :                                        wpabuf_len(buf));
     236         700 :         wpabuf_free(buf);
     237             : }
     238             : 
     239             : 
     240          13 : static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a,
     241             :                                    struct wpa_mbo_non_pref_channel *b)
     242             : {
     243          13 :         return a->oper_class == b->oper_class && a->chan == b->chan;
     244             : }
     245             : 
     246             : 
     247             : /*
     248             :  * wpa_non_pref_chan_cmp - Compare two channels for sorting
     249             :  *
     250             :  * In MBO IE non-preferred channel subelement we can put many channels in an
     251             :  * attribute if they are in the same operating class and have the same
     252             :  * preference and reason. To make it easy for the functions that build
     253             :  * the IE attributes and WNM Request subelements, save the channels sorted
     254             :  * by their oper_class and reason.
     255             :  */
     256          10 : static int wpa_non_pref_chan_cmp(const void *_a, const void *_b)
     257             : {
     258          10 :         const struct wpa_mbo_non_pref_channel *a = _a, *b = _b;
     259             : 
     260          10 :         if (a->oper_class != b->oper_class)
     261           1 :                 return a->oper_class - b->oper_class;
     262           9 :         if (a->reason != b->reason)
     263           5 :                 return a->reason - b->reason;
     264           4 :         return a->preference - b->preference;
     265             : }
     266             : 
     267             : 
     268         707 : int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
     269             :                                   const char *non_pref_chan)
     270             : {
     271         707 :         char *cmd, *token, *context = NULL;
     272         707 :         struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans;
     273         707 :         size_t num = 0, size = 0;
     274             :         unsigned i;
     275             : 
     276         707 :         wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s",
     277             :                    non_pref_chan ? non_pref_chan : "N/A");
     278             : 
     279             :         /*
     280             :          * The shortest channel configuration is 10 characters - commas, 3
     281             :          * colons, and 4 values that one of them (oper_class) is 2 digits or
     282             :          * more.
     283             :          */
     284         707 :         if (!non_pref_chan || os_strlen(non_pref_chan) < 10)
     285             :                 goto update;
     286             : 
     287          11 :         cmd = os_strdup(non_pref_chan);
     288          11 :         if (!cmd)
     289           1 :                 return -1;
     290             : 
     291          32 :         while ((token = str_token(cmd, " ", &context))) {
     292             :                 struct wpa_mbo_non_pref_channel *chan;
     293             :                 int ret;
     294             :                 unsigned int _oper_class;
     295             :                 unsigned int _chan;
     296             :                 unsigned int _preference;
     297             :                 unsigned int _reason;
     298             : 
     299          17 :                 if (num == size) {
     300          16 :                         size = size ? size * 2 : 1;
     301          16 :                         tmp_chans = os_realloc_array(chans, size,
     302             :                                                      sizeof(*chans));
     303          16 :                         if (!tmp_chans) {
     304           1 :                                 wpa_printf(MSG_ERROR,
     305             :                                            "Couldn't reallocate non_pref_chan");
     306           1 :                                 goto fail;
     307             :                         }
     308          15 :                         chans = tmp_chans;
     309             :                 }
     310             : 
     311          16 :                 chan = &chans[num];
     312             : 
     313          16 :                 ret = sscanf(token, "%u:%u:%u:%u", &_oper_class,
     314             :                              &_chan, &_preference, &_reason);
     315          31 :                 if (ret != 4 ||
     316          45 :                     _oper_class > 255 || _chan > 255 ||
     317          30 :                     _preference > 255 || _reason > 65535 ) {
     318           1 :                         wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s",
     319             :                                    token);
     320           1 :                         goto fail;
     321             :                 }
     322          15 :                 chan->oper_class = _oper_class;
     323          15 :                 chan->chan = _chan;
     324          15 :                 chan->preference = _preference;
     325          15 :                 chan->reason = _reason;
     326             : 
     327          30 :                 if (wpas_mbo_validate_non_pref_chan(chan->oper_class,
     328          30 :                                                     chan->chan, chan->reason)) {
     329           4 :                         wpa_printf(MSG_ERROR,
     330             :                                    "Invalid non_pref_chan: oper class %d chan %d reason %d",
     331           4 :                                    chan->oper_class, chan->chan, chan->reason);
     332           2 :                         goto fail;
     333             :                 }
     334             : 
     335          25 :                 for (i = 0; i < num; i++)
     336          13 :                         if (wpa_non_pref_chan_is_eq(chan, &chans[i]))
     337           1 :                                 break;
     338          13 :                 if (i != num) {
     339           2 :                         wpa_printf(MSG_ERROR,
     340             :                                    "oper class %d chan %d is duplicated",
     341           2 :                                    chan->oper_class, chan->chan);
     342           1 :                         goto fail;
     343             :                 }
     344             : 
     345          12 :                 num++;
     346             :         }
     347             : 
     348           5 :         os_free(cmd);
     349             : 
     350           5 :         if (chans) {
     351           5 :                 qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel),
     352             :                       wpa_non_pref_chan_cmp);
     353             :         }
     354             : 
     355             : update:
     356         701 :         os_free(wpa_s->non_pref_chan);
     357         701 :         wpa_s->non_pref_chan = chans;
     358         701 :         wpa_s->non_pref_chan_num = num;
     359         701 :         wpas_mbo_non_pref_chan_changed(wpa_s);
     360             : 
     361         701 :         return 0;
     362             : 
     363             : fail:
     364           5 :         os_free(chans);
     365           5 :         os_free(cmd);
     366           5 :         return -1;
     367             : }
     368             : 
     369             : 
     370        5522 : void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
     371             : {
     372        5522 :         wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
     373        5522 :         wpabuf_put_u8(ie, 7);
     374        5522 :         wpabuf_put_be24(ie, OUI_WFA);
     375        5522 :         wpabuf_put_u8(ie, MBO_OUI_TYPE);
     376             : 
     377        5522 :         wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
     378        5522 :         wpabuf_put_u8(ie, 1);
     379        5522 :         wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa);
     380        5522 : }
     381             : 
     382             : 
     383             : enum chan_allowed {
     384             :         NOT_ALLOWED, ALLOWED
     385             : };
     386             : 
     387        1408 : static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan,
     388             :                                        unsigned int *flags)
     389             : {
     390             :         int i;
     391             : 
     392       17127 :         for (i = 0; i < mode->num_channels; i++) {
     393       16934 :                 if (mode->channels[i].chan == chan)
     394        1215 :                         break;
     395             :         }
     396             : 
     397        2623 :         if (i == mode->num_channels ||
     398        1215 :             (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED))
     399         400 :                 return NOT_ALLOWED;
     400             : 
     401        1008 :         if (flags)
     402         834 :                 *flags = mode->channels[i].flag;
     403             : 
     404        1008 :         return ALLOWED;
     405             : }
     406             : 
     407             : 
     408         134 : static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel)
     409             : {
     410         134 :         u8 center_channels[] = {42, 58, 106, 122, 138, 155};
     411             :         size_t i;
     412             : 
     413         134 :         if (mode->mode != HOSTAPD_MODE_IEEE80211A)
     414           0 :                 return 0;
     415             : 
     416         432 :         for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
     417             :                 /*
     418             :                  * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
     419             :                  * so the center channel is 6 channels away from the start/end.
     420             :                  */
     421         864 :                 if (channel >= center_channels[i] - 6 &&
     422         432 :                     channel <= center_channels[i] + 6)
     423         134 :                         return center_channels[i];
     424             :         }
     425             : 
     426           0 :         return 0;
     427             : }
     428             : 
     429             : 
     430         134 : static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel)
     431             : {
     432             :         u8 center_chan;
     433             :         unsigned int i;
     434             : 
     435         134 :         center_chan = get_center_80mhz(mode, channel);
     436         134 :         if (!center_chan)
     437           0 :                 return NOT_ALLOWED;
     438             : 
     439             :         /* check all the channels are available */
     440        1028 :         for (i = 0; i < 4; i++) {
     441             :                 unsigned int flags;
     442         428 :                 u8 adj_chan = center_chan - 6 + i * 4;
     443             : 
     444         428 :                 if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
     445          90 :                         return NOT_ALLOWED;
     446             : 
     447         386 :                 if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
     448          98 :                     (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) ||
     449          98 :                     (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) ||
     450          86 :                     (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)))
     451           6 :                         return NOT_ALLOWED;
     452             :         }
     453             : 
     454          86 :         return ALLOWED;
     455             : }
     456             : 
     457             : 
     458          40 : static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel)
     459             : {
     460          40 :         u8 center_channels[] = { 50, 114 };
     461             :         unsigned int i;
     462             : 
     463          40 :         if (mode->mode != HOSTAPD_MODE_IEEE80211A)
     464           0 :                 return 0;
     465             : 
     466          60 :         for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
     467             :                 /*
     468             :                  * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
     469             :                  * so the center channel is 14 channels away from the start/end.
     470             :                  */
     471         120 :                 if (channel >= center_channels[i] - 14 &&
     472          60 :                     channel <= center_channels[i] + 14)
     473          40 :                         return center_channels[i];
     474             :         }
     475             : 
     476           0 :         return 0;
     477             : }
     478             : 
     479             : 
     480          40 : static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode,
     481             :                                        u8 channel)
     482             : {
     483             :         u8 center_chan;
     484             :         unsigned int i;
     485             : 
     486          40 :         center_chan = get_center_160mhz(mode, channel);
     487          40 :         if (!center_chan)
     488           0 :                 return NOT_ALLOWED;
     489             : 
     490             :         /* Check all the channels are available */
     491         368 :         for (i = 0; i < 8; i++) {
     492             :                 unsigned int flags;
     493         166 :                 u8 adj_chan = center_chan - 14 + i * 4;
     494             : 
     495         166 :                 if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
     496          26 :                         return NOT_ALLOWED;
     497             : 
     498         162 :                 if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
     499          18 :                     (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) ||
     500          18 :                     (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) ||
     501          18 :                     (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) ||
     502          18 :                     (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) ||
     503          18 :                     (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) ||
     504          18 :                     (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) ||
     505          18 :                     (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)))
     506          18 :                         return NOT_ALLOWED;
     507             :         }
     508             : 
     509          18 :         return ALLOWED;
     510             : }
     511             : 
     512             : 
     513         640 : static enum chan_allowed verify_channel(struct hostapd_hw_modes *mode,
     514             :                                         u8 channel, u8 bw)
     515             : {
     516         640 :         unsigned int flag = 0;
     517             :         enum chan_allowed res, res2;
     518             : 
     519         640 :         res2 = res = allow_channel(mode, channel, &flag);
     520         640 :         if (bw == BW40MINUS) {
     521         119 :                 if (!(flag & HOSTAPD_CHAN_HT40MINUS))
     522          32 :                         return NOT_ALLOWED;
     523          87 :                 res2 = allow_channel(mode, channel - 4, NULL);
     524         521 :         } else if (bw == BW40PLUS) {
     525         119 :                 if (!(flag & HOSTAPD_CHAN_HT40PLUS))
     526          32 :                         return NOT_ALLOWED;
     527          87 :                 res2 = allow_channel(mode, channel + 4, NULL);
     528         402 :         } else if (bw == BW80) {
     529             :                 /*
     530             :                  * channel is a center channel and as such, not necessarily a
     531             :                  * valid 20 MHz channels. Override earlier allow_channel()
     532             :                  * result and use only the 80 MHz specific version.
     533             :                  */
     534          30 :                 res2 = res = verify_80mhz(mode, channel);
     535         372 :         } else if (bw == BW160) {
     536             :                 /*
     537             :                  * channel is a center channel and as such, not necessarily a
     538             :                  * valid 20 MHz channels. Override earlier allow_channel()
     539             :                  * result and use only the 160 MHz specific version.
     540             :                  */
     541          40 :                 res2 = res = verify_160mhz(mode, channel);
     542         332 :         } else if (bw == BW80P80) {
     543             :                 /*
     544             :                  * channel is a center channel and as such, not necessarily a
     545             :                  * valid 20 MHz channels. Override earlier allow_channel()
     546             :                  * result and use only the 80 MHz specific version.
     547             :                  */
     548         104 :                 res2 = res = verify_80mhz(mode, channel);
     549             :         }
     550             : 
     551         576 :         if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
     552         186 :                 return NOT_ALLOWED;
     553             : 
     554         390 :         return ALLOWED;
     555             : }
     556             : 
     557             : 
     558         420 : static int wpas_op_class_supported(struct wpa_supplicant *wpa_s,
     559             :                                    const struct oper_class_map *op_class)
     560             : {
     561             :         int chan;
     562             :         size_t i;
     563             :         struct hostapd_hw_modes *mode;
     564             :         int found;
     565             : 
     566         420 :         mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode);
     567         420 :         if (!mode)
     568          20 :                 return 0;
     569             : 
     570         400 :         if (op_class->op_class == 128) {
     571          20 :                 u8 channels[] = { 42, 58, 106, 122, 138, 155 };
     572             : 
     573          31 :                 for (i = 0; i < ARRAY_SIZE(channels); i++) {
     574          30 :                         if (verify_channel(mode, channels[i], op_class->bw) ==
     575             :                             ALLOWED)
     576          19 :                                 return 1;
     577             :                 }
     578             : 
     579           1 :                 return 0;
     580             :         }
     581             : 
     582         380 :         if (op_class->op_class == 129) {
     583             :                 /* Check if either 160 MHz channels is allowed */
     584          40 :                 return verify_channel(mode, 50, op_class->bw) == ALLOWED ||
     585          20 :                         verify_channel(mode, 114, op_class->bw) == ALLOWED;
     586             :         }
     587             : 
     588         360 :         if (op_class->op_class == 130) {
     589             :                 /* Need at least two non-contiguous 80 MHz segments */
     590          20 :                 found = 0;
     591             : 
     592          22 :                 if (verify_channel(mode, 42, op_class->bw) == ALLOWED ||
     593           2 :                     verify_channel(mode, 58, op_class->bw) == ALLOWED)
     594          18 :                         found++;
     595          22 :                 if (verify_channel(mode, 106, op_class->bw) == ALLOWED ||
     596           4 :                     verify_channel(mode, 122, op_class->bw) == ALLOWED ||
     597           2 :                     verify_channel(mode, 138, op_class->bw) == ALLOWED)
     598          18 :                         found++;
     599          38 :                 if (verify_channel(mode, 106, op_class->bw) == ALLOWED &&
     600          18 :                     verify_channel(mode, 138, op_class->bw) == ALLOWED)
     601           0 :                         found++;
     602          20 :                 if (verify_channel(mode, 155, op_class->bw) == ALLOWED)
     603          13 :                         found++;
     604             : 
     605          20 :                 if (found >= 2)
     606          18 :                         return 1;
     607             : 
     608           2 :                 return 0;
     609             :         }
     610             : 
     611         340 :         found = 0;
     612         860 :         for (chan = op_class->min_chan; chan <= op_class->max_chan;
     613         180 :              chan += op_class->inc) {
     614         466 :                 if (verify_channel(mode, chan, op_class->bw) == ALLOWED) {
     615         286 :                         found = 1;
     616         286 :                         break;
     617             :                 }
     618             :         }
     619             : 
     620         340 :         return found;
     621             : }
     622             : 
     623             : 
     624          20 : int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
     625             :                               size_t len)
     626             : {
     627             :         struct wpabuf *buf;
     628             :         u8 op, current, chan;
     629             :         u8 *ie_len;
     630             :         int res;
     631             : 
     632             :         /*
     633             :          * Assume 20 MHz channel for now.
     634             :          * TODO: Use the secondary channel and VHT channel width that will be
     635             :          * used after association.
     636             :          */
     637          20 :         if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
     638             :                                           &current, &chan) == NUM_HOSTAPD_MODES)
     639           0 :                 return 0;
     640             : 
     641             :         /*
     642             :          * Need 3 bytes for EID, length, and current operating class, plus
     643             :          * 1 byte for every other supported operating class.
     644             :          */
     645          20 :         buf = wpabuf_alloc(global_op_class_size + 3);
     646          20 :         if (!buf)
     647           0 :                 return 0;
     648             : 
     649          20 :         wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES);
     650             :         /* Will set the length later, putting a placeholder */
     651          20 :         ie_len = wpabuf_put(buf, 1);
     652          20 :         wpabuf_put_u8(buf, current);
     653             : 
     654         440 :         for (op = 0; global_op_class[op].op_class; op++) {
     655         420 :                 if (wpas_op_class_supported(wpa_s, &global_op_class[op]))
     656         341 :                         wpabuf_put_u8(buf, global_op_class[op].op_class);
     657             :         }
     658             : 
     659          20 :         *ie_len = wpabuf_len(buf) - 2;
     660          20 :         if (*ie_len < 2 || wpabuf_len(buf) > len) {
     661           0 :                 wpa_printf(MSG_ERROR,
     662             :                            "Failed to add supported operating classes IE");
     663           0 :                 res = 0;
     664             :         } else {
     665          20 :                 os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
     666          20 :                 res = wpabuf_len(buf);
     667          20 :                 wpa_hexdump_buf(MSG_DEBUG,
     668             :                                 "MBO: Added supported operating classes IE",
     669             :                                 buf);
     670             :         }
     671             : 
     672          20 :         wpabuf_free(buf);
     673          20 :         return res;
     674             : }
     675             : 
     676             : 
     677           8 : void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
     678             :                            size_t len)
     679             : {
     680           8 :         const u8 *pos, *cell_pref = NULL, *reason = NULL;
     681             :         u8 id, elen;
     682           8 :         u16 disallowed_sec = 0;
     683             : 
     684          15 :         if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
     685           7 :             mbo_ie[3] != MBO_OUI_TYPE)
     686           1 :                 return;
     687             : 
     688           7 :         pos = mbo_ie + 4;
     689           7 :         len -= 4;
     690             : 
     691          20 :         while (len >= 2) {
     692          12 :                 id = *pos++;
     693          12 :                 elen = *pos++;
     694          12 :                 len -= 2;
     695             : 
     696          12 :                 if (elen > len)
     697           1 :                         goto fail;
     698             : 
     699          11 :                 switch (id) {
     700             :                 case MBO_ATTR_ID_CELL_DATA_PREF:
     701           3 :                         if (elen != 1)
     702           1 :                                 goto fail;
     703             : 
     704           2 :                         if (wpa_s->conf->mbo_cell_capa ==
     705             :                             MBO_CELL_CAPA_AVAILABLE)
     706           1 :                                 cell_pref = pos;
     707             :                         else
     708           1 :                                 wpa_printf(MSG_DEBUG,
     709             :                                            "MBO: Station does not support Cellular data connection");
     710           2 :                         break;
     711             :                 case MBO_ATTR_ID_TRANSITION_REASON:
     712           2 :                         if (elen != 1)
     713           1 :                                 goto fail;
     714             : 
     715           1 :                         reason = pos;
     716           1 :                         break;
     717             :                 case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
     718           4 :                         if (elen != 2)
     719           1 :                                 goto fail;
     720             : 
     721           3 :                         if (wpa_s->wnm_mode &
     722             :                             WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
     723           1 :                                 wpa_printf(MSG_DEBUG,
     724             :                                            "MBO: Unexpected association retry delay, BSS is terminating");
     725           1 :                                 goto fail;
     726           2 :                         } else if (wpa_s->wnm_mode &
     727             :                                    WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
     728           1 :                                 disallowed_sec = WPA_GET_LE16(pos);
     729             :                         } else {
     730           1 :                                 wpa_printf(MSG_DEBUG,
     731             :                                            "MBO: Association retry delay attribute not in disassoc imminent mode");
     732             :                         }
     733             : 
     734           2 :                         break;
     735             :                 case MBO_ATTR_ID_AP_CAPA_IND:
     736             :                 case MBO_ATTR_ID_NON_PREF_CHAN_REPORT:
     737             :                 case MBO_ATTR_ID_CELL_DATA_CAPA:
     738             :                 case MBO_ATTR_ID_ASSOC_DISALLOW:
     739             :                 case MBO_ATTR_ID_TRANSITION_REJECT_REASON:
     740           1 :                         wpa_printf(MSG_DEBUG,
     741             :                                    "MBO: Attribute %d should not be included in BTM Request frame",
     742             :                                    id);
     743           1 :                         break;
     744             :                 default:
     745           1 :                         wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u",
     746             :                                    id);
     747           1 :                         return;
     748             :                 }
     749             : 
     750           6 :                 pos += elen;
     751           6 :                 len -= elen;
     752             :         }
     753             : 
     754           1 :         if (cell_pref)
     755           1 :                 wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
     756           1 :                         *cell_pref);
     757             : 
     758           1 :         if (reason)
     759           1 :                 wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
     760           1 :                         *reason);
     761             : 
     762           1 :         if (disallowed_sec && wpa_s->current_bss)
     763           1 :                 wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
     764             :                                      disallowed_sec);
     765             : 
     766           1 :         return;
     767             : fail:
     768           5 :         wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)",
     769             :                    id, elen, len);
     770             : }
     771             : 
     772             : 
     773          40 : size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
     774             :                                     size_t len,
     775             :                                     enum mbo_transition_reject_reason reason)
     776             : {
     777             :         u8 reject_attr[3];
     778             : 
     779          40 :         reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON;
     780          40 :         reject_attr[1] = 1;
     781          40 :         reject_attr[2] = reason;
     782             : 
     783          40 :         return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr));
     784             : }
     785             : 
     786             : 
     787         265 : void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa)
     788             : {
     789             :         u8 cell_capa[7];
     790             : 
     791         265 :         if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) {
     792           1 :                 wpa_printf(MSG_DEBUG,
     793             :                            "MBO: Cellular capability already set to %u",
     794             :                            mbo_cell_capa);
     795         266 :                 return;
     796             :         }
     797             : 
     798         264 :         wpa_s->conf->mbo_cell_capa = mbo_cell_capa;
     799             : 
     800         264 :         cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC;
     801         264 :         cell_capa[1] = 5; /* Length */
     802         264 :         WPA_PUT_BE24(cell_capa + 2, OUI_WFA);
     803         264 :         cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA;
     804         264 :         cell_capa[6] = mbo_cell_capa;
     805             : 
     806         264 :         wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7);
     807         264 :         wpa_supplicant_set_default_scan_ies(wpa_s);
     808             : }
     809             : 
     810             : 
     811           1 : struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
     812             :                                    struct wpa_bss *bss)
     813             : {
     814             :         struct wpabuf *anqp_buf;
     815             :         u8 *len_pos;
     816             : 
     817           1 :         if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
     818           6 :                 wpa_printf(MSG_INFO, "MBO: " MACSTR
     819             :                            " does not support MBO - cannot request MBO ANQP elements from it",
     820           6 :                            MAC2STR(bss->bssid));
     821           1 :                 return NULL;
     822             :         }
     823             : 
     824           0 :         anqp_buf = wpabuf_alloc(10);
     825           0 :         if (!anqp_buf)
     826           0 :                 return NULL;
     827             : 
     828           0 :         len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC);
     829           0 :         wpabuf_put_be24(anqp_buf, OUI_WFA);
     830           0 :         wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
     831             : 
     832           0 :         wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
     833           0 :         gas_anqp_set_element_len(anqp_buf, len_pos);
     834             : 
     835           0 :         return anqp_buf;
     836             : }

Generated by: LCOV version 1.10